AppFunctions sono l'equivalente pratico dei server MCP su Android. Ti consentono di condividere parti della logica della tua app con agenti e assistenti AI.
Gli agenti utilizzano le descrizioni per selezionare l'AppFunction che soddisfa una richiesta. Questo processo di selezione funziona come il tradizionale rilevamento degli strumenti MCP, in cui le applicazioni AI utilizzano le descrizioni dei server e degli strumenti MCP per guidare le loro scelte. La tua app funge da server MCP Android e ogni AppFunction funge da uno dei suoi strumenti. Fornisci le seguenti descrizioni:
- Descrizione dell'app (il server MCP Android). La descrizione dell'app è il primo punto di contatto per gli agenti per capire cosa può fare l'app. Per aiutare gli agenti a scoprire la tua app, inizia con il suo dominio principale ed elenca in modo conciso le attività che le tue AppFunctions le consentono di svolgere.
- Scrivi tu la descrizione dell'app nel file XML dei metadati dell'app.
- Utilizza la descrizione dell'app per descrivere ciò che le singole descrizioni delle funzioni non possono, ad esempio l'ordine in cui chiamare le funzioni.
- Descrizioni di AppFunction (gli strumenti). Ogni descrizione della funzione spiega il comportamento di un'AppFunction. Le descrizioni delle funzioni sono generalmente strutturate attorno a un verbo, come aggiorna, crea o prenota, e a una risorsa, come impostazioni, canale o stanza.
Linee guida per la descrizione delle funzioni
Gli agenti si affidano alle descrizioni delle funzioni per decidere quale funzione è adatta a una richiesta e come chiamarla.
Scrivi descrizioni di AppFunction concise e non ambigue
Meno ambigua è una descrizione, più l'agente può scegliere con maggiore affidabilità la funzione corretta per una richiesta. La tabella seguente elenca cosa fare e cosa evitare nelle descrizioni delle funzioni:
| Cosa fare | Cosa non fare |
|---|---|
| Descrivi lo scopo della funzione e il risultato della sua esecuzione, con un linguaggio chiaro e neutro. | Non utilizzare le descrizioni come prompt. Comandi come "DEVI dire" e "Devi chiedere all'utente" indicano all'agente come comportarsi anziché descrivere la funzione. |
| Inserisci le informazioni chiave all'inizio delle descrizioni e mantienile concise. | Non aggiungere descrizioni con elementi di riempimento, ad esempio "Questa funzione viene utilizzata per". I contenuti di riempimento rendono una descrizione più lunga senza renderla più chiara. |
| Riflettere accuratamente ciò che fa il codice, inclusi i parametri e l'output. Ad esempio, una funzione descritta come di sola lettura non deve scrivere o eliminare dati. | Non utilizzare nomi fuorvianti, ad esempio una funzione denominata fetchData che elimina i dati utente. |
| Scrivi le descrizioni delle funzioni in formato KDoc, inclusi i parametri e il tipo restituito. Segui la guida di stile di Kotlin per i nomi e rendi le descrizioni comprensibili senza conoscere l'implementazione. | Non utilizzare un uso eccessivo di lettere maiuscole o caratteri speciali per attirare l'attenzione su parti della descrizione, ad esempio **CRITICAL** o VERY IMPORTANT. |
| Utilizza nomi leggibili per AppFunctions. | |
| Sii chiaro e diretto su ciò che fa ogni AppFunction. | Non fornire istruzioni procedurali che coinvolgono altre funzioni, ad esempio "Cerca prima il contatto". Gli agenti utilizzano la descrizione dell'app per pianificare le chiamate tra le funzioni, quindi descrivi l'ordine lì. |
| Descrivi cosa l'agente può aspettarsi dalla funzione, con un linguaggio come "Se hai X, allora Y". | |
| Supponiamo che gli agenti abbiano le proprie istruzioni per l'utilizzo di AppFunctions in generale. La descrizione della funzione spiega cosa fa la funzione, mentre le istruzioni dell'agente spiegano come utilizzarla. | Non provare a eseguire l'override o a incorporare istruzioni a livello di agente nelle descrizioni delle funzioni. |
Scrivi tenendo presente l'incapsulamento
Mantieni ogni descrizione della funzione autonoma, in modo che guidi l'agente nell'utilizzo di quella funzione e di nessun'altra. La tabella seguente elenca le linee guida per l'incapsulamento:
| Cosa fare | Cosa non fare |
|---|---|
| Scrivi descrizioni delle funzioni specifiche e mirate, con uno scopo unico e ben definito. Ad esempio, "creare, trovare ed eliminare attività personali". | Non scrivere una descrizione troppo generica, che potrebbe indurre l'agente a chiamare la funzione in momenti inappropriati o per motivi sbagliati. |
| Non creare AppFunction multiuso o esporre molte AppFunction per lo stesso caso d'uso. È più probabile che gli agenti scelgano la funzione sbagliata quando diverse funzioni hanno intenti simili. | |
| Non fare riferimento ad altre AppFunction nella tua app o in altre app e non tentare di influenzarne il comportamento. Ciò può creare dipendenze ed effetti collaterali indesiderati. |
Azioni consentite e condivisione dei dati
Segui queste linee guida per proteggere la privacy dell'utente e la sicurezza quando gli agenti utilizzano le funzioni della tua app:
| Cosa fare | Cosa non fare |
|---|---|
| Esegui solo attività sicure e non distruttive che l'utente svolgerebbe normalmente nella tua app, in cui l'agente può fornire parametri chiari dalla richiesta dell'utente, ad esempio la creazione di un'attività con un titolo o la ricerca di note. | |
| Condividi solo informazioni che non sono altamente personali o riservate o dati che l'utente acconsente esplicitamente a condividere per l'azione. | Non concedere all'agente un accesso ampio ai dati utente o alle risorse di sistema. |
| Non progettare AppFunctions che richiedono o restituiscono password, dati finanziari, chiavi private o altre informazioni personali altamente sensibili. | |
| Eseguire azioni richieste da un utente, anche se vengono eseguite in un secondo momento o in background, ad esempio l'invio di un messaggio in un momento scelto dall'utente. | Non eseguire azioni che l'utente non ha richiesto, ad esempio attività che vengono avviate autonomamente o che vanno oltre la richiesta. |
| Concentrati sulle funzionalità che traggono vantaggio dal linguaggio naturale, come le attività che sono più facili da esprimere in una conversazione che tramite la navigazione nell'interfaccia utente, ad esempio: "[Agente AI], puoi aiutarmi a trovare alcuni orari disponibili la prossima settimana nel mio calendario e poi suggerirmi un volo per Chicago in uno di quei giorni?" | |
| Su fattori di forma diversi dagli smartphone, rispetta la privacy dell'utente sui dispositivi condivisi, come TV, Android Auto e Android Automotive OS. | Non divulgare informazioni o eseguire azioni non supportate dalla UI della tua app su quel fattore di forma. |
| Identifica in modo chiaro e univoco lo scopo della funzione. | Non fingere di essere un altro strumento o servizio. Il nome e la descrizione della funzione non devono essere simili ad altri strumenti in modo da creare confusione, il che potrebbe indurre l'agente a selezionare la funzione per errore. |
Progettazione di parametri e valori restituiti
Un'attenta progettazione dei parametri e dei valori restituiti evita ambiguità e riduce la probabilità di chiamate non valide. Segui queste convenzioni:
| Cosa fare | Cosa non fare |
|---|---|
| Definisci i parametri solo per i dati necessari alla tua funzione. | Non richiedere informazioni che la funzione non utilizza. In questo modo, l'esposizione dei dati viene ridotta al minimo e l'utilizzo della funzione viene semplificato. |
Utilizza nomi di parametri che riflettono l'input previsto, ad esempio taskId, searchQuery o recipientEmail. |
Non utilizzare nomi come full_context o
conversation_history. Questi nomi sono ambigui perché gli agenti utilizzano questi termini per il proprio contesto e la propria cronologia delle conversazioni. Un agente potrebbe interpretarli in modo errato e passare una grande quantità di dati non necessari alla tua funzione, causando l'esfiltrazione di dati. |
Utilizza i tipi supportati da AppFunctions anziché definirne di tuoi, ad esempio LocalDate, LocalTime, LocalDateTime o Instant per date e ore e Uri per i link. La libreria rappresenta questi tipi nello stesso modo in ogni app, in modo che gli agenti ricevano un formato coerente. Per l'elenco completo dei tipi supportati, consulta AppFunctionSerializable. |
|
| Se un parametro non è essenziale per lo scopo della funzione, rendilo facoltativo. In questo modo, l'agente ha flessibilità quando dispone solo di informazioni parziali dell'utente. Per saperne di più, consulta Parametri facoltativi. | |
| Se la tua AppFunction aggiorna le risorse esistenti, assicurati che ogni parametro possa distinguere tra "imposta su null" e "non modificare". Per un esempio, consulta Aggiornamenti alle risorse esistenti. | |
Per consentire all'agente di aprire una schermata specifica nella tua app, restituisci un
PendingIntent
per quella schermata. L'agente può avviare l'PendingIntent così com'è, senza creare un intent per la tua app. Crealo con un intent esplicito e FLAG_IMMUTABLE in modo che l'agente non possa modificare la destinazione. |
Parametri facoltativi
Solo i tipi nullable, primitivi, di elenco e di array possono essere facoltativi. Quando l'agente omette un parametro facoltativo, la libreria passa il valore predefinito del tipo (null, zero, false o vuoto) anziché il valore predefinito dichiarato da te.
Risolvi i valori predefiniti significativi nel corpo della funzione, come nell'esempio seguente:
/**
* 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(),
)
}
Aggiornamenti alle risorse esistenti
L'esempio seguente utilizza un wrapper SetField nullable per ogni campo che l'agente può aggiornare. Un valore SetField nullo significa "non modificare" e
SetField(null) significa "imposta su 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,
)
}
Gestione degli errori
Quando una chiamata non va a buon fine, l'agente riceve l'eccezione generata dalla funzione, inclusi il tipo e il messaggio. Segui queste convenzioni in modo che l'agente possa riprendersi dall'errore o segnalarlo all'utente:
| Cosa fare | Cosa non fare |
|---|---|
Genera la sottoclasse AppFunctionException che corrisponde alla causa, con un messaggio che descrive il problema in un linguaggio semplice, ad esempio AppFunctionInvalidArgumentException quando un argomento non è valido o AppFunctionElementNotFoundException quando un elemento richiesto non esiste. Il tipo indica all'agente il tipo di errore che si è verificato e il messaggio fornisce i dettagli necessari per il recupero, ad esempio la correzione di un argomento o la comunicazione all'utente. L'esempio updateNote in Aggiornamenti alle risorse esistenti mostra questo pattern. |
Non lasciare che altre eccezioni sfuggano alla tua funzione. L'agente li riceve come AppFunctionAppUnknownException, che non identifica il tipo di errore. |