1. O que são cenários Multi‑App e por que você precisa deles
Até agora víamos o GiftGenius como o único aplicativo externo em um chat específico: o usuário escolhe seu App na lista, o ChatGPT carrega suas tools, e você é o “protagonista” da história. Na Store real é diferente: o usuário pode conectar vários Apps ao mesmo tempo, e o ChatGPT decide qual aplicativo chamar em resposta a uma solicitação específica.
Por exemplo, em um mesmo chat podem estar:
- um App corporativo de calendário que sabe os aniversários dos colegas;
- o GiftGenius, que sugere ideias de presentes;
- um commerce‑App da empresa que consegue criar pedidos e processar pagamentos.
O usuário escreve: “Lembre-me dos aniversários dos colegas e já sugira o que presentear e como comprar.” O modelo pode chamar três Apps em sequência: um para o calendário, o segundo para ideias de presentes e o terceiro para o checkout.
É importante entender: o usuário não tem um botão “chame, por favor, o App nº 2 e aqui está o endpoint HTTP dele”. Ele se comunica em linguagem natural, e o ChatGPT atua como um roteador — lê as descriptions e os metadados de todos os aplicativos disponíveis e decide quem chamar e quando.
Daí decorrem três ideias-chave:
- Há concorrência pelo contexto. Seu App precisa ser escolhido entre dezenas de outros com base nas descrições, nomes e comportamento.
- Os metadados viram seu “SEO para LLMs” — são eles que determinam se o modelo vai notar o GiftGenius no momento certo ou ignorá-lo.
- É preciso pensar em interoperabilidade: suas respostas devem ser úteis não só para a pessoa no chat, mas também para outros Apps que leem o mesmo contexto.
Em essência, transformamos um ChatGPT App isolado em um componente de um sistema maior.
2. Como o modelo escolhe o App: modelo mental de roteamento
O roteamento em cenários Multi‑App funciona mais ou menos assim (bastante simplificado, mas útil para desenvolvimento):
- O ChatGPT tem uma lista de Apps disponíveis e suas tools com metadados (nome, descrição, JSON Schema de parâmetros, anotações e _meta).
- O usuário envia uma mensagem.
- O modelo constrói uma representação interna de intenção (intent) e, basicamente, faz uma busca semântica pelas descriptions das tools e dos aplicativos para entender quais ferramentas são adequadas.
- Se os critérios batem — chama a tool ou sugere abrir um App.
Há um detalhe importante: as descriptions devem ser suficientemente distintas (discriminativas). A formulação “Busca de produtos” pouco difere de “Busca de presentes” ou “Busca de livros”, mas “Busca de ideias de presentes com base no acervo de parceiros do GiftGenius” restringe bastante o domínio e aumenta as chances de a sua ferramenta ser escolhida para pedidos de presentes.
Segundo detalhe — evite colisões de nomes. Uma ferramenta chamada get_data no mundo de dezenas de Apps não diz muita coisa, mas giftgenius_get_gift_catalog é bem mais clara. Especialmente em combinação com um description preciso.
E, por fim, o modelo se apoia no contexto: se no chat já se falou em “presentes”, “aniversários” e até no nome GiftGenius, isso destaca seu App aos olhos do roteador.
3. Metadados e descriptions como LLM‑SEO
Para não encarar metadados como “uma formalidade obrigatória em JSON”, é útil pensá-los como trabalho de copy de produto. As recomendações oficiais dizem explicitamente: treat metadata like product copy e projete “one job per tool”.
De forma geral, podemos distinguir alguns níveis de descrição:
| Nível | Para quem | O que descreve |
|---|---|---|
| Manifest description | Humano + modelo | A tarefa do App como um todo: por que incluí-lo no chat |
| Tool description | Modelo (roteamento) | Quando usar uma tool específica e para quais tarefas |
| Parameter descriptions | Modelo (preenchimento de slots) | Como preencher argumentos e quais valores são válidos |
|
Modelo (UI) | O que aparece no widget e se isso deve ser duplicado pela resposta textual do modelo |
widgetDescription é especialmente importante no mundo dos widgets: o modelo não “vê” seu código React, ele só sabe quais props você vai fornecer e para quê. Um campo bem preenchido evita que o modelo “invente por você” e, ao contrário, o ajuda a adaptar respostas textuais levando em conta o que a UI já exibiu.
A documentação do Apps SDK destaca: o ChatGPT decide quando e como chamar seu conector (App) com base nos metadados. descriptions cuidadosas e documentação de parâmetros aumentam o recall — a proporção de situações em que o modelo lembra do seu App — e reduzem falsos acionamentos.
Mini‑exemplo: antigo vs novo description para o GiftGenius
Suponha que antes tivéssemos algo como:
export const appDescription = `
GiftGenius — assistente para encontrar e comprar presentes.
`;
Do ponto de vista humano, está ok, mas para roteamento no mundo Multi‑App é melhor enfatizar quando usar o App e o que ele não faz:
export const appDescription = `
GiftGenius — assistente de ideias de presentes.
Use este aplicativo quando o usuário pedir para criar uma ideia de presente
para uma pessoa ou ocasião específica e respeitar um orçamento.
Não use para compras online genéricas ou planejamento de finanças pessoais.
`;
Agora fica mais fácil para o modelo distinguir o GiftGenius de um App de e‑commerce genérico ou de um consultor financeiro.
4. _meta["openai/widgetDescription"]: explicando ao modelo nossa UI
Na tabela acima citamos separadamente _meta["openai/widgetDescription"]. Agora vamos focar nesse nível: ele ajuda o modelo a “imaginar” seu widget e entender quais partes da resposta já estão cobertas pela UI e o que vale a pena dizer em texto.
Suponha que nossa ferramenta principal suggest_gifts devolva uma lista de presentes, e o widget os renderize como um carrossel horizontal de cartões. Na descrição da ferramenta já explicamos quando usá-la e, em widgetDescription, explicamos como o resultado aparece.
Exemplo de fragmento de descritor da ferramenta (simplificado, inspirado nas recomendações):
const suggestGiftsTool = {
name: "suggest_gifts",
description: "Use this to generate gift ideas within user's budget.",
inputSchema: { /* ... */ },
_meta: {
"openai/widgetDescription":
"Mostra uma lista horizontal de cartões de presentes com preço e botão 'Comprar'. Não repita os nomes dos presentes no texto da resposta."
}
};
Aqui conseguimos várias coisas de uma vez:
- O modelo sabe que a UI já mostrará nomes e preços — logo, na resposta textual pode se concentrar em explicações e conselhos, e não em duplicar a lista.
- Outros Apps (via modelo) entendem que o toolOutput não é apenas um parágrafo de texto, mas uma lista estruturada que pode ser “aproveitada” em seu próprio contexto.
E sim, sejamos honestos: escrever essas descrições é menos divertido do que programar, mas são elas que poupam horas de depuração de comportamentos estranhos do modelo.
5. Anotações das ferramentas: readOnlyHint, destructiveHint, openWorldHint
No mundo Multi‑App importa não só “quando chamar”, mas também quão seguro é chamar uma ferramenta específica. Para isso, o Apps SDK introduz um conjunto de anotações nos descritores das ferramentas.
A ideia é: anotações são dicas suaves ao modelo sobre o caráter da operação. Elas não substituem a autorização no servidor, mas influenciam bastante como o ChatGPT vai se comportar em cadeias (chains).
Resumo curto (conceitual):
| Anotação | Significado | Comportamento típico do modelo |
|---|---|---|
|
Não altera dados | Pode ser chamada com frequência e sem confirmações extras |
|
Altera estado (compras, exclusão) | Antes de chamar, pedir confirmação ao usuário |
|
Acessa o “mundo externo” (busca, web) | O modelo é mais cauteloso com volume e qualidade do resultado |
As anotações (readOnlyHint, destructiveHint, openWorldHint) fazem parte da descrição padrão de uma ferramenta e podem ser usadas não só pelo ChatGPT. O campo _meta["openai/isConsequential"] é um sinal mais específico do ChatGPT, que ajuda adicionalmente o modelo a diferenciar chamadas “seguras” e “com consequências”.
Vejamos duas ferramentas do GiftGenius:
- suggest_gifts — leitura de catálogo, segura.
- create_checkout_session — criação de checkout, ação com side‑effect explícito.
Exemplo de descrição da ferramenta suggest_gifts
const suggestGiftsTool = {
name: "suggest_gifts",
description:
"Use this when the user asks for gift ideas for a person or occasion.",
inputSchema: { /* ... */ },
annotations: {
readOnlyHint: true
},
_meta: {
"openai/widgetDescription": "Carrossel de presentes com preço e link.",
"openai/isConsequential": false
}
};
Esse tipo de ferramenta o modelo pode chamar várias vezes seguidas, inclusive “de forma preventiva”, para preparar opções com antecedência, sem perguntar ao usuário sobre cada ação.
Exemplo de descrição da ferramenta create_checkout_session
const createCheckoutTool = {
name: "create_checkout_session",
description:
"Finalize purchase of selected gifts via Instant Checkout.",
inputSchema: { /* ... */ },
annotations: {
destructiveHint: true
},
_meta: {
"openai/isConsequential": true
}
};
Aqui sinalizamos explicitamente: é uma operação de escrita, tem consequências (dinheiro cobrado, pedido criado) e o modelo deve solicitar confirmação do usuário antes de chamar, especialmente em cadeias longas com vários Apps.
É importante não superestimar a “magia”: mesmo com destructiveHint você deve revalidar no servidor os dados de entrada, tokens e permissões, como discutimos nos módulos sobre segurança e autorização. Mas, do ponto de vista da orquestração Multi‑App, as anotações ajudam o modelo a não “disparar” esse tipo de ferramenta sem necessidade.
6. App isolado vs ecossistema: definindo fronteiras do GiftGenius
Quando o GiftGenius era o único App no chat, dava para permitir um escopo bem amplo: seleção de presentes, dicas de embalagem, lembretes de datas, até pequenos textos de felicitação. O modelo chamaria apenas as suas ferramentas de qualquer maneira.
Em um cenário Multi‑App, essa abordagem “eu faço tudo” passa a atrapalhar:
- o roteador distingue pior quando você é o candidato ideal e quando é melhor usar outro App;
- você começa a se sobrepor ao calendário, ao gerenciador geral de tarefas, ao planejador financeiro etc.;
- com vários aplicativos juntos, o modelo pode escolher o “executor errado” e confundir as ferramentas.
A melhor abordagem é delimitar claramente a responsabilidade:
- GiftGenius: apenas ideias de presentes + ajuda com a compra via ACP/Checkout;
- CalendarApp: eventos e lembretes;
- Finance‑App: orçamento do usuário em geral, planejamento financeiro pessoal.
Nas descrições do App e das ferramentas, é útil indicar explicitamente não só “Use this when…”, mas também “Do not use when…”. O discovery‑playbook oficial recomenda exatamente isso.
Mini‑exemplo de descrição de ferramenta:
description: `
Use this tool when the user explicitly asks for gift suggestions.
Do not use for generic product discovery or price comparison.
`
Tais restrições não só ajudam no roteamento, mas também tornam o comportamento do seu App mais previsível para produto e QA.
7. Padrões de composição de Apps: pipeline, handoff, contexto compartilhado
Na prática, em cenários Multi‑App surgem três ideias correlatas:
- pipeline — vários Apps atuam em sequência (calendário → presentes → comércio), cada um executa sua etapa;
- handoff — a saída de um App vira a entrada do próximo;
- shared context — toda essa passagem ocorre por meio do contexto textual compartilhado do chat, sem chamadas HTTP diretas entre aplicativos.
Como já sugerimos, um cenário Multi‑App não é “App A chama App B por HTTP”. Na implementação atual de ChatGPT Apps, o isolamento é bastante rígido: os aplicativos não se chamam diretamente; a comunicação acontece via contexto textual compartilhado.
Padrão básico em três passos:
- O App A retorna ao chat texto ou JSON (com frequência dentro de structuredContent/widget).
- O modelo lê essa saída.
- No passo seguinte ele pode chamar o App B, preenchendo detalhes da resposta A nos argumentos das suas tools.
Isso é chamado de text/context handoff: “Saída do App A → modelo → entrada do App B”.
Exemplo: CalendarApp + GiftGenius + CommerceApp
Vamos analisar um cenário específico.
Usuário: “O chefe faz aniversário amanhã, escolha um presente e já finalize a compra.”
Passo a passo:
-
O modelo entende que precisa primeiro saber a data e a pessoa. Ele chama a ferramenta do App de calendário, algo como corporate_calendar.list_upcoming_birthdays, e obtém a estrutura:
[ { "name": "Aleksey Bykov", "date": "2025-11-22", "relation": "manager" } ] -
Em seguida, o modelo decide que é hora de chamar o GiftGenius. Ele chama seu suggest_gifts com argumentos vindos do calendário:
{ "recipientName": "Aleksey", "occasion": "birthday", "budget": 150, "relationship": "manager" }O widget do GiftGenius mostra um carrossel de presentes, e a resposta textual explica por que essas ideias são apropriadas.
-
O usuário escolhe uma ou duas opções (pelo botão no widget → widgetState), e o modelo chama a ferramenta do commerce‑App, por exemplo corp_checkout.create_gift_order, com os IDs dos SKUs escolhidos e o endereço de entrega.
Para o ChatGPT são três aplicativos diferentes, mas para o usuário — uma conversa única. O segredo para isso funcionar:
- descriptions claras para as ferramentas de cada App;
- nomes cuidadosos (corporate_calendar.list_upcoming_birthdays, e não apenas list_events);
- formato consistente de dados estruturados (para que a ideia de presente seja descrita de forma que o commerce‑App consiga entendê-la).
Esquema visual
Podemos desenhar esse pipeline assim:
sequenceDiagram
participant U as Usuário
participant C as ChatGPT (Router)
participant Cal as CalendarApp
participant G as GiftGenius
participant Com as CommerceApp
U->>C: O chefe faz aniversário amanhã, escolha e finalize a compra do presente
C->>Cal: tools.call(list_upcoming_birthdays)
Cal-->>C: [{ name, date, relation }]
C->>G: tools.call(suggest_gifts, { recipient, occasion, budget })
G-->>C: gift suggestions (+ widget)
C-->>U: Explicações + widget do GiftGenius
U->>C: Gostei da opção #2, compre-a
C->>Com: tools.call(create_gift_order, { skuId, address })
Com-->>C: Order confirmation
C-->>U: Pronto, pedido realizado
Sua tarefa como desenvolvedor do GiftGenius é fazer com que, nesse coro, sua voz soe clara e objetiva, sem atrapalhar as demais.
8. Interoperabilidade: torne as respostas úteis para outros Apps
No mundo Multi‑App não basta “responder bonito ao usuário”. É desejável que seu toolOutput possa ser processado de forma automática por outro aplicativo: um commerce‑App, um agente analítico, um orquestrador de workflow etc.
Isso implica algumas práticas:
- usar JSON estruturado nas respostas das ferramentas, e não texto “humano” serializado;
- procurar manter campos estáveis e compreensíveis.
Por exemplo, você pode tipar o resultado de suggest_gifts assim:
export type GiftSuggestion = {
id: string;
title: string;
description: string;
price: number;
currency: string;
forPerson: string;
occasion: string;
purchaseUrl: string;
};
E, na resposta da ferramenta, retornar um array desses objetos:
{
"gifts": [
{
"id": "sku_123",
"title": "Planetário de mesa",
"description": "Mini projetor de céu estrelado...",
"price": 89.99,
"currency": "USD",
"forPerson": "Aleksey",
"occasion": "birthday",
"purchaseUrl": "https://shop.example.com/sku_123"
}
]
}
O widget do GiftGenius usará isso como props e renderizará os cartões de forma bonita, e o commerce‑App, ao ver esse JSON no contexto, poderá aproveitar o id e o purchaseUrl para o checkout.
A prática de cenários Multi‑App mostra: um bom App devolve dados de modo que outro App possa “consumi-los”, e não apenas os olhos de uma pessoa.
9. Refatoração prática do GiftGenius para Multi‑App
Vamos resumir tudo em algumas mudanças concretas no nosso aplicativo didático.
Ajustando a manifest‑description
Suponha que temos um openai-app.json (ou equivalente no template de Next.js) com a descrição:
{
"name": "GiftGenius",
"description": "Gift assistant for finding and buying presents."
}
Deixemos mais explícito para o roteamento:
{
"name": "GiftGenius",
"description": "Assistant for gift ideas and purchase flows. Use this app when the user asks what to gift a specific person or for a specific occasion within a budget. Do not use for generic online shopping or personal finance planning."
}
Agora o texto deixa claro que não é shopping genérico, nem consultoria financeira, nem calendário.
Reescrevendo as descriptions das ferramentas
Ferramenta de busca de presentes:
const suggestGiftsTool = {
name: "giftgenius_suggest_gifts",
description: `
Use this when the user asks for gift ideas for a specific person or group,
optionally with a budget or occasion.
Do not use for non-gift product recommendations or travel booking.
`
};
Ferramenta que puxa detalhes de SKU do seu catálogo (read‑only):
const getGiftDetailsTool = {
name: "giftgenius_get_gift_details",
description: `
Use this to fetch more details for a gift suggested earlier by GiftGenius,
for example when the user asks “tell me more about option #2”.
`,
annotations: { readOnlyHint: true }
};
Ferramenta de compra — com destructiveHint, como já mostramos.
Atualizando _meta["openai/widgetDescription"]
Suponha que nosso widget já tenha cartões com o CTA “Comprar”. Vamos indicar isso ao modelo:
const giftWidgetMeta = {
_meta: {
"openai/widgetDescription": `
Mostra uma lista de cartões de presentes com descrição, preço e botão 'Comprar'.
O modelo não deve repetir a lista completa no texto; apenas comentar a escolha e ajudar na decisão.
`
}
};
Agora o modelo tenderá a evitar escrever um textão com dez presentes no chat se eles já estão visíveis no widget, e focará em explicações e lógica — o que é bom para UX e para o custo de tokens.
10. Mentalidade Multi‑App para o seu produto futuro
É importante mudar do modo “como vencer todos os concorrentes e ser o único App do usuário” para o modo “como fazer meu App ser um módulo ideal em um grande ecossistema”.
Essa abordagem traz vários benefícios práticos:
- fica mais fácil explicar ao usuário e aos revisores da Store para que serve seu App e quando ele é adequado;
- o modelo decide o roteamento com mais facilidade: menos confusão, menos chamadas “erradas”;
- você consegue projetar composições conscientemente: hoje — com calendário e comércio; amanhã — com um bot de RH corporativo ou uma CRM interna.
Os guias oficiais de discovery destacam: projete “one job per tool” e trate metadados como um artefato vivo, que deve ser testado e atualizado, não um texto estático do primeiro commit.
Ajuda muito o que você já fez nos tópicos anteriores do Módulo 20: golden cases, LLM evals, execução em CI. Você pode ampliar o conjunto de casos com cenários “no chat há GiftGenius e CalendarApp” e acompanhar como mudanças nas descrições afetam a escolha do App e a qualidade das respostas.
11. Erros comuns ao trabalhar com Multi‑App e composição
Erro nº 1: descrição do App “eu faço de tudo”.
Se você escreve na manifest‑description algo como “assistente inteligente para qualquer tarefa”, você concorre não só com outros Apps, mas também com o ChatGPT básico. Fica difícil para o roteador entender quando deve chamar você e quando bastam os recursos embutidos. No mundo Multi‑App, vencem os aplicativos com propósito claro e estreito: “seleção de presentes”, “gestão de calendário”, “análise de logs”.
Erro nº 2: descriptions das ferramentas vagas e colisões de nomes.
Ferramentas com nomes get_data, process_request e a explicação “processa dados do usuário” são perfeitas para confundir o modelo. No mundo de vários Apps, é fácil acabar com sua tool sendo chamada onde o domínio é outro. O caminho certo é ligar domínio e ação (giftgenius_get_gift_catalog, calendar.list_birthdays) e descrever explicitamente “Use this when… / Do not use when…”.
Erro nº 3: ignorar _meta["openai/widgetDescription"].
Desenvolvedores costumam preencher apenas o description e, no máximo, lembrar de _meta por causa de localidade. Resultado: o modelo não entende o que o widget mostra e começa ou a duplicar a UI em texto, ou, ao contrário, prometer ao usuário uma “tabela de preços” que seu widget não tem. Duas ou três linhas em widgetDescription evitam muitos mal-entendidos.
Erro nº 4: ausência de anotações readOnlyHint/destructiveHint.
Se todas as suas ferramentas parecem igualmente “neutras”, o modelo não distingue quais é seguro chamar com frequência e quais exigem confirmação do usuário. Em cenários de vários passos com muitos Apps isso é crítico: dá para fazer várias operações de escrita seguidas sem participação explícita da pessoa. Não esqueça de marcar tools de leitura e destacar ações consequenciais/destrutivas.
Erro nº 5: respostas pensadas apenas para humanos, e não para outros Apps.
Retornar da ferramenta apenas “uma lista de presentes” em uma linha de texto é tentador, mas dificulta o uso por qualquer outro App. JSON estruturado com campos claros (id, price, currency, purchaseUrl, occasion) dá bônus tanto na UI quanto na composição: o modelo pode usar esses dados nos argumentos de outras tools sem precisar fazer parsing de linguagem natural.
Erro nº 6: tentar implementar em um único App tudo o que o usuário possa precisar.
Às vezes dá vontade: “já que fiz o GiftGenius, que ele também gerencie calendário, envie e-mails aos colegas e planeje o orçamento”. Em um mundo isolado, isso pode passar; em contexto Multi‑App, você vira um “canivete suíço” que conflita com outros Apps estreitos e bem lapidados. O melhor é combinar consigo mesmo: meu App faz X e faz isso perfeitamente; o resto é responsabilidade alheia. Esse design simplifica muito o UX e o crescimento do ecossistema.
Erro nº 7: não testar o comportamento em ambiente com outros Apps.
Desenvolvedores costumam testar seu aplicativo em Dev Mode em um chat “limpo”, sem outros Apps. Já na Store, o usuário pode ter uma dezena de aplicativos conectados, alguns conceitualmente próximos do seu. Crie um cenário de teste com Apps vizinhos (calendário, shopping genérico, finanças) e rode seus golden cases: o modelo escolhe corretamente o GiftGenius em pedidos de presentes e não o confunde com outros participantes?
GO TO FULL VERSION