AppFunctions to praktyczny odpowiednik serwerów MCP na Androidzie. Umożliwiają udostępnianie części logiki aplikacji agentom i asystentom AI.
Agenci używają opisów, aby wybrać funkcję AppFunction, która spełnia żądanie. Ten proces wyboru działa jak tradycyjne wykrywanie narzędzi MCP, w którym aplikacje AI wykorzystują opisy serwerów i narzędzi MCP do podejmowania decyzji. Aplikacja działa jako serwer MCP na Androidzie, a każda funkcja AppFunction działa jako jedno z jego narzędzi. Podaj te teksty reklamy:
- Opis aplikacji (serwer MCP na Androida). Opis aplikacji to pierwszy punkt wejścia dla agentów, który pozwala im zrozumieć, co potrafi Twoja aplikacja. Aby pomóc agentom odkryć Twoją aplikację, zacznij od jej głównej domeny i krótko wymień zadania, które można wykonać za pomocą funkcji AppFunctions.
- Samodzielnie napisz opis aplikacji w pliku XML metadanych aplikacji.
- Użyj opisu aplikacji, aby opisać to, czego nie można opisać w opisach poszczególnych funkcji, np. kolejność wywoływania funkcji.
- Opisy AppFunction (narzędzia). Każdy opis funkcji wyjaśnia działanie jednej funkcji AppFunction. Opisy funkcji są zwykle zbudowane wokół czasownika, takiego jak zaktualizuj, utwórz lub zarezerwuj, oraz zasobu, takiego jak ustawienia, kanał lub pokój.
Wytyczne dotyczące opisu funkcji
Agenci korzystają z opisów funkcji, aby zdecydować, która funkcja pasuje do żądania i jak ją wywołać.
Tworzenie zwięzłych i jednoznacznych opisów funkcji AppFunction
Im mniej niejednoznaczny jest opis, tym większe prawdopodobieństwo, że agent wybierze odpowiednią funkcję dla żądania. W tabeli poniżej znajdziesz wskazówki dotyczące tego, co należy robić, a czego unikać w opisach funkcji:
| Tak | Czego nie robić |
|---|---|
| Opisz przeznaczenie funkcji i wynik jej działania w jasny i neutralny sposób. | Nie używaj opisów jako promptów. Polecenia takie jak „MUSISZ powiedzieć” i „Musisz poprosić użytkownika o” instruują agenta, jak ma się zachowywać, zamiast opisywać funkcję. |
| Na początku opisu umieść kluczowe informacje i zadbaj o to, aby był on zwięzły. | Nie dodawaj do opisów zbędnych słów, takich jak „Ta funkcja służy do”. Wypełniacze wydłużają opis, ale nie sprawiają, że jest on bardziej przejrzysty. |
| dokładnie odzwierciedlać działanie kodu, w tym jego parametry i dane wyjściowe; Na przykład funkcja opisana jako tylko do odczytu nie powinna zapisywać ani usuwać danych. | Nie używaj nazw wprowadzających w błąd, takich jak funkcja o nazwie fetchData, która usuwa dane użytkownika. |
| Opisy funkcji pisz w formacie KDoc, uwzględniając parametry i zwracany typ. Podczas nadawania nazw postępuj zgodnie z przewodnikiem po stylu Kotlina i twórz opisy, które będą zrozumiałe bez znajomości implementacji. | Nie używaj nadmiernie wielkich liter ani znaków specjalnych, aby zwrócić uwagę na części opisu, np. **CRITICAL** lub VERY IMPORTANT. |
| Używaj nazw funkcji AppFunctions, które są zrozumiałe dla człowieka. | |
| Jasno i bezpośrednio informuj o tym, co robi każda funkcja AppFunction. | Nie podawaj instrukcji proceduralnych, które obejmują inne funkcje, np. „Najpierw wyszukaj kontakt”. Agenty używają opisu aplikacji do planowania wywołań funkcji, więc opisz tę kolejność w opisie. |
| Opisz, czego agent może oczekiwać od funkcji, używając sformułowań takich jak „Jeśli masz X, to Y”. | |
| Załóż, że agenty mają własne instrukcje dotyczące ogólnego korzystania z AppFunctions. Opis funkcji wyjaśnia, co ona robi, a instrukcje agenta – jak jej używać. | Nie próbuj zastępować ani umieszczać instrukcji na poziomie agenta w opisach funkcji. |
Pisanie z myślą o enkapsulacji
Każdy opis funkcji powinien być samodzielny, aby agent mógł korzystać z tej funkcji i żadnej innej. W tabeli poniżej znajdziesz wytyczne dotyczące enkapsulacji:
| Tak | Czego nie robić |
|---|---|
| Opisy funkcji powinny być konkretne i skupione na jednym, dobrze zdefiniowanym celu. Na przykład „tworzyć, znajdować i usuwać zadania osobiste”. | Nie pisz zbyt ogólnego opisu, ponieważ może to spowodować, że agent będzie wywoływać funkcję w nieodpowiednich momentach lub z niewłaściwych powodów. |
| Nie twórz funkcji AppFunctions o wielu zastosowaniach ani nie udostępniaj wielu funkcji AppFunctions do tego samego przypadku użycia. Agenci częściej wybierają niewłaściwą funkcję, gdy kilka funkcji ma podobne intencje. | |
| Nie odwołuj się do innych funkcji AppFunctions w swojej aplikacji ani w innych aplikacjach i nie próbuj wpływać na ich działanie. Może to spowodować niezamierzone zależności i efekty uboczne. |
Dozwolone działania i udostępnianie danych
Aby chronić prywatność użytkownika i bezpieczeństwo użytkowników, gdy agenty korzystają z funkcji aplikacji, postępuj zgodnie z tymi wytycznymi:
| Tak | Czego nie robić |
|---|---|
| wykonywać tylko bezpieczne, nieniszczące zadania, które użytkownik normalnie wykonywałby w Twojej aplikacji, gdy agent może podać jasne parametry z prośby użytkownika – na przykład utworzyć zadanie z tytułem lub wyszukać notatki; | |
| Udostępniaj tylko informacje, które nie są ściśle osobiste ani poufne, lub dane, na których udostępnienie użytkownik wyraźnie wyraził zgodę w ramach działania. | Nie przyznawaj agentowi szerokiego dostępu do danych użytkownika ani zasobów systemowych. |
| Nie projektuj funkcji AppFunctions, które wymagają lub zwracają hasła, dane finansowe, klucze prywatne lub inne wysoce poufne dane osobowe. | |
| wykonywanie działań, o które poprosił użytkownik, nawet jeśli są one uruchamiane później lub w tle – na przykład wysyłanie wiadomości o godzinie wybranej przez użytkownika; | Nie wykonuj działań, o które użytkownik nie prosił, np. zadań, które uruchamiają się same lub wykraczają poza prośbę. |
| Skup się na funkcjach, które korzystają z języka naturalnego, takich jak zadania, które łatwiej wyrazić w rozmowie niż za pomocą interfejsu, np. „[Agent AI], czy możesz pomóc mi znaleźć wolne terminy w moim kalendarzu w przyszłym tygodniu, a następnie zaproponować lot do Chicago w jednym z tych dni?”. | |
| Na urządzeniach innych niż telefony, takich jak telewizory, Android Auto i system operacyjny Android Automotive, szanuj prywatność użytkowników na urządzeniach współdzielonych. | Nie ujawniaj informacji ani nie wykonuj działań, których interfejs aplikacji nie obsługuje na danym formacie. |
| Jasno i jednoznacznie określ cel funkcji. | Nie udawaj, że jesteś innym narzędziem lub usługą. Nazwa i opis funkcji nie mogą być łudząco podobne do innych narzędzi, ponieważ może to spowodować, że agent wybierze Twoją funkcję przez pomyłkę. |
Projektowanie parametrów i wartości zwracanych
Staranne projektowanie parametrów i wartości zwracanych zapobiega niejednoznaczności i zmniejsza prawdopodobieństwo nieprawidłowych wywołań. Postępuj zgodnie z tymi konwencjami:
| Tak | Czego nie robić |
|---|---|
| Zdefiniuj parametry tylko dla danych, których potrzebuje funkcja. | Nie proś o informacje, których funkcja nie używa. Zmniejsza to ekspozycję danych i upraszcza korzystanie z funkcji. |
Używaj nazw parametrów, które odzwierciedlają oczekiwane dane wejściowe, np. taskId, searchQuery lub recipientEmail. |
Nie używaj nazw takich jak full_context czy conversation_history. Takie nazwy są niejednoznaczne, ponieważ agenty używają tych terminów we własnym kontekście i historii rozmów. Agent może je błędnie zinterpretować i przekazać do funkcji dużą ilość niepotrzebnych danych, co może doprowadzić do ich wycieku. |
Zamiast definiować własne typy, używaj typów obsługiwanych przez AppFunctions – na przykład LocalDate, LocalTime, LocalDateTime lub Instant w przypadku dat i godzin oraz Uri w przypadku linków. Biblioteka reprezentuje te typy w ten sam sposób w każdej aplikacji, dzięki czemu agenci otrzymują spójny format. Pełną listę obsługiwanych typów znajdziesz w artykule AppFunctionSerializable. |
|
| Jeśli parametr nie jest niezbędny do działania funkcji, ustaw go jako opcjonalny. Dzięki temu agent może działać elastycznie, gdy ma tylko częściowe informacje od użytkownika. Więcej informacji znajdziesz w sekcji Parametry opcjonalne. | |
| Jeśli funkcja AppFunction aktualizuje istniejące zasoby, upewnij się, że każdy parametr może odróżnić „ustaw na null” od „nie zmieniaj”. Przykład znajdziesz w sekcji Aktualizacje istniejących zasobów. | |
Aby umożliwić agentowi otwarcie konkretnego ekranu w aplikacji, zwróć PendingIntent dla tego ekranu. Agent może uruchomić PendingIntent w obecnej postaci, bez tworzenia intencji dla aplikacji. Utwórz ją z intencją jawną i FLAG_IMMUTABLE, aby agent nie mógł zmienić miejsca, do którego prowadzi. |
Parametry opcjonalne
Opcjonalne mogą być tylko typy dopuszczające wartość null, typy podstawowe, listy i tablice. Gdy agent pominie opcjonalny parametr, biblioteka przekazuje domyślną wartość typu – null, zero, false lub pustą – zamiast zadeklarowanej przez Ciebie wartości domyślnej.
Rozwiąż istotne wartości domyślne w treści funkcji, jak w tym przykładzie:
/**
* Creates a note.
*
* @param title The title of the note.
* @param content The body text of the note.
* @param attachments The files to attach to the note.
* @param owner The ID of the user who owns the note.
* @return The created note.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun createNote(
title: String, // Required
content: String? = null, // Optional
attachments: List<Attachment> = emptyList(), // Optional
owner: String? = null, // Optional
): Note = withContext(Dispatchers.IO) {
noteRepository.createNote(
title = title,
// The library ignores declared default values and passes null for
// omitted nullable parameters, so resolve meaningful defaults here.
content = content ?: "",
attachments = attachments,
owner = owner ?: currentUserId(),
)
}
Aktualizacje istniejących zasobów
W przykładzie poniżej użyto opakowania SetField, które może mieć wartość null, dla każdego pola, które agent może aktualizować. Wartość null SetField oznacza „nie zmieniaj”, a SetField(null) oznacza „ustaw na null”:
/** A new value for a field of an existing item. */
@AppFunctionSerializable(isDescribedByKDoc = true)
data class SetField<T>(
/** The new value of the field. */
val value: T,
)
/**
* Updates an existing note. Omitted fields keep their values.
*
* @param noteId The ID of the note to update.
* @param title The new title of the note.
* @param content The new body text of the note.
* @return The updated note.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun updateNote(
noteId: String,
title: SetField<String>? = null,
content: SetField<String?>? = null,
): Note = withContext(Dispatchers.IO) {
val note = noteRepository.getNote(noteId)
?: throw AppFunctionElementNotFoundException(
"No note found with ID $noteId"
)
noteRepository.updateNote(
noteId = noteId,
// A null SetField means "don't change", and SetField(null) means
// "set to null", so don't use ?: for nullable fields.
title = title?.value ?: note.title,
content = if (content != null) content.value else note.content,
)
}
Obsługa błędów
Gdy wywołanie się nie powiedzie, agent otrzyma wyjątek zgłoszony przez funkcję, w tym jego typ i komunikat. Postępuj zgodnie z tymi konwencjami, aby agent mógł naprawić błąd lub zgłosić go użytkownikowi:
| Tak | Czego nie robić |
|---|---|
Zgłoś podklasę AppFunctionException, która pasuje do przyczyny, z komunikatem opisującym problem prostym językiem, np. AppFunctionInvalidArgumentException, gdy argument jest nieprawidłowy, lub AppFunctionElementNotFoundException, gdy żądany element nie istnieje. Typ informuje agenta, jaki rodzaj błędu wystąpił, a wiadomość zawiera szczegóły potrzebne do przywrócenia działania, takie jak poprawienie argumentu lub poinformowanie użytkownika. Ten wzorzec jest widoczny w przykładzie updateNote w sekcji Aktualizacje istniejących zasobów. |
Nie pozwól, aby inne wyjątki uciekły z funkcji. Agent otrzymuje je jako AppFunctionAppUnknownException, co nie pozwala określić rodzaju błędu. |