SpyBara
Go Premium

agent-sdk/structured-outputs.md 2026-11-09 23:02 UTC to 2026-11-10 21:57 UTC

This page contains 48 additions and 27 deletions.

2026
Tue 10 21:57

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 estruturadas
  • schema: Um objeto JSON Schema definindo sua estrutura de saída. Você pode gerar isso a partir de um schema Zod com z.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_use chamado StructuredOutput, cuja entrada é o objeto que o agente produziu para o seu esquema
  • Um bloco tool_result com o texto Structured 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_error no resultado é true: por exemplo, a requisição final ao modelo terminou em um erro de API ou em uma recusa. Não use um valor structured_output em tal resultado.
  • structured_output está 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