Saída estruturada entre agentes: como exigir JSON com schema e parar de quebrar no parse

saída estruturada em JSON Schema entre agentes de IA
Resposta rápida

Saída estruturada é quando você para de pedir JSON no texto do prompt e passa a exigir um JSON Schema que vira gramática restringindo os tokens gerados na inferência. Na API da Claude ela está em disponibilidade geral desde 29 de janeiro de 2026, sem beta header, com o parâmetro em output_config.format. Para argumento de ferramenta existe o strict tool use, e no Claude Agent SDK a opção outputFormat devolve os dados validados no campo structured_output. Restam dois ramos que fogem do schema: recusa por segurança e resposta cortada por max_tokens. Trate os dois e metade do seu problema de orquestração vira erro explícito.

Fala aí, beleza? Se você já montou uma cadeia de agentes, conhece a cena: o agente 2 recebe um texto lindo do agente 1 e fica tentando adivinhar onde o JSON começa

Aí vem o regex, vem o try/catch, vem a crase de bloco de código na frente do objeto, e a orquestração inteira quebra num ponto que não tem NADA a ver com raciocínio do modelo

A boa notícia é que existe um caminho determinístico pra isso: o JSON Schema é compilado em uma gramática que restringe os tokens gerados durante a inferência (constrained decoding), em vez de pedir JSON no texto do prompt

E isso deixou de ser experimento: a saída estruturada da API da Claude está em disponibilidade geral (GA) desde 29 de janeiro de 2026, e não exige mais beta header 🙂

O que você precisa antes de exigir schema entre agentes

Checagem rápida antes de abrir o editor

  • Um modelo com saída estruturada disponível na API da Claude: hoje a lista é Claude Mythos Preview, Opus 4.7, Opus 4.6, Sonnet 4.6, Sonnet 4.5, Opus 4.5 e Haiku 4.5
  • Saber QUAL retorno você quer travar: a resposta final do agente e a chamada de ferramenta são caminhos diferentes, e é super comum travar um e deixar o outro solto
  • O schema desenhado em JSON Schema: dá pra declarar com Zod no TypeScript ou Pydantic no Python, ninguém precisa escrever aquilo na unha

Roda na AWS? Também tem: o recurso está em GA no Amazon Bedrock pros modelos Claude 4.5, nas APIs Converse, ConverseStream, InvokeModel e InvokeModelWithResponseStream, em todas as regiões comerciais com Bedrock

Formação Claude Code
Formação Recomendada

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 45min

Pedir JSON e exigir schema não são a mesma coisa:

Essa confusão é a raiz de metade da dor

O modo JSON garante que a resposta é um JSON válido, e só isso

Ele NÃO garante que o objeto tem os campos que a próxima etapa espera, com os tipos que ela espera

Quem garante aderência exata ao schema fornecido é a saída estruturada

E não é papo de marketing, é diferença de mecanismo: na avaliação de aderência a JSON Schema complexo da própria OpenAI, os números ficaram assim

Configuração Aderência a JSON Schema complexo (avaliação da OpenAI)
gpt-4-0613 abaixo de 40%
Modelo treinado para schemas complexos 93%
gpt-4o-2024-08-06 com Structured Outputs 100%

Se liga no detalhe mais importante dessa tabela: treinar o modelo pra entender schema levou a coisa até 93%

Os 100% só apareceram com a abordagem determinística de restringir a saída

Ou seja, prompt caprichado ajuda, mas quem fecha a conta é a gramática

Como exigir JSON com schema validado, passo a passo

  1. Escreva o schema só com os recursos suportados

No Claude Agent SDK são aceitos tipos básicos (object, array, string, number, boolean, null), enum, const, required, objetos aninhados e definições via $ref

Com Zod fica assim, e o schema vira o contrato do handoff

import { z } from "zod"

const Handoff = z.object({
  status: z.enum(["ok", "parcial", "falhou"]),
  arquivos: z.array(z.object({
    caminho: z.string(),
    linhas_alteradas: z.number()
  })),
  resumo: z.string()
})

O erro comum deste passo: enfiar um recurso exótico de JSON Schema que não está nessa lista e descobrir isso só na hora da chamada

  1. Ligue pelo parâmetro certo na API da Claude

Na virada pra GA o parâmetro mudou de lugar: output_format virou output_config.format

O header antigo anthropic-beta: structured-outputs-2025-11-13 junto do output_format continua funcionando por um período de transição, então nada explode hoje

O erro comum deste passo: copiar tutorial da época do beta, deixar rodando no caminho antigo e nunca migrar

  1. Se o dado crítico é o argumento da ferramenta, use strict tool use

Muita orquestração não quebra na resposta final, quebra no input que o agente passa pra ferramenta

Pra isso existe o strict tool use: "strict": true entra como propriedade de topo na definição da ferramenta, do lado de name, description e input_schema

{
  "name": "registrar_resultado",
  "description": "Grava o resultado da etapa anterior",
  "input_schema": {
    "type": "object",
    "properties": {
      "status": { "type": "string", "enum": ["ok", "parcial", "falhou"] },
      "resumo": { "type": "string" }
    },
    "required": ["status", "resumo"]
  },
  "strict": true
}

Com isso o campo input do bloco tool_use passa a seguir o input_schema, e o name é sempre válido

É o mesmo pipeline de gramática, só que apontado pra chamada de ferramenta

O erro comum deste passo: travar só a resposta final e deixar a chamada de ferramenta solta, que é justamente onde o dado vira efeito colateral no seu sistema

  1. No Claude Agent SDK, leia o campo validado

Esse é o caso do agente que usa ferramentas no meio do caminho e MESMO ASSIM precisa devolver JSON validado no final

Tem a opção outputFormat no query() em TypeScript, e output_format em Python

Quando o agente termina, a result message traz o campo structured_output com os dados já validados

O erro comum deste passo: continuar fazendo regex no texto final do agente, com o campo validado ali do lado esperando ser lido 😛

  1. Mantenha o schema enxuto desde o primeiro dia

Campo opcional é o item que mais infla a gramática compilada: cada parâmetro opcional aproximadamente dobra uma parte do espaço de estados

E não é só ele: parâmetros opcionais, união de tipos, objetos aninhados e número de ferramentas interagem entre si

O erro comum deste passo: marcar tudo como opcional "por segurança", que é exatamente o caminho mais rápido pro schema estourar limite de complexidade

Quando o retorno não bate com o schema: o que aconteceu e o que fazer

Tem gente que liga o recurso e assume que agora TODA resposta é um objeto válido

Não é bem assim, e os casos são documentados

Caso 1: recusa por segurança

A recusa tem precedência sobre o schema

Ou seja, o modelo pode recusar e aquele retorno não vai ser o seu objeto

Conduta: a etapa que consome esse retorno precisa tratar esse ramo explicitamente, em vez de assumir objeto válido e seguir a vida

Prevenção: trate recusa como um estado normal da cadeia, com log e parada limpa, não como exceção genérica

Caso 2: resposta cortada no limite de tokens

Se a geração bate no max_tokens, ela para no meio

JSON incompleto é JSON quebrado, mesmo com a gramática restringindo os tokens

Conduta: dimensionar o limite pro tamanho REAL do objeto que você pediu

Tome cuidado com schema que devolve array de tamanho livre: é aí que o objeto cresce sem você perceber

Caso 3: o schema nem chega a rodar (erro 400)

Esse aqui nem é resposta fora do formato, é a chamada morrendo antes

A API responde com mensagens do tipo Schema is too complex ou Too many recursive definitions in schema

A razão é que existem limites de complexidade justamente pra evitar tempo excessivo de compilação da gramática

A solução documentada é bem direta

  • simplificar a estrutura
  • reduzir a profundidade de aninhamento
  • quebrar em schemas menores
  • dividir em múltiplas requisições
  • marcar como strict só as ferramentas críticas

E olha, erro 400 explícito é BOA notícia

Bem melhor que JSON quebrado passando silencioso pro agente seguinte e explodindo três etapas depois, num lugar que não tem relação nenhuma com a causa

Orquestração com provedores diferentes: o mesmo contrato em cada API

Quem mistura fornecedores na mesma cadeia vive esse problema em dobro: cada etapa fala um dialeto

A sacada é parar de tratar o prompt como contrato

O contrato entre etapas é o SCHEMA, o prompt é só como você pede

Na OpenAI, você passa json_schema no parâmetro response_format com strict: true

No Gemini, define responseMimeType como application/json e o responseSchema dentro do generation_config

"generation_config": {
  "responseMimeType": "application/json",
  "responseSchema": { "...": "seu JSON Schema aqui" }
}

Os SDKs GenAI do Gemini aceitam o schema via Pydantic no Python e Zod no JavaScript, então o mesmo objeto que você já declarou serve

Resumo do mapa

Onde Como se ativa
API da Claude (resposta final) output_config.format (o output_format com o header anthropic-beta: structured-outputs-2025-11-13 segue valendo por um período de transição)
API da Claude (chamada de ferramenta) "strict": true na definição da ferramenta
Claude Agent SDK outputFormat no query() em TypeScript, output_format em Python, resultado em structured_output
Amazon Bedrock (Claude 4.5) GA em Converse, ConverseStream, InvokeModel e InvokeModelWithResponseStream
OpenAI json_schema no response_format, com strict: true
Gemini responseMimeType e responseSchema no generation_config

Se as três pontas produzem e consomem o MESMO schema, o handoff deixa de depender do humor do modelo na hora de formatar texto

Conclusão

Enquanto o handoff entre agentes for texto livre, boa parte dos seus erros não vai ser de raciocínio, vai ser de parse

Não tenho número medido pra cravar a proporção, isso é argumento, não dado

Mas o mecanismo é o que interessa: gramática que restringe token é determinístico, regex em texto de modelo é torcida

Próximo passo, pequeno e concreto

  1. pegue UM ponto da sua orquestração onde uma etapa lê a saída da anterior
  2. escreva o schema mínimo desse handoff, só os campos que a próxima etapa realmente usa
  3. ligue pelo parâmetro do produto que você usa (output_config.format, strict na ferramenta, outputFormat no Agent SDK, response_format ou responseSchema)
  4. trate os dois ramos que fogem do schema na resposta: recusa e corte por limite de tokens

Só depois disso funcionando você expande pro resto da cadeia

Começar travando tudo de uma vez é o caminho mais rápido pro Schema is too complex e pra frustração 😀

até o próximo post!

Perguntas frequentes

Quais modelos da Claude têm saída estruturada disponível na API hoje?

A lista atual é Claude Mythos Preview, Opus 4.7, Opus 4.6, Sonnet 4.6, Sonnet 4.5, Opus 4.5 e Haiku 4.5. Essa cobertura é maior que a do beta público, que saiu com menos modelos. Vale conferir se o modelo que você já usa na cadeia de agentes está nessa lista antes de desenhar o schema.

Saída estruturada e strict tool use resolvem o mesmo problema?

Não exatamente: são dois pontos diferentes da mesma cadeia. A saída estruturada trava o formato da resposta final do modelo, enquanto o strict tool use trava o campo input de uma chamada de ferramenta, com strict: true na definição da ferramenta ao lado de name, description e input_schema. Os dois usam o mesmo pipeline de gramática por trás, mas é comum travar só um e deixar o outro solto.

Preciso trocar output_format por output_config.format agora que saiu do beta?

Não com urgência: o parâmetro output_format e o header anthropic-beta: structured-outputs-2025-11-13 continuam funcionando por um período de transição. Mas o nome atual, já que o recurso está em GA desde 29 de janeiro de 2026, é output_config.format. Migrar o código que ainda usa o caminho do beta evita ficar preso a um parâmetro que um dia sai de circulação.

Saída estruturada elimina totalmente a chance de resposta fora do schema?

Não em 100% dos casos, e isso é documentado. São duas situações em que a RESPOSTA sai fora do schema mesmo com o recurso ligado: recusa por segurança, que tem precedência sobre o schema, e resposta cortada por atingir o limite de max_tokens. E tem um terceiro caso, que acontece antes disso: schema complexo demais é rejeitado com erro 400 (Schema is too complex, Too many recursive definitions in schema), aí a chamada nem chega a rodar. Fora esses casos, a decodificação restrita garante aderência exata ao schema fornecido.

Dá pra usar saída estruturada da Claude no Amazon Bedrock?

Dá sim, e também está em GA. Nos modelos Claude 4.5 funciona no Bedrock nas APIs Converse, ConverseStream, InvokeModel e InvokeModelWithResponseStream, em todas as regiões comerciais da AWS que têm Bedrock. Então quem orquestra agentes via Bedrock não fica de fora desse recurso.

E se eu orquestro agentes com Gemini junto de Claude, existe equivalente?

Existe. No Gemini o caminho é definir responseMimeType como application/json e passar responseSchema dentro do generation_config. Os SDKs GenAI aceitam o schema via Pydantic em Python e Zod em JavaScript, o que facilita manter o mesmo contrato de dados entre agentes de provedores diferentes.



Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares