Das AI Edge Function Calling SDK (FC SDK) ist eine Bibliothek, mit der Entwickler Funktionsaufrufe mit On-Device-LLMs verwenden können. Mit Funktionsaufrufen können Sie Modelle mit externen Tools und APIs verbinden. So können Modelle bestimmte Funktionen mit den erforderlichen Parametern aufrufen, um reale Aktionen auszuführen.
Anstatt nur Text zu generieren, kann ein LLM, das das FC SDK verwendet, einen strukturierten Aufruf einer Funktion generieren, die eine Aktion ausführt, z. B. nach aktuellen Informationen suchen, Alarme einstellen oder Reservierungen vornehmen.
In dieser Anleitung wird beschrieben, wie Sie die LLM Inference API mit dem FC SDK in eine Android-Anwendung einbinden. In diesem Leitfaden geht es darum, einem On-Device-LLM die Möglichkeit zu geben, Funktionen aufzurufen. Weitere Informationen zur Verwendung der LLM Inference API finden Sie im Leitfaden zur LLM Inference API für Android.
Kurzanleitung
So verwenden Sie das FC SDK in Ihrer Android-Anwendung: In diesem Schnellstart wird die LLM Inference API mit Hammer 2.1 (1,5 Mrd.) verwendet. Die LLM Inference API ist für High-End-Android-Geräte wie das Pixel 8 und das Samsung S23 oder höher optimiert und unterstützt Geräteemulatoren nicht zuverlässig.
Abhängigkeiten hinzufügen
Das FC SDK verwendet die Bibliothek com.google.ai.edge.localagents:localagents-fc und die LLM Inference API die Bibliothek com.google.mediapipe:tasks-genai. Fügen Sie beide Abhängigkeiten der Datei build.gradle in Ihrer Android-App hinzu:
dependencies {
implementation 'com.google.mediapipe:tasks-genai:0.10.24'
implementation 'com.google.ai.edge.localagents:localagents-fc:0.1.0'
}
Fügen Sie für Geräte mit Android 12 (API 31) oder höher die Abhängigkeit der nativen OpenCL-Bibliothek hinzu. Weitere Informationen finden Sie in der Dokumentation zum Tag uses-native-library.
Fügen Sie der Datei AndroidManifest.xml die folgenden uses-native-library-Tags hinzu:
<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"/>
Modell herunterladen
Laden Sie Hammer 1B im 8‑Bit-quantisierten Format von Hugging Face herunter. Weitere Informationen zu den verfügbaren Modellen finden Sie in der Dokumentation zu Modellen.
Übertragen Sie den Inhalt des Ordners hammer2.1_1.5b_q8_ekv4096.task auf das Android-Gerät.
$ 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
Funktionsdefinitionen deklarieren
Definieren Sie die Funktionen, die dem Modell zur Verfügung gestellt werden. Zur Veranschaulichung des Prozesses enthält diese Kurzanleitung zwei Funktionen als statische Methoden, die hartcodierte Antworten zurückgeben. Eine praktischere Implementierung würde Funktionen definieren, die eine REST-API aufrufen oder Informationen aus einer Datenbank abrufen.
Die Funktionen getWeather und getTime werden so definiert:
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() {}
}
Verwenden Sie FunctionDeclaration, um jede Funktion zu beschreiben. Geben Sie für jede Funktion einen Namen und eine Beschreibung an und legen Sie die Typen fest. So wird dem Modell mitgeteilt, was die Funktionen tun und wann Funktionsaufrufe erfolgen sollen.
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();
Fügen Sie die Funktionsdeklarationen einem Tool-Objekt hinzu:
var tool = Tool.newBuilder()
.addFunctionDeclarations(getWeather)
.addFunctionDeclarations(getTime)
.build();
Inferenz-Backend erstellen
Erstellen Sie ein Inferenz-Backend mit der LLM Inference API und übergeben Sie ein Formatierungsobjekt für Ihr Modell. Der FC SDK-Formatter (ModelFormatter) fungiert sowohl als Formatierungstool als auch als Parser. Da in dieser Kurzanleitung Gemma-3 1B verwendet wird, verwenden wir GemmaFormatter:
var llmInferenceOptions = LlmInferenceOptions.builder()
.setModelPath(modelFile.getAbsolutePath())
.build();
var llmInference = LlmInference.createFromOptions(context, llmInferenceOptions);
var llmInferenceBackend = new llmInferenceBackend(llmInference, new GemmaFormatter());
Weitere Informationen finden Sie unter Konfigurationsoptionen für LLM-Inferenz.
Modell instanziieren
Verwenden Sie das GenerativeModel-Objekt, um das Inferenz-Backend, den Systemprompt und die Tools zu verbinden. Wir haben bereits das Inferenz-Backend und die Tools, sodass wir nur noch den Systemprompt erstellen müssen:
var systemInstruction = Content.newBuilder()
.setRole("system")
.addParts(Part.newBuilder().setText("You are a helpful assistant."))
.build();
Instanziieren Sie das Modell mit GenerativeModel:
var generativeModel = new GenerativeModel(
llmInferenceBackend,
systemInstruction,
List.of(tool),
)
Chatsitzung starten
Der Einfachheit halber wird in dieser Kurzanleitung eine einzelne Chatsitzung gestartet. Sie können auch mehrere unabhängige Sitzungen erstellen.
Starten Sie eine Chatsitzung mit der neuen Instanz von GenerativeModel:
var chat = generativeModel.startChat();
Senden Sie Prompts über die Chatsitzung an das Modell. Verwenden Sie dazu die Methode sendMessage:
var response = chat.sendMessage("How's the weather in San Francisco?");
Modellantwort analysieren
Nachdem ein Prompt an das Modell übergeben wurde, muss die Anwendung die Antwort untersuchen, um zu entscheiden, ob ein Funktionsaufruf erfolgen oder Text in natürlicher Sprache ausgegeben werden soll.
// 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());
}
Der Beispielcode ist eine zu stark vereinfachte Implementierung. Weitere Informationen dazu, wie eine Anwendung Modellantworten untersuchen kann, finden Sie unter Formatierung und Parsing.
Funktionsweise
In diesem Abschnitt finden Sie detailliertere Informationen zu den wichtigsten Konzepten und Komponenten des Function Calling SDK for Android.
Modelle
Für das Function Calling SDK ist ein Modell mit einem Formatierungsprogramm und einem Parser erforderlich. Das FC-SDK enthält einen integrierten Formatierer und Parser für die folgenden Modelle:
- Gemma: Verwenden Sie die
GemmaFormatter. - Llama: Verwenden Sie die
LlamaFormatter. - Hammer: Verwenden Sie den
HammerFormatter.
Wenn Sie ein anderes Modell mit dem FC SDK verwenden möchten, müssen Sie einen eigenen Formatierer und Parser entwickeln, der mit der LLM Inference API kompatibel ist.
Formatierung und Parsing
Ein wichtiger Teil der Unterstützung von Funktionsaufrufen ist die Formatierung von Prompts und das Parsen der Modellausgabe. Das sind zwar zwei separate Prozesse, das FC SDK übernimmt jedoch sowohl die Formatierung als auch das Parsen über die ModelFormatter-Schnittstelle.
Der Formatierer ist dafür verantwortlich, die strukturierten Funktionsdeklarationen in Text umzuwandeln, Funktionsantworten zu formatieren und Tokens einzufügen, um den Beginn und das Ende von Unterhaltungsrunden sowie die Rollen dieser Runden (z.B. „Nutzer“, „Modell“) anzugeben.
Der Parser ist dafür verantwortlich, zu erkennen, ob die Modellantwort einen Funktionsaufruf enthält. Wenn der Parser einen Funktionsaufruf erkennt, wird er in einen strukturierten Datentyp geparst. Andernfalls wird der Text als Antwort in natürlicher Sprache behandelt.
Eingeschränkte Decodierung
Die eingeschränkte Dekodierung ist eine Technik, mit der die Ausgabegenerierung von LLMs so gesteuert wird, dass sie einem vordefinierten strukturierten Format wie JSON-Objekten oder Python-Funktionsaufrufen entspricht. Durch die Durchsetzung dieser Einschränkungen formatiert das Modell seine Ausgaben so, dass sie mit den vordefinierten Funktionen und den entsprechenden Parametertypen übereinstimmen.
Wenn Sie die eingeschränkte Dekodierung aktivieren möchten, definieren Sie die Einschränkungen in einem ConstraintOptions-Objekt und rufen Sie die enableConstraint-Methode einer ChatSession-Instanz auf.
Wenn diese Einschränkung aktiviert ist, enthält die Antwort nur die Tools, die mit dem GenerativeModel verknüpft sind.
Das folgende Beispiel zeigt, wie die eingeschränkte Dekodierung konfiguriert wird, um die Antwort auf Tool-Aufrufe zu beschränken. Dadurch wird der Tool-Aufruf so eingeschränkt, dass er mit dem Präfix ```tool_code\n beginnt und mit dem Suffix \n``` endet.
ConstraintOptions constraintOptions = ConstraintOptions.newBuilder()
.setToolCallOnly( ConstraintOptions.ToolCallOnly.newBuilder()
.setConstraintPrefix("```tool_code\n")
.setConstraintSuffix("\n```"))
.build(); chatSession.enableConstraint(constraintOptions);
Wenn Sie die aktive Einschränkung in derselben Sitzung deaktivieren möchten, verwenden Sie die Methode disableConstraint:
chatSession.disableConstraint();