Les AppFunctions sont l'équivalent pratique des serveurs MCP sur Android. Ils vous permettent de partager des parties de la logique de votre application avec des agents et des assistants IA.
Les agents utilisent les descriptions pour sélectionner l'AppFunction qui répond à une requête. Ce processus de sélection fonctionne comme la découverte d'outils MCP traditionnelle, où les applications d'IA utilisent les descriptions des serveurs et des outils MCP pour guider leurs choix. Votre application agit en tant que serveur MCP Android, et chaque AppFunction agit comme l'un de ses outils. Fournissez les descriptions suivantes :
- Description de l'application (le serveur MCP Android). La description de votre application est le premier point d'entrée permettant aux agents de comprendre ce que votre application peut faire. Pour aider les agents à découvrir votre application, commencez par son domaine principal et listez de manière concise les tâches que vos AppFunctions lui permettent d'accomplir.
- Rédigez vous-même la description de l'application dans le fichier XML de métadonnées de votre application.
- Utilisez la description de l'application pour décrire ce que les descriptions de fonctions individuelles ne peuvent pas, comme l'ordre dans lequel appeler les fonctions.
- Descriptions des AppFunctions (les outils). Chaque description de fonction explique le comportement d'une AppFunction. Les descriptions de fonctions sont généralement structurées autour d'un verbe, tel que update, create ou book, et d'une ressource, telle que settings, channel ou room.
Consignes concernant la description des fonctions
Les agents s'appuient sur les descriptions de fonction pour décider quelle fonction correspond à une requête et comment l'appeler.
Rédigez des descriptions d'AppFunction concises et sans ambiguïté
Moins une description est ambiguë, plus un agent peut choisir de manière fiable la fonction appropriée pour une requête. Le tableau suivant indique ce qu'il faut faire et ce qu'il faut éviter dans les descriptions de fonctions :
| À faire | À éviter |
|---|---|
| Décrivez l'objectif de la fonction et le résultat de son exécution, dans un langage clair et neutre. | N'utilisez pas de descriptions comme prompts. Les commandes telles que "Vous DEVEZ dire" et "Vous devez demander à l'utilisateur" indiquent à l'agent comment se comporter au lieu de décrire la fonction. |
| Placez les informations clés au début des descriptions et restez concis. | Ne remplissez pas les descriptions avec des éléments de remplissage, tels que "Cette fonction est utilisée pour". Les éléments de remplissage allongent une description sans la rendre plus claire. |
| Refléter précisément ce que fait le code, y compris ses paramètres et sa sortie. Par exemple, une fonction décrite comme étant en lecture seule ne doit pas écrire ni supprimer de données. | N'utilisez pas de noms trompeurs, comme une fonction nommée fetchData qui supprime les données utilisateur. |
| Rédigez les descriptions des fonctions au format KDoc, y compris les paramètres et le type renvoyé. Suivez le guide de style Kotlin pour l'attribution de noms et rendez les descriptions compréhensibles sans connaissance de l'implémentation. | N'utilisez pas de majuscules ou de caractères spéciaux de manière excessive pour attirer l'attention sur certaines parties de la description, comme **CRITICAL** ou VERY IMPORTANT. |
| Utilisez des noms lisibles par l'humain pour les AppFunctions. | |
| Soyez clair et direct sur ce que fait chaque AppFunction. | Ne donnez pas d'instructions procédurales impliquant d'autres fonctions, telles que "Rechercher d'abord le contact". Les agents utilisent la description de votre application pour planifier les appels entre les fonctions. Décrivez donc cet ordre à cet endroit. |
| Décrivez ce que l'agent peut attendre de la fonction, en utilisant des termes tels que "Si vous avez X, alors Y". | |
| Supposons que les agents disposent de leurs propres instructions pour utiliser les AppFunctions en général. La description de votre fonction indique ce qu'elle fait, et les instructions de l'agent expliquent comment l'utiliser. | (n'essayez pas de remplacer ou d'intégrer des instructions au niveau de l'agent dans les descriptions de fonctions) ; |
Écrire en gardant à l'esprit l'encapsulation
Faites en sorte que chaque description de fonction soit autonome, afin qu'elle guide l'agent dans l'utilisation de cette fonction et d'aucune autre. Le tableau suivant présente les consignes d'encapsulation :
| À faire | À éviter |
|---|---|
| Rédigez des descriptions de fonction spécifiques et ciblées, avec un seul objectif bien défini. Par exemple, "créer, rechercher et supprimer des tâches personnelles". | N'écrivez pas de description trop générale, car l'agent pourrait appeler votre fonction à des moments inappropriés ou pour de mauvaises raisons. |
| Ne créez pas d'AppFunctions polyvalentes et n'exposez pas de nombreuses AppFunctions pour le même cas d'utilisation. Les agents sont plus susceptibles de choisir la mauvaise fonction lorsque plusieurs fonctions ont des intents similaires. | |
| Ne faites pas référence à d'autres AppFunctions dans votre application ou dans d'autres applications, et n'essayez pas d'influencer leur comportement. Cela peut créer des dépendances et des effets secondaires involontaires. |
Actions autorisées et partage de données
Suivez ces consignes pour protéger la confidentialité et la sécurité des utilisateurs lorsque les agents utilisent les fonctions de votre application :
| À faire | À éviter |
|---|---|
| N'effectuez que des tâches sûres et non destructives que l'utilisateur effectuerait normalement dans votre application, où l'agent peut fournir des paramètres clairs à partir de la requête de l'utilisateur (par exemple, créer une tâche avec un titre ou rechercher des notes). | |
| Ne partagez que des informations qui ne sont pas hautement personnelles ou confidentielles, ou des données que l'utilisateur accepte explicitement de partager pour l'action. | N'accordez pas à l'agent un accès étendu aux données utilisateur ou aux ressources système. |
| Ne concevez pas d'AppFunctions qui nécessitent ou renvoient des mots de passe, des informations financières, des clés privées ou d'autres informations personnelles hautement sensibles. | |
| Effectuer des actions demandées par un utilisateur, même lorsqu'elles s'exécutent plus tard ou en arrière-plan (par exemple, envoyer un message à l'heure choisie par l'utilisateur). | N'effectuez pas d'actions que l'utilisateur n'a pas demandées, telles que des tâches qui démarrent d'elles-mêmes ou qui vont au-delà de la requête. |
| Concentrez-vous sur les fonctionnalités qui bénéficient du langage naturel, comme les tâches plus faciles à exprimer dans une conversation que par le biais de la navigation dans l'interface utilisateur. Par exemple : "[Agent IA], peux-tu m'aider à trouver des créneaux disponibles la semaine prochaine dans mon agenda, puis me suggérer un vol pour Chicago l'un de ces jours ?" | |
| Sur les facteurs de forme autres que les téléphones, respectez la confidentialité des utilisateurs sur les appareils partagés, tels que les téléviseurs, Android Auto et Android Automotive OS. | Ne divulguez pas d'informations et n'effectuez pas d'actions que l'UI de votre application n'accepte pas sur ce facteur de forme. |
| Identifiez clairement et de manière unique l'objectif de votre fonction. | Ne vous faites pas passer pour un autre outil ou service. Le nom et la description de votre fonction ne doivent pas être trop similaires à ceux d'autres outils, car l'agent pourrait sélectionner votre fonction par erreur. |
Conception des paramètres et des valeurs de retour
Une conception minutieuse des paramètres et des valeurs de retour évite toute ambiguïté et réduit le risque d'appels non valides. Suivez ces conventions :
| À faire | À éviter |
|---|---|
| Ne définissez des paramètres que pour les données dont votre fonction a besoin. | Ne demandez pas d'informations que la fonction n'utilise pas. Cela réduit l'exposition des données et simplifie l'utilisation de la fonction. |
Utilisez des noms de paramètres qui reflètent l'entrée attendue, par exemple taskId, searchQuery ou recipientEmail. |
N'utilisez pas de noms tels que full_context ou conversation_history. Ces noms sont ambigus, car les agents utilisent ces termes pour leur propre contexte et l'historique des conversations. Un agent pourrait les interpréter de manière incorrecte et transmettre une grande quantité de données inutiles à votre fonction, ce qui entraînerait une exfiltration de données. |
Utilisez les types compatibles avec AppFunctions au lieu de définir les vôtres (par exemple, LocalDate, LocalTime, LocalDateTime ou Instant pour les dates et les heures, et Uri pour les liens). La bibliothèque représente ces types de la même manière dans chaque application, de sorte que les agents obtiennent un format cohérent. Pour obtenir la liste complète des types acceptés, consultez AppFunctionSerializable. |
|
| Si un paramètre n'est pas essentiel à l'objectif de la fonction, rendez-le facultatif. Cela donne de la flexibilité à l'agent lorsqu'il ne dispose que d'informations partielles de l'utilisateur. Pour en savoir plus, consultez Paramètres facultatifs. | |
| Si votre AppFunction met à jour des ressources existantes, assurez-vous que chaque paramètre peut faire la distinction entre "définir sur null" et "ne pas modifier". Pour obtenir un exemple, consultez Mises à jour des ressources existantes. | |
Pour permettre à l'agent d'ouvrir un écran spécifique dans votre application, renvoyez un PendingIntent pour cet écran. L'agent peut lancer le PendingIntent tel quel, sans créer d'intent pour votre application. Créez-le avec un intent explicite et FLAG_IMMUTABLE afin que l'agent ne puisse pas modifier sa destination. |
Paramètres facultatifs
Seuls les types nullable, primitif, liste et tableau peuvent être facultatifs. Lorsque l'agent omet un paramètre facultatif, la bibliothèque transmet la valeur par défaut du type (null, zéro, false ou vide) au lieu de la valeur par défaut que vous déclarez.
Résolvez les valeurs par défaut significatives dans le corps de la fonction, comme dans l'exemple suivant :
/**
* 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(),
)
}
Mises à jour des ressources existantes
L'exemple suivant utilise un wrapper SetField nullable pour chaque champ que l'agent peut mettre à jour. Une valeur SetField nulle signifie "ne pas modifier" et SetField(null) signifie "définir sur 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,
)
}
Gestion des exceptions
En cas d'échec d'un appel, l'agent reçoit l'exception générée par votre fonction, y compris son type et son message. Suivez ces conventions pour que l'agent puisse se remettre de l'échec ou le signaler à l'utilisateur :
| À faire | À éviter |
|---|---|
Générez la sous-classe AppFunctionException correspondant à la cause, avec un message décrivant le problème en langage clair (par exemple, AppFunctionInvalidArgumentException lorsqu'un argument n'est pas valide ou AppFunctionElementNotFoundException lorsqu'un élément demandé n'existe pas). Le type indique à l'agent le type d'échec qui s'est produit, et le message lui fournit les détails dont il a besoin pour récupérer, par exemple en corrigeant un argument ou en informant l'utilisateur. L'exemple updateNote dans Mises à jour des ressources existantes illustre ce modèle. |
Ne laissez pas d'autres exceptions échapper à votre fonction. L'agent les reçoit sous la forme AppFunctionAppUnknownException, ce qui ne permet pas d'identifier le type d'échec. |