L'SDK AI Edge Function Calling (SDK FC) è una libreria che consente agli sviluppatori di utilizzare la chiamata di funzione con LLM on-device. La chiamata di funzioni consente di connettere modelli a strumenti e API esterni, consentendo ai modelli di chiamare funzioni specifiche con i parametri necessari per eseguire azioni nel mondo reale.
Anziché generare solo testo, un LLM che utilizza l'SDK FC può generare una chiamata strutturata a una funzione che esegue un'azione, ad esempio la ricerca di informazioni aggiornate, l'impostazione di sveglie o la prenotazione.
Questa guida illustra una guida rapida di base per aggiungere l'API LLM Inference con l'SDK FC a un'applicazione Android. Questa guida si concentra sull'aggiunta di funzionalità di chiamata di funzioni a un LLM sul dispositivo. Per saperne di più sull'utilizzo dell'API LLM Inference, consulta la guida LLM Inference per Android.
Guida rapida
Per utilizzare l'SDK FC nella tua applicazione Android: Questa guida rapida utilizza l'API LLM Inference con Hammer 2.1 (1,5 miliardi). L'API LLM Inference è ottimizzata per i dispositivi Android di fascia alta, come Pixel 8 e Samsung S23 o modelli successivi, e non supporta in modo affidabile gli emulatori di dispositivi.
Aggiungi dipendenze
L'SDK FC utilizza la libreria com.google.ai.edge.localagents:localagents-fc e
l'API LLM Inference utilizza la libreria com.google.mediapipe:tasks-genai. Aggiungi
entrambe le dipendenze al file build.gradle della tua app per Android:
dependencies {
implementation 'com.google.mediapipe:tasks-genai:0.10.24'
implementation 'com.google.ai.edge.localagents:localagents-fc:0.1.0'
}
Per i dispositivi con Android 12 (API 31) o versioni successive, aggiungi la dipendenza della libreria OpenCL nativa. Per saperne di più, consulta la documentazione relativa al tag
uses-native-library.
Aggiungi i seguenti tag uses-native-library al file AndroidManifest.xml:
<uses-native-library android:name="libOpenCL.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-car.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false"/>
Scaricare un modello
Scarica Hammer 1B in un formato quantizzato a 8 bit da Hugging Face. Per ulteriori informazioni sui modelli disponibili, consulta la documentazione sui modelli.
Invia i contenuti della cartella hammer2.1_1.5b_q8_ekv4096.task al dispositivo Android.
$ adb shell rm -r /data/local/tmp/llm/ # Remove any previously loaded models
$ adb shell mkdir -p /data/local/tmp/llm/
$ adb push hammer2.1_1.5b_q8_ekv4096.task /data/local/tmp/llm/hammer2.1_1.5b_q8_ekv4096.task
Dichiarare le definizioni di funzione
Definisci le funzioni che verranno rese disponibili al modello. Per illustrare il processo, questa guida rapida include due funzioni come metodi statici che restituiscono risposte codificate. Un'implementazione più pratica definirebbe funzioni che chiamano un'API REST o recuperano informazioni da un database.
Di seguito vengono definite le funzioni getWeather e getTime:
class ToolsForLlm {
public static String getWeather(String location) {
return "Cloudy, 56°F";
}
public static String getTime(String timezone) {
return "7:00 PM " + timezone;
}
private ToolsForLlm() {}
}
Utilizza FunctionDeclaration per descrivere ogni funzione, assegnando a ciascuna un nome e una descrizione e specificando i tipi. In questo modo, il modello sa cosa fanno le funzioni e quando chiamarle.
var getWeather = FunctionDeclaration.newBuilder()
.setName("getWeather")
.setDescription("Returns the weather conditions at a location.")
.setParameters(
Schema.newBuilder()
.setType(Type.OBJECT)
.putProperties(
"location",
Schema.newBuilder()
.setType(Type.STRING)
.setDescription("The location for the weather report.")
.build())
.build())
.build();
var getTime = FunctionDeclaration.newBuilder()
.setName("getTime")
.setDescription("Returns the current time in the given timezone.")
.setParameters(
Schema.newBuilder()
.setType(Type.OBJECT)
.putProperties(
"timezone",
Schema.newBuilder()
.setType(Type.STRING)
.setDescription("The timezone to get the time from.")
.build())
.build())
.build();
Aggiungi le dichiarazioni di funzione a un oggetto Tool:
var tool = Tool.newBuilder()
.addFunctionDeclarations(getWeather)
.addFunctionDeclarations(getTime)
.build();
Crea il backend di inferenza
Crea un backend di inferenza utilizzando l'API LLM Inference e trasmetti un oggetto formattatore per il tuo modello. FC SDK Formatter (ModelFormatter) funge sia da
formattatore che da analizzatore. Poiché questa guida rapida utilizza Gemma-3 1B, utilizzeremo
GemmaFormatter:
var llmInferenceOptions = LlmInferenceOptions.builder()
.setModelPath(modelFile.getAbsolutePath())
.build();
var llmInference = LlmInference.createFromOptions(context, llmInferenceOptions);
var llmInferenceBackend = new llmInferenceBackend(llmInference, new GemmaFormatter());
Per saperne di più, consulta le opzioni di configurazione dell'inferenza LLM.
Istanzia il modello
Utilizza l'oggetto GenerativeModel per connettere il backend di inferenza, il prompt di sistema e gli strumenti. Abbiamo già il backend e gli strumenti di inferenza, quindi dobbiamo solo
creare il prompt di sistema:
var systemInstruction = Content.newBuilder()
.setRole("system")
.addParts(Part.newBuilder().setText("You are a helpful assistant."))
.build();
Istanzia il modello con GenerativeModel:
var generativeModel = new GenerativeModel(
llmInferenceBackend,
systemInstruction,
List.of(tool),
)
Avviare una sessione di chat
Per semplicità, questa guida rapida avvia una singola sessione di chat. Puoi anche creare più sessioni indipendenti.
Utilizzando la nuova istanza di GenerativeModel, avvia una sessione di chat:
var chat = generativeModel.startChat();
Invia prompt al modello tramite la sessione di chat utilizzando il metodo sendMessage:
var response = chat.sendMessage("How's the weather in San Francisco?");
Analizzare la risposta del modello
Dopo aver passato un prompt al modello, l'applicazione deve esaminare la risposta per determinare se effettuare una chiamata di funzione o generare un testo in linguaggio naturale.
// Extract the model's message from the response.
var message = response.getCandidates(0).getContent().getParts(0);
// If the message contains a function call, execute the function.
if (message.hasFunctionCall()) {
var functionCall = message.getFunctionCall();
var args = functionCall.getArgs().getFieldsMap();
var result = null;
// Call the appropriate function.
switch (functionCall.getName()) {
case "getWeather":
result = ToolsForLlm.getWeather(args.get("location").getStringValue());
break;
case "getTime":
result = ToolsForLlm.getWeather(args.get("timezone").getStringValue());
break;
default:
throw new Exception("Function does not exist:" + functionCall.getName());
}
// Return the result of the function call to the model.
var functionResponse =
FunctionResponse.newBuilder()
.setName(functionCall.getName())
.setResponse(
Struct.newBuilder()
.putFields("result", Value.newBuilder().setStringValue(result).build()))
.build();
var functionResponseContent = Content.newBuilder()
.setRole("user")
.addParts(Part.newBuilder().setFunctionResponse(functionResponse))
.build();
var response = chat.sendMessage(functionResponseContent);
} else if (message.hasText()) {
Log.i(message.getText());
}
Il codice di esempio è un'implementazione eccessivamente semplificata. Per saperne di più su come un'applicazione può esaminare le risposte del modello, consulta Formattazione e analisi.
Come funziona
Questa sezione fornisce informazioni più approfondite sui concetti e sui componenti principali dell'SDK Function Calling per Android.
Modelli
L'SDK Function Calling richiede un modello con un formatter e un parser. L'SDK FC contiene un formattatore e un parser integrati per i seguenti modelli:
Per utilizzare un modello diverso con l'SDK FC, devi sviluppare un formattatore e un parser compatibili con l'API LLM Inference.
Formattazione e analisi
Una parte fondamentale del supporto della chiamata di funzione è la formattazione dei prompt e l'analisi
dell'output del modello. Anche se si tratta di due processi separati, l'SDK FC gestisce sia la formattazione che l'analisi con l'interfaccia ModelFormatter.
Il formatter è responsabile della conversione delle dichiarazioni di funzione strutturate in testo, della formattazione delle risposte della funzione e dell'inserimento di token per indicare l'inizio e la fine dei turni di conversazione, nonché i ruoli di questi turni (ad es. "utente", "modello").
Il parser è responsabile del rilevamento se la risposta del modello contiene una chiamata di funzione. Se l'analizzatore rileva una chiamata di funzione, la analizza in un tipo di dati strutturato. In caso contrario, considera il testo come una risposta in linguaggio naturale.
Decodifica vincolata
La decodifica vincolata è una tecnica che guida la generazione dell'output degli LLM per garantire che rispetti un formato strutturato predefinito, come oggetti JSON o chiamate di funzioni Python. Applicando questi vincoli, il modello formatta i suoi output in modo da allinearsi alle funzioni predefinite e ai loro tipi di parametri corrispondenti.
Per attivare la decodifica vincolata, definisci i vincoli in un oggetto ConstraintOptions e richiama il metodo enableConstraint di un'istanza ChatSession.
Se abilitato, questo vincolo limiterà la risposta in modo da includere solo gli strumenti associati a GenerativeModel.
L'esempio seguente mostra come configurare la decodifica vincolata per
limitare la risposta alle chiamate di funzioni. Limita la chiamata allo strumento in modo che inizi con
il prefisso ```tool_code\n e termini con il suffisso
\n```.
ConstraintOptions constraintOptions = ConstraintOptions.newBuilder()
.setToolCallOnly( ConstraintOptions.ToolCallOnly.newBuilder()
.setConstraintPrefix("```tool_code\n")
.setConstraintSuffix("\n```"))
.build(); chatSession.enableConstraint(constraintOptions);
Per disattivare il vincolo attivo all'interno della stessa sessione, utilizza il
metodo disableConstraint:
chatSession.disableConstraint();