CodeGym /Cursos /ChatGPT Apps /Cenários Multi‑App e composição de Apps

Cenários Multi‑App e composição de Apps

ChatGPT Apps
Nível 20 , Lição 4
Disponível

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):

  1. 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).
  2. O usuário envia uma mensagem.
  3. 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.
  4. 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
_meta["openai/widgetDescription"]
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
readOnlyHint
Não altera dados Pode ser chamada com frequência e sem confirmações extras
destructiveHint / isConsequential
Altera estado (compras, exclusão) Antes de chamar, pedir confirmação ao usuário
openWorldHint
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:

  1. O App A retorna ao chat texto ou JSON (com frequência dentro de structuredContent/widget).
  2. O modelo lê essa saída.
  3. 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:

  1. 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" }
    ]
    
  2. 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.

  3. 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?

1
Pesquisa/teste
LLM-Apps: nova geração, nível 20, lição 4
Indisponível
LLM-Apps: nova geração
LLM-Apps: nova geração
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION