Obter saída estruturada de agentes
Retorne JSON validado de fluxos de trabalho de agentes usando JSON Schema, Zod ou Pydantic. Obtenha dados estruturados e type-safe após o uso de múltiplas ferramentas.
Saídas estruturadas permitem que você defina a forma exata dos dados que deseja receber de um agente. O agente pode usar qualquer ferramenta necessária para completar a tarefa, e você ainda obtém JSON validado correspondente ao seu schema no final. Defina um JSON Schema para a estrutura que você precisa, e o SDK valida a saída contra ele, re-solicitando em caso de incompatibilidade. Se a validação não for bem-sucedida dentro do limite de tentativas, o resultado é um erro em vez de dados estruturados; veja Tratamento de erros.
Para segurança de tipo completa, use Zod (TypeScript) ou Pydantic (Python) para definir seu esquema e obter objetos fortemente tipados.
Por que saídas estruturadas?
Os agentes retornam texto livre por padrão, o que funciona para chat, mas não quando você precisa usar a saída programaticamente. As saídas estruturadas fornecem dados tipados que você pode passar diretamente para a lógica da sua aplicação, banco de dados ou componentes de UI.
Considere um aplicativo de receitas em que um agente pesquisa na web e traz receitas. Sem saídas estruturadas, você recebe texto livre que precisaria analisar por conta própria. Com saídas estruturadas, você define o formato desejado e obtém dados tipados que pode usar diretamente no seu aplicativo.
[accordion] Sem saídas estruturadas
Here's a classic chocolate chip cookie recipe! **Chocolate Chip Cookies** Prep time: 15 minutes | Cook time: 10 minutes Ingredients: - 2 1/4 cups all-purpose flour - 1 cup butter, softened ...Para usar isso no seu aplicativo, você precisaria extrair o título, converter "15 minutes" em um número, separar os ingredientes das instruções e lidar com formatação inconsistente entre as respostas.
[accordion] Com saídas estruturadas
{ "name": "Chocolate Chip Cookies", "prep_time_minutes": 15, "cook_time_minutes": 10, "ingredients": [ { "item": "all-purpose flour", "amount": 2.25, "unit": "cups" }, { "item": "butter, softened", "amount": 1, "unit": "cup" } // ... ], "steps": ["Preheat oven to 375°F", "Cream butter and sugar" /* ... */] }Dados tipados que você pode usar diretamente na sua UI.
Início rápido
Para usar saídas estruturadas, defina um JSON Schema descrevendo a forma dos dados que você deseja, depois passe-o para query() via a opção outputFormat (TypeScript) ou output_format (Python). Quando o agente terminar, a mensagem de resultado inclui um campo structured_output com dados validados correspondendo ao seu schema.
Antes de executar os exemplos desta página, instale o Claude Agent SDK seguindo o guia de início rápido. O exemplo abaixo pede ao agente para pesquisar Anthropic e retornar o nome da empresa, ano de fundação e sede como saída estruturada.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define a forma dos dados que você deseja receber
const schema = {
type: "object",
properties: {
company_name: { type: "string" },
founded_year: { type: "number" },
headquarters: { type: "string" }
},
required: ["company_name"]
};
try {
for await (const message of query({
prompt: "Research Anthropic and provide key company information",
options: {
outputFormat: {
type: "json_schema",
schema: schema
}
}
})) {
// A mensagem de resultado contém structured_output com dados validados
if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
console.log(message.structured_output);
// { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
}
}
} catch (error) {
// Uma query() de uma única tentativa lança um erro após gerar um resultado de erro, como
// error_max_structured_output_retries; veja a seção Tratamento de erros.
console.error(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# Define a forma dos dados que você deseja receber
schema = {
"type": "object",
"properties": {
"company_name": {"type": "string"},
"founded_year": {"type": "number"},
"headquarters": {"type": "string"},
},
"required": ["company_name"],
}
async def main():
try:
async for message in query(
prompt="Research Anthropic and provide key company information",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": schema}
),
):
# A mensagem de resultado contém structured_output com dados validados
if (
isinstance(message, ResultMessage)
and not message.is_error
and message.structured_output
):
print(message.structured_output)
# {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}
except Exception as error:
# Uma query() de uma única tentativa lança um erro após gerar um resultado de erro, como
# error_max_structured_output_retries; veja a seção Tratamento de erros.
print(f"Session ended with an error: {error}")
asyncio.run(main())
Schemas type-safe com Zod e Pydantic
Em vez de escrever JSON Schema manualmente, você pode usar Zod (TypeScript) ou Pydantic (Python) para definir seu schema. Essas bibliotecas geram o JSON Schema para você e permitem que você analise a resposta em um objeto totalmente tipado que você pode usar em todo seu codebase com autocomplete e verificação de tipo.
O exemplo abaixo define um schema para um plano de implementação de recurso com um resumo, lista de etapas (cada uma com nível de complexidade) e riscos potenciais. O agente planeja o recurso e retorna um objeto FeaturePlan tipado. Você pode então acessar propriedades como plan.summary e iterar sobre plan.steps com segurança de tipo completa.
O SDK valida schemas com JSON Schema draft-07, portanto schemas que declaram uma versão mais recente são rejeitados. Zod tem como alvo draft 2020-12 por padrão, então passe target: "draft-7" ao converter seu schema.
import { z } from "zod";
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define schema com Zod
const FeaturePlan = z.object({
feature_name: z.string(),
summary: z.string(),
steps: z.array(
z.object({
step_number: z.number(),
description: z.string(),
estimated_complexity: z.enum(["low", "medium", "high"])
})
),
risks: z.array(z.string())
});
type FeaturePlan = z.infer<typeof FeaturePlan>;
// Converta para JSON Schema usando o alvo draft-07 que o SDK espera
const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
// Use em query
try {
for await (const message of query({
prompt:
"Plan how to add dark mode support to a React app. Break it into implementation steps.",
options: {
outputFormat: {
type: "json_schema",
schema: schema
}
}
})) {
if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
// Valide e obtenha resultado totalmente tipado
const parsed = FeaturePlan.safeParse(message.structured_output);
if (parsed.success) {
const plan: FeaturePlan = parsed.data;
console.log(`Feature: ${plan.feature_name}`);
console.log(`Summary: ${plan.summary}`);
plan.steps.forEach((step) => {
console.log(`${step.step_number}. [${step.estimated_complexity}] ${step.description}`);
});
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result, such as
// error_max_structured_output_retries; see the Error handling section.
console.error(`Session ended with an error: ${error}`);
}
import asyncio
from pydantic import BaseModel
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
class Step(BaseModel):
step_number: int
description: str
estimated_complexity: str # 'low', 'medium', 'high'
class FeaturePlan(BaseModel):
feature_name: str
summary: str
steps: list[Step]
risks: list[str]
async def main():
try:
async for message in query(
prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
options=ClaudeAgentOptions(
output_format={
"type": "json_schema",
"schema": FeaturePlan.model_json_schema(),
}
),
):
if (
isinstance(message, ResultMessage)
and not message.is_error
and message.structured_output
):
# Valide e obtenha resultado totalmente tipado
plan = FeaturePlan.model_validate(message.structured_output)
print(f"Feature: {plan.feature_name}")
print(f"Summary: {plan.summary}")
for step in plan.steps:
print(
f"{step.step_number}. [{step.estimated_complexity}] {step.description}"
)
except Exception as error:
# A single-shot query() raises after yielding an error result, such as
# error_max_structured_output_retries; see the Error handling section.
print(f"Session ended with an error: {error}")
asyncio.run(main())
Configuração do formato de saída
A opção outputFormat (TypeScript) ou output_format (Python) aceita um objeto com:
type: Defina como"json_schema"para saídas estruturadasschema: Um objeto JSON Schema definindo sua estrutura de saída. Você pode gerar isso a partir de um schema Zod comz.toJSONSchema(schema, { target: "draft-7" })ou um modelo Pydantic com.model_json_schema()
O SDK suporta recursos padrão de JSON Schema, incluindo todos os tipos básicos (object, array, string, number, boolean, null), enum, const, required, objetos aninhados e definições $ref. Para a lista completa de recursos suportados e limitações, veja Limitações de JSON Schema.
Um schema que não é um JSON Schema válido falha na execução na inicialização com um erro nomeando o problema. Antes da v2.1.205, um schema inválido era silenciosamente ignorado e o agente retornava texto não estruturado.
A palavra-chave format, como "format": "email", é aceita como uma anotação e não é aplicada pelo validador do SDK. Antes da v2.1.205, qualquer schema contendo format era tratado como inválido.
Exemplo: agente de rastreamento de TODO
Este exemplo demonstra como saídas estruturadas funcionam com uso de ferramentas em múltiplas etapas. O agente precisa encontrar comentários TODO no codebase, depois procurar informações de git blame para cada um. Ele decide autonomamente quais ferramentas usar (Grep para pesquisar, Bash para executar comandos git) e combina os resultados em uma única resposta estruturada.
O schema inclui campos opcionais (author e date) já que informações de git blame podem não estar disponíveis para todos os arquivos. O agente preenche o que consegue encontrar e omite o resto.
import { query } from "@anthropic-ai/claude-agent-sdk";
// Define estrutura para extração de TODO
const todoSchema = {
type: "object",
properties: {
todos: {
type: "array",
items: {
type: "object",
properties: {
text: { type: "string" },
file: { type: "string" },
line: { type: "number" },
author: { type: "string" },
date: { type: "string" }
},
required: ["text", "file", "line"]
}
},
total_count: { type: "number" }
},
required: ["todos", "total_count"]
};
// Agente usa Grep para encontrar TODOs, Bash para obter informações de git blame
try {
for await (const message of query({
prompt: "Find all TODO comments in this codebase and identify who added them",
options: {
outputFormat: {
type: "json_schema",
schema: todoSchema
}
}
})) {
if (message.type === "result" && message.subtype === "success" && !message.is_error && message.structured_output) {
const data = message.structured_output as { total_count: number; todos: Array<{ file: string; line: number; text: string; author?: string; date?: string }> };
console.log(`Found ${data.total_count} TODOs`);
data.todos.forEach((todo) => {
console.log(`${todo.file}:${todo.line} - ${todo.text}`);
if (todo.author) {
console.log(` Added by ${todo.author} on ${todo.date}`);
}
});
}
}
} catch (error) {
// Uma query() de uma única tentativa lança um erro após gerar um resultado de erro, como
// error_max_structured_output_retries; veja a seção Tratamento de erros.
console.error(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
# Define estrutura para extração de TODO
todo_schema = {
"type": "object",
"properties": {
"todos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"text": {"type": "string"},
"file": {"type": "string"},
"line": {"type": "number"},
"author": {"type": "string"},
"date": {"type": "string"},
},
"required": ["text", "file", "line"],
},
},
"total_count": {"type": "number"},
},
"required": ["todos", "total_count"],
}
async def main():
# Agente usa Grep para encontrar TODOs, Bash para obter informações de git blame
try:
async for message in query(
prompt="Find all TODO comments in this codebase and identify who added them",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": todo_schema}
),
):
if (
isinstance(message, ResultMessage)
and not message.is_error
and message.structured_output
):
data = message.structured_output
print(f"Found {data['total_count']} TODOs")
for todo in data["todos"]:
print(f"{todo['file']}:{todo['line']} - {todo['text']}")
if "author" in todo:
print(f" Added by {todo['author']} on {todo['date']}")
except Exception as error:
# Uma query() de uma única tentativa lança um erro após gerar um resultado de erro, como
# error_max_structured_output_retries; veja a seção Tratamento de erros.
print(f"Session ended with an error: {error}")
asyncio.run(main())
Reconhecer a chamada de ferramenta StructuredOutput
Quando você define outputFormat em TypeScript ou output_format em Python, seu fluxo de mensagens contém uma chamada de ferramenta chamada StructuredOutput que você nunca definiu. O Claude Code adiciona essa ferramenta à sessão, e o agente a chama para retornar sua resposta. A mensagem de inicialização do sistema a lista no array tools, e cada chamada chega como dois blocos:
- Um bloco
tool_usechamadoStructuredOutput, cuja entrada é o objeto que o agente produziu para o seu esquema - Um bloco
tool_resultcom o textoStructured output provided successfully
Quando esse objeto não corresponde ao seu esquema, o tool_result contém is_error: true em vez disso e o agente chama StructuredOutput novamente, de modo que seu fluxo pode conter várias chamadas para uma única consulta. Leia os dados validados de structured_output na mensagem de resultado, e não de um bloco tool_use, e consulte Tratamento de erros para ver o resultado que você recebe quando nenhuma tentativa é bem-sucedida.
Se sua aplicação exibe ou registra em log as chamadas de ferramenta, ignore as chamadas chamadas StructuredOutput para que o mecanismo não apareça como uma etapa que o agente escolheu. Um bloco tool_result não contém nome de ferramenta, apenas o tool_use_id da chamada à qual responde, então compare-o com o id de cada chamada que você ignorou.
Tratamento de erros
A geração de saída estruturada pode falhar quando o agente não consegue produzir JSON válido correspondendo ao seu esquema. Isso normalmente acontece quando o esquema é muito complexo para a tarefa, a tarefa em si é ambígua, ou o agente atinge seu limite de tentativas tentando corrigir erros de validação. Também pode acontecer sem nenhuma falha de validação: um fallback de modelo pode retratar uma saída já concluída no meio do fluxo, e se nenhuma nova tentativa a substituir, a execução termina com o mesmo erro. Verifique a lista errors na mensagem de resultado de erro para distinguir as duas causas antes de depurar seu esquema.
Verifique o subtype da mensagem de resultado:
| Subtype | Significado |
|---|---|
success |
A execução terminou sem um subtype de erro |
error_max_structured_output_retries |
Nenhuma saída válida sobreviveu após múltiplas tentativas (falhas de validação, ou uma retração de fallback de modelo sem tentativa bem-sucedida) |
Um subtype success não significa por si só que a execução produziu uma resposta. Trate um resultado como uma falha quando o is_error do resultado for true ou structured_output estiver ausente:
is_errorno resultado étrue: por exemplo, a requisição final ao modelo terminou em um erro de API ou em uma recusa. Não use um valorstructured_outputem tal resultado.structured_outputestá ausente: para encontrar a causa, consulte structured_output is None but the result says success
O exemplo abaixo aplica essas verificações:
import { query } from "@anthropic-ai/claude-agent-sdk";
const contactSchema = {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string" }
},
required: ["name"]
};
try {
for await (const msg of query({
prompt: "Extract contact info from the document",
options: {
outputFormat: {
type: "json_schema",
schema: contactSchema
}
}
})) {
if (msg.type === "result") {
if (msg.subtype === "success" && !msg.is_error && msg.structured_output) {
// Use the validated output
console.log(msg.structured_output);
} else if (msg.subtype === "error_max_structured_output_retries") {
console.error("Could not produce valid output");
} else {
console.error("Run ended with an error or without a structured output");
}
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the error subtype branches above have
// already run; connection or process failures yield no result message.
console.log(`Session ended with an error: ${error}`);
}
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
contact_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
},
"required": ["name"],
}
async def main():
try:
async for message in query(
prompt="Extract contact info from the document",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": contact_schema}
),
):
if isinstance(message, ResultMessage):
if (
message.subtype == "success"
and not message.is_error
and message.structured_output
):
# Use the validated output
print(message.structured_output)
elif message.subtype == "error_max_structured_output_retries":
print("Could not produce valid output")
else:
print("Run ended with an error or without a structured output")
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the error subtype branches above have
# already run; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
asyncio.run(main())
Dicas para evitar erros:
- Mantenha schemas focados. Schemas profundamente aninhados com muitos campos obrigatórios são mais difíceis de satisfazer. Comece simples e adicione complexidade conforme necessário.
- Corresponda schema à tarefa. Se a tarefa pode não ter todas as informações que seu schema requer, torne esses campos opcionais.
- Use prompts claros. Prompts ambíguos dificultam para o agente saber qual saída produzir.
Recursos relacionados
- Documentação de JSON Schema: aprenda sintaxe de JSON Schema para definir schemas complexos com objetos aninhados, arrays, enums e restrições de validação
- Saídas Estruturadas da API: use saídas estruturadas com a API Claude diretamente para solicitações de turno único sem uso de ferramentas
- Ferramentas personalizadas: dê ao seu agente ferramentas personalizadas para chamar durante a execução antes de retornar saída estruturada