Pakiet SDK wywoływania funkcji AI Edge (FC SDK) to biblioteka, która umożliwia deweloperom używanie wywoływania funkcji z usługami LLM na urządzeniu. Wywoływanie funkcji umożliwia łączenie modeli z zewnętrznymi narzędziami i interfejsami API, dzięki czemu modele mogą wywoływać określone funkcje z wymaganymi parametrami, aby wykonywać działania w rzeczywistych warunkach.
Zamiast generować tylko tekst, LLM korzystający z pakietu FC SDK może generować wywołanie funkcji, które wykonuje działanie, takie jak wyszukiwanie aktualnych informacji, ustawianie alarmów czy rezerwowanie.
W tym przewodniku znajdziesz podstawowe informacje o dodawaniu interfejsu LLM Inference API za pomocą pakietu FC SDK do aplikacji na Androida. Ten przewodnik skupia się na dodawaniu funkcji wywoływania funkcji do LLM na urządzeniu. Więcej informacji o korzystaniu z interfejsu LLM Inference API znajdziesz w przewodniku LLM Inference na Androida.
Krótkie wprowadzenie
Aby używać pakietu FC SDK w aplikacji na Androida, wykonaj te czynności. Ten samouczek używa interfejsu LLM Inference API z użyciem Hammer 2.1 (1,5 mld). Interfejs API LLM Inference jest zoptymalizowany pod kątem zaawansowanych urządzeń z Androidem, takich jak Pixel 8 i Samsung S23 lub nowszych, i nie obsługuje niezawodnie emulatorów urządzeń.
Dodawanie zależności
Pakiet FC SDK korzysta z biblioteki com.google.ai.edge.localagents:localagents-fc
, a interfejs LLM Inference API korzysta z biblioteki com.google.mediapipe:tasks-genai
. Dodaj obie zależności do pliku build.gradle
aplikacji na Androida:
dependencies {
implementation 'com.google.mediapipe:tasks-genai:0.10.24'
implementation 'com.google.ai.edge.localagents:localagents-fc:0.1.0'
}
W przypadku urządzeń z Androidem 12 (poziom API 31) lub nowszym dodaj zależność od natywnej biblioteki OpenCL. Więcej informacji znajdziesz w dokumentacji dotyczącej tagu uses-native-library
.
Dodaj do pliku AndroidManifest.xml
te tagi uses-native-library
:
<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"/>
Pobieranie modelu
Pobierz Hammer 1B w 8-bitowym formacie kwantowanym z HuggingFace. Więcej informacji o dostępnych modelach znajdziesz w dokumentacji modeli.
Przekaż zawartość folderu hammer2.1_1.5b_q8_ekv4096.task
na urządzenie z Androidem.
$ 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
Deklarowanie definicji funkcji
Określ funkcje, które będą dostępne dla modelu. Aby zilustrować ten proces, to krótkie wprowadzenie zawiera 2 funkcje jako metody statyczne, które zwracają zakodowane odpowiedzi. Bardziej praktyczne wdrożenie polega na zdefiniowaniu funkcji, które wywołują interfejs API REST lub pobierają informacje z bazy danych.
Funkcję getWeather
i getTime
definiują te wyrażenia:
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() {}
}
Użyj funkcji FunctionDeclaration
, aby opisać każdą funkcję, podając jej nazwę i opis oraz określając typ. Dzięki temu model wie, co robią funkcje i kiedy należy je wywoływać.
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();
Dodaj deklaracje funkcji do obiektu Tool
:
var tool = Tool.newBuilder()
.addFunctionDeclarations(getWeather)
.addFunctionDeclarations(getTime)
.build();
Tworzenie backendu do wnioskowania
Utwórz backend wnioskowania za pomocą interfejsu LLM Inference API i podaj mu obiekt formatowania dla modelu. Formater pakietu FC SDK (ModelFormatter
) pełni funkcję formatera i parsowania. Ponieważ w tym krótkim wprowadzeniu używamy Gemma-3 1B, użyjemy:
GemmaFormatter
var llmInferenceOptions = LlmInferenceOptions.builder()
.setModelPath(modelFile.getAbsolutePath())
.build();
var llmInference = LlmInference.createFromOptions(context, llmInferenceOptions);
var llmInferenceBackend = new llmInferenceBackend(llmInference, new GemmaFormatter());
Więcej informacji znajdziesz w opcjach konfiguracji wnioskowania LLM.
Tworzenie wystąpienia modelu
Użyj obiektu GenerativeModel
, aby połączyć backend inferencji, prompt systemu i narzędzia. Mamy już narzędzia i system do obsługi wnioskowania, więc musimy tylko utworzyć prompt systemowy:
var systemInstruction = Content.newBuilder()
.setRole("system")
.addParts(Part.newBuilder().setText("You are a helpful assistant."))
.build();
Tworzenie wystąpienia modelu za pomocą GenerativeModel
:
var generativeModel = new GenerativeModel(
llmInferenceBackend,
systemInstruction,
List.of(tool),
)
Rozpoczęcie sesji czatu
W tym krótkim przewodniku rozpoczniesz jedną sesję czatu. Możesz też tworzyć wiele niezależnych sesji.
Używając nowej instancji GenerativeModel
, rozpocznij sesję czatu:
var chat = generativeModel.startChat();
Wysyłanie promptów do modelu podczas sesji czatu za pomocą metody sendMessage
:
var response = chat.sendMessage("How's the weather in San Francisco?");
Przeanalizuj odpowiedź modelu.
Po przekazaniu prompta do modelu aplikacja musi sprawdzić odpowiedź, aby określić, czy ma wywołać funkcję, czy wyświetlić tekst w języku naturalnym.
// 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());
}
Przykładowy kod jest zbyt uproszczony. Więcej informacji o tym, jak aplikacja może sprawdzać odpowiedzi modelu, znajdziesz w artykule Formatowanie i analizowanie.
Jak to działa
W tej sekcji znajdziesz szczegółowe informacje o podstawowych pojęciach i elementach pakietu Function Calling SDK na Androida.
Modele
Pakiet SDK wywoływania funkcji wymaga modelu z formaterem i analizatorem. Pakiet FC SDK zawiera wbudowany formater i parsujący dla tych modeli:
Aby używać innego modelu z pakietem FC SDK, musisz opracować własny formater i analizator, który będzie zgodny z interfejsem LLM Inference API.
Formatowanie i analizowanie
Kluczową częścią obsługi wywoływania funkcji jest formatowanie promptów i analizowanie danych wyjściowych modelu. Chociaż są to 2 oddzielne procesy, pakiet FC SDK obsługuje zarówno formatowanie, jak i analizowanie za pomocą interfejsu ModelFormatter
.
Formatowanie odpowiada za konwertowanie deklaracji funkcji strukturalnych na tekst, formatowanie odpowiedzi funkcji oraz wstawianie tokenów wskazujących początek i koniec tur konwersacji, a także role tych tur (np. „user” i „model”).
Analizator odpowiada za wykrywanie, czy odpowiedź modelu zawiera wywołanie funkcji. Jeśli wykryje wywołanie funkcji, przeanalizuje je i przekształci w uporządkowany typ danych. W przeciwnym razie traktuje tekst jako odpowiedź w języku naturalnym.
Dekodowanie z ograniczeniami
Dekodowanie z ograniczeniami to technika, która kieruje generowaniem danych wyjściowych przez duże modele językowe, aby zapewnić zgodność z wstępnie zdefiniowanym formatem uporządkowanym, takim jak obiekty JSON czy wywołania funkcji Pythona. Dzięki stosowaniu tych ograniczeń model formatuje dane wyjściowe w sposób zgodny z wstępnie zdefiniowanymi funkcjami i odpowiednimi typami parametrów.
Aby włączyć kodowanie ograniczone, zdefiniuj ograniczenia w obiekcie ConstraintOptions
i wywołaj metodę enableConstraint
instancji ChatSession
.
Gdy to ograniczenie jest włączone, odpowiedź będzie zawierać tylko narzędzia powiązane z GenerativeModel
.
W tym przykładzie pokazujemy, jak skonfigurować kodowanie ograniczone, aby ograniczyć odpowiedź do wywołań narzędzi. Wywołanie narzędzia musi zaczynać się od prefiksu ```tool_code\n
i kończyć sufiksem \n```
.
ConstraintOptions constraintOptions = ConstraintOptions.newBuilder()
.setToolCallOnly( ConstraintOptions.ToolCallOnly.newBuilder()
.setConstraintPrefix("```tool_code\n")
.setConstraintSuffix("\n```"))
.build();
chatSession.enableConstraint(constraintOptions);
Aby wyłączyć aktywne ograniczenie w ramach tej samej sesji, użyj metody disableConstraint
:
chatSession.disableConstraint();