Como passar contexto entre agentes de IA sem perder informação no caminho

diagrama mostrando como passar contexto entre agentes de IA sem perder informação
Resposta rápida

Passar contexto entre agentes quebra quando cada etapa devolve texto livre e a próxima reinterpreta tudo de novo. No Claude Agent SDK, só a mensagem final do subagente volta pro pai, e o único canal de ida é a string de prompt da ferramenta Agent, então o handoff precisa ser contrato, não conversa. Ativando saída estruturada com outputFormat e um JSON Schema, o SDK valida, refaz o prompt em caso de divergência e entrega o dado em message.structured_output. O mesmo padrão aparece no OpenAI Agents SDK (input_filter, output_type) e no LangGraph (estado tipado com reducer por chave)

Fala aí, beleza? Cadeia de agentes é o telefone sem fio mais caro que você já montou 😀

O primeiro agente pesquisa, escreve três parágrafos bonitos e entrega

O segundo lê aquilo, interpreta do jeito dele, resume e passa adiante

O terceiro recebe um resumo de um resumo e decide alguma coisa em cima disso…

No fim, ninguém sabe direito onde o dado sumiu

E aqui vai a parte que quase todo mundo erra no diagnóstico: o problema normalmente NÃO é o modelo, é o canal do handoff. Enquanto cada etapa devolver prosa, cada etapa seguinte vira uma nova chance de reinterpretar. Neste post a gente fecha esse canal: transforma a saída de cada agente num contrato validado, no Claude Agent SDK, no OpenAI Agents SDK, no LangGraph e direto na API

Bora ver na prática?

Por que texto livre degrada a cadeia de agentes

Texto livre não tem contrato

Se o agente 1 pode escrever qualquer coisa, o agente 2 precisa ADIVINHAR o que era importante ali dentro. Isso não é bug de prompt, é falta de formato

E tem um agravante medido: a pesquisa Context Rot, da Chroma, avaliou 18 modelos de ponta e concluiu que o desempenho degrada de forma relevante conforme o input cresce, inclusive em tarefas simples de recuperação e replicação de texto. Ou seja, a suposição de que o modelo processa o contexto de forma uniforme não se sustenta

Pior: a degradação aumenta conforme cai a similaridade entre a informação buscada e a pergunta, que é justamente o cenário do mundo real, onde correspondência exata é rara

Se você já sentiu isso ao revisar textos longos sem perder o contexto, é a mesma física: quanto mais material bruto empilhado, mais coisa escapa

O que acontece quando os agentes conversam demais:

A própria Anthropic relatou o efeito no sistema de pesquisa multi-agente dela: versões iniciais dos agentes chegavam a gerar 50 subagentes para consultas simples e se distraíam com atualizações excessivas entre eles. A principal alavanca de correção foi o prompt de cada agente

Formação Vibe Coding
Formação Recomendada

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Se liga que a lição é dupla

Mais troca não é mais informação, é mais ruído

E o formato do que trafega é decisão de projeto, não detalhe

A régua que dá o tom:

Nesse mesmo sistema, cada subagente pode gastar dezenas de milhares de tokens explorando, mas devolve apenas um resumo destilado, com frequência de 1.000 a 2.000 tokens

Guarda esse número, porque ele é o modelo mental do post inteiro: exploração é barata dentro do subagente, o que atravessa a fronteira é caro

O que você precisa antes de montar o handoff

Antes de escrever qualquer linha, o mais importante é entender ONDE o subagente vive e o que ele enxerga de verdade

Onde os subagentes vivem:

No Claude Agent SDK, subagentes são definidos programaticamente pela opção agents dentro das options do query(), usando o tipo AgentDefinition (com os campos description, prompt, tools, model, skills e mcpServers)

No Claude Code, subagentes são arquivos Markdown com frontmatter YAML guardados em .claude/agents/.md (nível de projeto) ou ~/.claude/agents/.md (nível de usuário)

E tem uma regra de desempate que já me pouparia tempo se eu soubesse antes: agente definido programaticamente tem PRECEDÊNCIA sobre agente carregado do sistema de arquivos com o mesmo nome. Se você jurava que editou o .md e nada mudou, é provavelmente isso

O que o subagente enxerga (e o que não enxerga):

O subagente recebe:

  • o system prompt próprio dele
  • a string de prompt da ferramenta Agent
  • o CLAUDE.md do projeto (via settingSources)
  • as definições de ferramentas herdadas

E o que ele NÃO recebe: o histórico da conversa do agente pai e o system prompt do pai

Ou seja, o único canal do pai para o subagente é a string de prompt da ferramenta Agent

Do outro lado, na volta, cada subagente roda com contexto próprio e só a mensagem final dele volta pro pai. Os resultados intermediários de ferramenta ficam lá dentro, morrem com o subagente

Então o desenho é este: uma porta estreita na ida, uma porta estreita na volta. Todo o resto é você achando que o contexto viajou junto

Passo a passo: montando um handoff que não perde informação

  1. Mapear a fronteira antes de codar. Pega papel (ou um comentário no topo do arquivo mesmo) e escreve duas colunas: o que PRECISA atravessar e o que pode ficar dentro do subagente. Como só a mensagem final volta, tudo que não estiver nela simplesmente não existe pro resto da cadeia. O erro comum deste passo: assumir que o pai vai "ver" o que o subagente leu com o Read ou o Grep, quando aquilo nunca sai de lá
  1. Escrever o prompt da ferramenta Agent como um briefing autossuficiente. Como ele é o canal único, o prompt precisa carregar o objetivo, os dados de entrada já destilados e o formato esperado de saída. Nada de "continue o que a gente estava fazendo", porque não existe "estávamos" pro subagente. O erro comum: escrever o prompt como se fosse mais uma mensagem numa conversa em andamento
const result = query({
  prompt: "Analise os achados da etapa de coleta e classifique cada item",
  options: {
    agents: {
      classificador: {
        description: "Classifica achados de coleta em categorias",
        prompt: "Você classifica achados. Nunca invente item novo.",
        tools: ["Read", "Grep"],
        model: "sonnet"
      }
    }
  }
})
  1. Definir o schema da etapa ANTES do código. O contrato vem primeiro: quais campos a etapa devolve, quais são obrigatórios, o que é lista e o que é string. Se você conhece a ideia de tipar a resposta de uma API antes de consumir ela, é exatamente o mesmo raciocínio aplicado ao agente. O erro comum: schema largo demais, cheio de campo opcional, que na prática volta a ser texto livre com chaves em volta
const schema = {
  type: "object",
  additionalProperties: false,
  required: ["itens", "resumo"],
  properties: {
    resumo: { type: "string" },
    itens: {
      type: "array",
      items: {
        type: "object",
        additionalProperties: false,
        required: ["titulo", "categoria", "fonte"],
        properties: {
          titulo: { type: "string" },
          categoria: { type: "string" },
          fonte: { type: "string" }
        }
      }
    }
  }
}
  1. Ativar a saída estruturada e ler o campo certo. No Claude Agent SDK, você passa o JSON Schema pela opção outputFormat (TypeScript) ou output_format (Python) no query(), com type: json_schema. O SDK valida o resultado contra o schema e refaz o prompt em caso de divergência, e o dado validado chega no campo structured_output da mensagem de resultado. O erro comum: continuar fazendo parse do texto da resposta na mão, ignorando o structured_output que já veio validado

> Atenção pra não confundir CAMADA aqui, porque o nome se repete e confunde mesmo: esse outputFormat / output_format é a OPÇÃO do Claude Agent SDK, no query(), e é assim que se liga saída estruturada por lá. Não é o mesmo que o campo output_format da API da Claude, que virou output_config.format (esse eu explico mais pra frente, na seção da API). Mesmo nome, lugares diferentes

const result = query({
  prompt: "Classifique os achados abaixo",
  options: {
    outputFormat: {
      type: "json_schema",
      schema
    }
  }
})

for await (const message of result) {
  if (message.type === "result") {
    const dados = message.structured_output
    // dados já validado contra o schema
  }
}
  1. Tratar a falha do contrato. Se o agente não conseguir produzir saída válida pro schema, o SDK devolve um resultado de erro com o subtype error_max_structured_output_retries. Isso é um sinal bom, não uma tragédia: significa que a cadeia parou em vez de seguir com lixo. Trata esse caso explicitamente, seja simplificando o schema, seja deixando o prompt do agente mais direto sobre o formato. O erro comum: não checar o subtype e deixar a próxima etapa rodar em cima de um resultado que nunca chegou
  1. Encadear destilando, não despejando. A saída validada de uma etapa vira campo do prompt da próxima, e só os campos que a próxima precisa. Aqui é onde a régua da destilação entra: o subagente pode ter queimado dezenas de milhares de tokens explorando, mas o que atravessa é o resumo. É a mesma lógica de quem usa contexto longo com RAG em vez de jogar a base inteira na janela, ou seja, busca o que interessa e leva só isso adiante. O erro comum: serializar o objeto inteiro e colar no prompt seguinte "por segurança", que é justamente o caminho de volta pro input gigante que degrada
const briefing = dados.itens
  .filter(i => i.categoria === "critico")
  .map(i => `- ${i.titulo} (${i.fonte})`)
  .join("\n")

const proximaEtapa = query({
  prompt: `Escreva o plano de ação para os itens críticos abaixo:\n${briefing}`,
  options: { outputFormat: { type: "json_schema", schema: schemaDoPlano } }
})

O mesmo handoff em outros frameworks (OpenAI Agents SDK e LangGraph)

O padrão é o mesmo em todo lugar: definir o que atravessa e validar o formato. O que muda é a mecânica, e ela muda MUITO

Framework O que atravessa por padrão Onde você controla
Claude Agent SDK Só a mensagem final do subagente volta pro pai; na ida, só a string de prompt da ferramenta Agent outputFormat / output_format com JSON Schema
OpenAI Agents SDK Todo o histórico da conversa vai pro agente que recebe o handoff input_filter (ou filtro global no RunConfig) e output_type
LangGraph Estado compartilhado tipado entre os nós Reducer por chave no estado
  1. No OpenAI Agents SDK, comece assumindo o oposto do Claude Agent SDK. Por padrão, o agente que recebe o handoff enxerga TODO o histórico da conversa. Não é pouca coisa: você sai de "falta contexto" pra "contexto demais", que é o cenário que o Context Rot descreve. O erro comum: migrar mentalmente de um SDK pro outro achando que o default é o mesmo
  1. Use o input_filter como o ponto de corte. Ele é uma função que recebe um HandoffInputData e devolve um novo HandoffInputData, controlando o que segue pro próximo agente. Se o corte é uma política geral da sua aplicação, dá pra aplicar um filtro global no nível do RunConfig. O erro comum: filtrar em um handoff só e deixar os outros passando o histórico inteiro
def filtrar(data: HandoffInputData) -> HandoffInputData:
    # devolve um novo HandoffInputData só com o que a próxima etapa precisa
    ...
  1. Ligue a saída estruturada com output_type. Passar output_type ao agente faz o modelo usar saída estruturada em vez de texto puro, que é exatamente o contrato que a gente quer no fim de cada etapa. O erro comum: filtrar a entrada com carinho e deixar a saída em prosa, ou seja, resolver metade do handoff
agente = Agent(
    name="classificador",
    output_type=ResultadoDaEtapa,
)
  1. No LangGraph, pense em estado, não em mensagem. Os nós de um StateGraph se comunicam lendo e escrevendo em um estado compartilhado tipado, que pode ser um TypedDict ou um modelo Pydantic. Cada nó recebe o estado atual e devolve um dicionário de atualizações. O erro comum: tentar reproduzir "conversa entre agentes" quando o modelo do framework já é um contrato tipado, e você só precisa preencher os campos certos
from typing import TypedDict

class Estado(TypedDict):
    achados: list
    resumo: str

def no_de_coleta(estado: Estado) -> dict:
    return {"achados": [...]}
  1. Cuide do reducer de cada chave. No LangGraph, cada chave do estado pode ter um reducer que define como combinar valor antigo e valor novo, e o reducer padrão é SUBSTITUIÇÃO. Esse é o clássico "perdi informação no caminho" que não é culpa do modelo: dois nós escrevem na mesma chave e o segundo apaga o primeiro. O erro comum: deixar no padrão uma chave que devia acumular, tipo uma lista de achados de várias etapas

Contrato do lado da API: JSON outputs e strict tool use

Se a sua orquestração é feita na mão, direto na API da Claude, o contrato existe do mesmo jeito

E olha que aqui é OUTRA camada: não é mais opção de SDK, é campo da requisição

Structured Outputs na API está em disponibilidade geral e não exige mais o cabeçalho beta. O parâmetro passou de output_format para output_config.format, e o formato antigo segue funcionando por um período de transição

  1. Escolha o modo certo pro que você quer garantir. São dois modos complementares: JSON outputs, via output_config.format, para a resposta do modelo, e strict tool use, com strict: true, que garante validação de schema no nome e nos inputs da ferramenta. O erro comum: validar a resposta final e deixar a chamada de ferramenta solta, que é justamente onde a cadeia costuma quebrar
  1. Ajuste o schema pro subconjunto aceito no modo strict. Com strict: true, o schema precisa seguir o subconjunto de JSON Schema aceito: additionalProperties em false em TODO objeto e todas as propriedades listadas em required. O erro comum: reaproveitar um schema que você já tinha, cheio de campo opcional e objeto aberto, e levar erro de validação sem entender o motivo
{
  "type": "object",
  "additionalProperties": false,
  "required": ["acao", "alvo"],
  "properties": {
    "acao": { "type": "string" },
    "alvo": { "type": "string" }
  }
}

Tome cuidado com o hábito de deixar campo opcional "por flexibilidade": no modo strict, todas as propriedades vão em required, então o desenho do contrato tem que ser pensado, não copiado

Quando vale (e quando não vale) quebrar em vários agentes

Agora a parte que ninguém gosta de ouvir: nem toda tarefa merece uma cadeia de agentes

Na avaliação interna da Anthropic, a arquitetura multi-agente com Claude Opus 4 como líder e subagentes Claude Sonnet 4 superou o Claude Opus 4 sozinho em 90,2%, consumindo cerca de 15 vezes mais tokens que uma interação de chat

Se liga nos DOIS números juntos, porque um sozinho engana

Onde a conta fecha:

  • tarefa paralelizável, com várias frentes de exploração independentes
  • tarefa cara em busca, onde vale queimar token explorando pra achar pouca coisa relevante
  • etapas com responsabilidades bem diferentes, que se beneficiam de system prompts próprios

Onde a conta não fecha:

  • tarefa curta e linear, que um agente só resolve numa passada
  • fluxo onde cada etapa depende do resultado imediato da anterior, sem nada pra paralelizar
  • cadeia montada "porque é multi-agente", que é como acabar com 50 subagentes numa consulta simples

E o que torna a conta viável quando ela fecha é o formato de retorno: subagente explorando com dezenas de milhares de tokens e devolvendo de 1.000 a 2.000 destilados

Se cada subagente seu devolve tudo que leu, você não montou uma arquitetura, montou um funil entupido 😛

Conclusão

Handoff é contrato, não conversa

No Claude Agent SDK a fronteira já é estreita por construção (só a mensagem final volta, e o prompt da ferramenta Agent é o único canal de ida), no OpenAI Agents SDK ela é larga por padrão e você fecha com input_filter, e no LangGraph ela é um estado tipado onde o reducer decide quem acumula e quem substitui

Muda a mecânica, não muda a ideia: definir o que atravessa e validar o formato

E lembra da camada: no SDK o contrato se liga na opção do query(), na API ele se liga no campo da requisição

O próximo passo é bem concreto

Pega a etapa mais frágil da sua cadeia hoje, aquela que você já sabe qual é

Escreve o schema dela, troca o retorno em prosa por saída estruturada validada e observa se o erro some

Se aparecer error_max_structured_output_retries, ótimo: a cadeia parou de mentir pra você

té o próximo post! =)

Perguntas frequentes

Como o OpenAI Agents SDK passa contexto entre agentes num handoff?

Por padrão, o agente que recebe o handoff enxerga todo o histórico da conversa, o que já resolve boa parte do telefone sem fio. Quando isso é demais (ou informação sensível demais), dá pra usar um input_filter, uma função que recebe um HandoffInputData e devolve um novo HandoffInputData controlando o que segue adiante. Também existe filtro global no nível do RunConfig, pra aplicar a regra em todos os handoffs de uma vez.

Dá pra forçar saída estruturada no OpenAI Agents SDK como se faz no Claude Agent SDK?

Dá sim: passando output_type ao agente, o modelo usa saída estruturada em vez de texto puro. É o equivalente direto à opção outputFormat (TypeScript) ou output_format (Python) do query() no Claude Agent SDK, só que resolvido no nível do agente em vez do query().

Como funciona a troca de contexto entre nós no LangGraph?

No LangGraph os nós de um StateGraph não trocam mensagem entre si: eles leem e escrevem num estado compartilhado tipado, como um TypedDict ou um modelo Pydantic. Cada nó recebe o estado atual e devolve um dicionário de atualizações, e cada chave pode ter um reducer definindo como combinar valor antigo e novo (o padrão é simplesmente substituir).

Qual a diferença entre output_config.format e strict: true na API da Claude?

São dois modos complementares de Structured Outputs, hoje em disponibilidade geral e sem exigir cabeçalho beta. O output_config.format entrega o JSON output em si, e é o parâmetro atual da API (ele sucedeu o campo output_format da própria API, que ainda funciona por um período de transição). Cuidado pra não confundir camadas: esse output_format em transição é o campo da API, não a opção outputFormat / output_format do query() no Claude Agent SDK, que continua sendo a forma de ativar saída estruturada por lá. Já o strict: true garante validação de schema em nome e inputs de ferramenta, e exige additionalProperties: false em todo objeto com todas as propriedades listadas em required.

O que acontece se o agente não conseguir gerar uma saída que bate com o schema?

No Claude Agent SDK, o próprio SDK valida o resultado contra o schema e refaz o prompt automaticamente quando há divergência. Se mesmo assim não rolar, ele devolve um resultado de erro com o subtype error_max_structured_output_retries, então esse é o sinal de que o schema tá largo demais ou pedindo algo que o agente não tem como responder.

Vale a pena usar arquitetura multi-agente pra qualquer tarefa?

Não necessariamente. Na avaliação interna da Anthropic, o time líder com Opus 4 coordenando subagentes Sonnet 4 superou o Opus 4 sozinho em 90,2%, mas consumindo cerca de 15 vezes mais tokens que uma interação de chat simples. Pra tarefa que um único agente resolve bem, multiplicar agente é só multiplicar custo (e risco de ruído no handoff).



Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação SAAS com IA

Formação SAAS com IA

Tire usas ideias do papel criando softwares com IA, integre pagamentos e lance seu projeto!

  • 291 aulas
  • 18 projetos
  • 24h 17min

Blog | Mais populares