As AppFunctions são o equivalente prático dos servidores MCP no Android. Elas permitem compartilhar partes da lógica do app com agentes e assistentes de IA.
Os agentes usam descrições para selecionar a AppFunction que atende a uma solicitação. Esse processo de seleção funciona como a descoberta tradicional de ferramentas do MCP, em que os aplicativos de IA usam o servidor MCP e as descrições das ferramentas para orientar as escolhas. Seu app atua como o servidor MCP do Android, e cada AppFunction atua como uma das ferramentas dele. Forneça as seguintes descrições:
- Descrição do app (o servidor MCP do Android). A descrição do app é o
primeiro ponto de entrada para que os agentes entendam o que ele pode fazer. Para ajudar
os agentes a descobrir seu app, comece com o domínio principal e liste de forma concisa
as tarefas que as AppFunctions permitem que ele realize.
- Escreva a descrição do app no arquivo XML de metadados do app.
- Use a descrição do app para descrever o que as descrições de funções individuais não podem, como a ordem em que as funções devem ser chamadas.
- Descrições de AppFunction (as ferramentas). Cada descrição de função explica o comportamento de uma AppFunction. As descrições de funções geralmente são estruturadas em torno de um verbo, como atualizar, criar ou reservar, e um recurso, como configurações, canal ou sala.
Diretrizes para descrição de funções
Os agentes usam as descrições de função para decidir qual função se encaixa em uma solicitação e como chamá-la.
Escreva descrições concisas e inequívocas de AppFunction
Quanto menos ambígua for uma descrição, mais confiável será a escolha da função certa para uma solicitação por um agente. A tabela a seguir lista o que fazer e o que evitar nas descrições de funções:
| O que fazer | O que não fazer |
|---|---|
| Descreva a finalidade da função e o resultado da execução dela em uma linguagem clara e neutra. | Não use descrições como comandos. Comandos como "Você PRECISA dizer" e "Você precisa pedir ao usuário" instruem o agente sobre como se comportar, em vez de descrever a função. |
| Coloque as informações mais importantes no início das descrições e seja conciso. | Não preencha as descrições com frases de efeito, como "Esta função é usada para". As palavras de preenchimento deixam uma descrição mais longa sem torná-la mais clara. |
| Refletir com precisão o que o código faz, incluindo os parâmetros e a saída. Por exemplo, uma função descrita como somente leitura não deve gravar ou excluir dados. | Não use nomes enganosos, como uma função chamada
fetchData que exclui dados do usuário. |
| Escreva descrições de funções no formato KDoc, incluindo parâmetros e o tipo de retorno. Siga o guia de estilo do Kotlin para nomear e criar descrições compreensíveis sem conhecimento da implementação. | Não use letras maiúsculas em excesso ou caracteres especiais para chamar
atenção para partes da descrição, como **CRITICAL**
ou VERY IMPORTANT. |
| Use nomes legíveis por humanos para AppFunctions. | |
| Seja claro e direto sobre o que cada AppFunction faz. | Não dê instruções de procedimento que envolvam outras funções, como "Primeiro, pesquise o contato". Os agentes usam a descrição do app para planejar chamadas entre funções. Portanto, descreva essa ordem lá. |
| Descreva o que o agente pode esperar da função, com uma linguagem como "Se você tiver X, então Y". | |
| Suponha que os agentes tenham as próprias instruções para usar as AppFunctions em geral. A descrição da função diz o que ela faz, e as instruções do agente explicam como usá-la. | Não tente substituir ou incorporar instruções no nível do agente nas descrições de funções. |
Escrever com o encapsulamento em mente
Mantenha cada descrição de função independente, para que ela oriente o agente a usar essa função e nenhuma outra. A tabela a seguir lista as diretrizes de encapsulamento:
| O que fazer | O que não fazer |
|---|---|
| Escreva descrições de funções específicas e focadas, com um propósito único e bem definido. Por exemplo, "criar, encontrar e excluir tarefas pessoais". | Não escreva uma descrição muito ampla, o que pode fazer com que o agente chame sua função em momentos inadequados ou por motivos errados. |
| Não crie AppFunctions multiuso nem exponha muitas AppFunctions para o mesmo caso de uso. Os agentes têm mais chances de escolher a função errada quando várias funções têm intents semelhantes. | |
| Não faça referência nem tente influenciar o comportamento de outras AppFunctions no seu app ou em outros apps. Isso pode criar dependências e efeitos colaterais não intencionais. |
Ações permitidas e compartilhamento de dados pessoais
Siga estas diretrizes para proteger a privacidade e a segurança do usuário quando os agentes usarem as funções do seu app:
| O que fazer | O que não fazer |
|---|---|
| Realize apenas tarefas seguras e não destrutivas que o usuário normalmente faria no app, em que o agente pode fornecer parâmetros claros da solicitação do usuário, como criar uma tarefa com um título ou pesquisar notas. | |
| Compartilhe apenas informações que não sejam altamente pessoais ou confidenciais ou dados que o usuário consente explicitamente em compartilhar para a ação. | Não dê ao agente acesso amplo aos dados do usuário ou aos recursos do sistema. |
| Não crie AppFunctions que exijam ou retornem senhas, detalhes financeiros, chaves privadas ou outras informações pessoais altamente confidenciais. | |
| Realizar ações solicitadas por um usuário, mesmo que sejam executadas mais tarde ou em segundo plano, por exemplo, enviar uma mensagem em um horário escolhido pelo usuário. | Não realize ações que o usuário não pediu, como tarefas que começam sozinhas ou que vão além do pedido. |
| Concentre-se em funcionalidades que se beneficiam da linguagem natural, como tarefas que são mais fáceis de expressar em uma conversa do que pela navegação na interface, por exemplo: "[Agente de IA], você pode me ajudar a encontrar alguns horários disponíveis na próxima semana na minha agenda e sugerir um voo para Chicago em um desses dias?" | |
| Em formatos além de smartphones, respeite a privacidade do usuário em dispositivos compartilhados, como TVs, Android Auto e Android Automotive OS. | Não divulgue informações nem realize ações que a interface do app não oferece suporte nesse formato. |
| Identifique de forma clara e exclusiva o propósito da função. | Não finja ser outra ferramenta ou serviço. O nome e a descrição da função não podem ser confusamente semelhantes a outras ferramentas, o que pode fazer com que o agente selecione sua função por engano. |
Design de parâmetros e valores de retorno
O design cuidadoso de parâmetros e valores de retorno evita ambiguidades e reduz a chance de chamadas inválidas. Siga estas convenções:
| O que fazer | O que não fazer |
|---|---|
| Defina parâmetros apenas para os dados de que sua função precisa. | Não solicite informações que a função não usa. Isso minimiza a exposição de dados e simplifica o uso da função. |
Use nomes de parâmetros que reflitam a entrada esperada, como
taskId, searchQuery ou
recipientEmail. |
Não use nomes como full_context ou
conversation_history. Esses nomes são ambíguos porque os agentes
usam esses termos para o próprio contexto e histórico de conversas. Um agente
pode interpretá-los incorretamente e transmitir uma grande quantidade de dados desnecessários para sua
função, levando à exfiltração de dados. |
Use os tipos compatíveis com as AppFunctions em vez de definir os seus,
por exemplo, LocalDate, LocalTime,
LocalDateTime ou Instant para datas e horários
e Uri para links. A biblioteca representa esses tipos da
mesma forma em todos os apps, para que os agentes tenham um formato consistente. Para ver a lista completa
de tipos compatíveis, consulte
AppFunctionSerializable. |
|
| Se um parâmetro não for essencial para a finalidade da função, torne-o opcional. Isso dá flexibilidade ao agente quando ele tem apenas informações parciais do usuário. Para mais informações, consulte Parâmetros opcionais. | |
| Se a AppFunction atualizar recursos, verifique se cada parâmetro pode distinguir entre "definir como nulo" e "não mudar". Para ver um exemplo, consulte Atualizações de recursos atuais. | |
Para permitir que o agente abra uma tela específica no seu app, retorne um
PendingIntent
para essa tela. O agente pode iniciar o PendingIntent como
está, sem criar uma intent para o app. Crie-o com uma intent explícita
e
FLAG_IMMUTABLE,
para que o agente não possa mudar o destino. |
Parâmetros opcionais
Somente os tipos anuláveis, primitivos, de lista e de matriz podem ser opcionais. Quando o agente
omite um parâmetro opcional, a biblioteca transmite o valor padrão do tipo,
null, zero, false ou vazio, em vez do padrão declarado.
Resolva os padrões significativos no corpo da função, como no exemplo a seguir:
/**
* 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(),
)
}
Atualizações de recursos atuais
O exemplo a seguir usa um wrapper SetField anulável para cada campo que
o agente pode atualizar. Um SetField nulo significa "não mudar", e
SetField(null) significa "definir como nulo":
/** 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,
)
}
Tratamento de erros
Quando uma chamada falha, o agente recebe a exceção gerada pela função, incluindo o tipo e a mensagem. Siga estas convenções para que o agente possa se recuperar da falha ou informá-la ao usuário:
| O que fazer | O que não fazer |
|---|---|
Lance a subclasse
AppFunctionException
que corresponde à causa, com uma mensagem que descreve o problema
em linguagem simples, por exemplo,
AppFunctionInvalidArgumentException
quando um argumento não é válido ou
AppFunctionElementNotFoundException
quando um item solicitado não existe. O tipo informa ao agente que tipo de
falha ocorreu, e a mensagem fornece os detalhes necessários para a recuperação,
como corrigir um argumento ou informar o usuário. O exemplo de updateNote em Atualizações de recursos mostra esse padrão. |
Não deixe que outras exceções escapem da função. O agente os recebe como AppFunctionAppUnknownException, que não identifica o tipo de falha. |