Skip to content

配置结构化输出后,Agent 工作流能返回匹配你定义的 JSON Schema 的验证数据,而非自由文本。通过 outputFormat(TS)或 output_format(Python)参数传入 schema 即可生效。支持 Zod(TypeScript)和 Pydantic(Python)定义 schema 以获得全类型安全对象。若验证重试超过限制,返回 error_max_structured_output_retries 而非结构数据。适合需要把代理输出直接接入应用逻辑、数据库或 UI 的场景。

Claude Code Agent SDK 结构化输出配置

结构化输出让你精确定义从 Agent 返回的数据形状。Agent 可以自由使用所需工具完成多轮任务,最终返回匹配你定义的 JSON Schema 的验证数据。定义一个 JSON Schema 描述所需的数据结构,SDK 会在 Agent 完成后验证输出,失败则重新提示。如果在重试限制内未通过验证,结果会返回错误而不是结构化数据;详见错误处理

为了实现全类型安全,可以用 Zod(TypeScript)或 Pydantic(Python)定义 schema,获得带强类型的对象。

为什么需要结构化输出

Agent 默认返回自由格式文本,这对聊天场景适用,但当你需要程序化使用输出时就力不从心了。结构化输出给你提供类型化的数据,可以直接传给应用逻辑、数据库或 UI 组件。

以菜谱应用为例:Agent 搜索网页带回菜谱。不用结构化输出,你得到的是自由文本,需要自己解析。用结构化输出,你定义好数据形状,就能拿到类型数据直接用在应用里。

**不用结构化输出时的返回示例:** ``` 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 ...
要在应用中使用这些数据,你需要解析出标题、把"15 minutes"转成数字、分离原料和步骤,还得处理不同响应中的格式不一致。
</blockquote>

<blockquote>
**使用结构化输出时的返回示例:**
```json
{
  "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"]
}

类型化的数据可以直接用在 UI 中。

快速开始

使用结构化输出,需要定义一个 JSON Schema 描述你期望的数据形状,然后通过 outputFormat(TypeScript)或 output_format(Python)参数传给 query()。Agent 完成后,结果消息的 structured_output 字段会包含验证过的数据。

下面的例子让 Agent 调研 Anthropic 并返回公司名称、成立年份和总部地址,数据格式符合定义的 schema。

typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

// 定义你想拿回的数据形状
const schema = {
  type: "object",
  properties: {
    company_name: { type: "string" },
    founded_year: { type: "number" },
    headquarters: { type: "string" }
  },
  required: ["company_name"]
};

for await (const message of query({
  prompt: "Research Anthropic and provide key company information",
  options: {
    outputFormat: {
      type: "json_schema",
      schema: schema
    }
  }
})) {
  // 结果消息包含 validated data 的 structured_output
  if (message.type === "result" && message.subtype === "success" && message.structured_output) {
    console.log(message.structured_output);
    // { company_name: "Anthropic", founded_year: 2021, headquarters: "San Francisco, CA" }
  }
}
python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

# 定义你想拿回的数据形状
schema = {
    "type": "object",
    "properties": {
        "company_name": {"type": "string"},
        "founded_year": {"type": "number"},
        "headquarters": {"type": "string"},
    },
    "required": ["company_name"],
}

async def main():
    async for message in query(
        prompt="Research Anthropic and provide key company information",
        options=ClaudeAgentOptions(
            output_format={"type": "json_schema", "schema": schema}
        ),
    ):
        # 结果消息包含 validated data 的 structured_output
        if isinstance(message, ResultMessage) and message.structured_output:
            print(message.structured_output)
            # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco, CA'}

asyncio.run(main())

类型安全的 Schema:Zod 和 Pydantic

不手写 JSON Schema,可以用 Zod(TypeScript)或 Pydantic(Python)定义 schema。这些库会自动生成 JSON Schema,然后你可以把响应解析成全类型对象,在代码库中启用自动补全和类型检查。

下面的例子定义了一个功能实现计划 schema,包含摘要、步骤列表(每步有复杂等级)和潜在风险。Agent 规划功能后返回带类型的 FeaturePlan 对象,你可以直接用 plan.summary 和遍历 plan.steps,全程类型安全。

typescript
import { z } from "zod";
import { query } from "@anthropic-ai/claude-agent-sdk";

// 用 Zod 定义 schema
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>;

// 转为 JSON Schema
const schema = z.toJSONSchema(FeaturePlan);

// 在 query 中使用
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.structured_output) {
    // 验证并获取全类型结果
    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}`);
      });
    }
  }
}
python
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():
    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 message.structured_output:
            # 验证并获取全类型结果
            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}"
                )

asyncio.run(main())

优势:

  • 全类型推导(TypeScript)和类型提示(Python)
  • 运行时验证:safeParse()model_validate()
  • 错误信息更清晰
  • schema 可组合、可复用

输出格式配置项

outputFormat(TypeScript)或 output_format(Python)参数接受一个对象,包含:

  • type:设置为 "json_schema" 启用结构化输出
  • schema:一个 JSON Schema 对象,定义输出结构。可以从 Zod schema(z.toJSONSchema())或 Pydantic 模型(.model_json_schema())生成

SDK 支持标准 JSON Schema 特性,包括所有基础类型(object、array、string、number、boolean、null)、enumconstrequired、嵌套对象和 $ref 定义。完整支持列表和限制详见 JSON Schema limitations

示例:TODO 追踪 Agent

这个示例展示结构化输出如何与多步工具调用配合。Agent 需要先在代码库中找到 TODO 注释,然后查看每个 TODO 的 git blame 信息。它自主决定使用哪些工具(Grep 搜索,Bash 执行 git 命令),然后将结果汇总为一条结构化响应。

schema 中 authordate 字段设为可选,因为某些文件可能没有 git blame 信息。Agent 填入能找到的信息,缺失部分省略。

typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

// 定义 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"]
};

// Agent 使用 Grep 查找 TODO,用 Bash 获取 git blame 信息
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.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}`);
      }
    });
  }
}
python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage

# 定义 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():
    # Agent 使用 Grep 查找 TODO,用 Bash 获取 git blame 信息
    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 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']}")

asyncio.run(main())

错误处理

结构化输出生成失败的情况包括:Agent 无法生成匹配你 schema 的有效 JSON。典型原因有:schema 对当前任务过于复杂、任务本身模糊不清、Agent 在重试修正验证错误时达到限制。

当出错时,结果消息的 subtype 字段会指明原因:

subtype含义
success输出已生成并通过验证
error_max_structured_output_retriesAgent 多次尝试后仍无法生成有效输出

下面的示例检查 subtype 字段,判断输出是否成功生成,是否需要处理失败:

typescript
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.structured_output) {
      // 使用验证过的输出
      console.log(msg.structured_output);
    } else if (msg.subtype === "error_max_structured_output_retries") {
      // 处理失败 - 重试更简单的提示、回退到非结构化输出等
      console.error("Could not produce valid output");
    }
  }
}
python
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 message.structured_output:
            # 使用验证过的输出
            print(message.structured_output)
        elif message.subtype == "error_max_structured_output_retries":
            # 处理失败
            print("Could not produce valid output")

避免出错的技巧:

  • 保持 schema 聚焦。 深度嵌套、多必填字段的 schema 更难满足。先简单,按需增加复杂度。
  • 匹配 schema 与任务。 如果任务可能不包含 schema 要求的所有信息,把这些字段设为可选。
  • 提示词要明确。 模糊的提示让 Agent 更难知道要输出什么。

相关资源

  • JSON Schema 文档:学习用嵌套对象、数组、枚举和验证约束定义复杂 schema 的 JSON Schema 语法
  • API 结构化输出:直接在 Claude API 中使用结构化输出,适用于单轮请求、无工具使用的场景
  • 自定义工具:给 Agent 添加自定义工具,在返回结构化输出前调用

常见问题

结构化输出失败返回纯文本怎么办?

检查 subtype 是否为 error_max_structured_output_retries。如果是,说明 Agent 多次重试后仍无法生成符合 schema 的有效 JSON。常见原因有 schema 太复杂、任务要求的信息不全、或提示词模糊。尝试简化 schema、把必填字段改为可选、或让提示更清晰。

怎么让代理返回指定格式的 JSON 数据?

query() 调用时传入 outputFormat(TS)/ output_format(Python)参数,设置 type: "json_schema" 和定义好的 schema。Agent 完成多轮工具调用后,result 消息的 structured_output 字段就包含已验证的 JSON。如果使用 Zod(TS)或 Pydantic(Python),用 safeParse()model_validate() 可以进一步获得全类型对象。

结构化输出支持多步工具调用吗?

支持。Agent 自主决定使用哪些工具完成多轮调用,最终返回一个符合 schema 的结构化 JSON。例如 TODO 追踪示例中,Agent 先用 Grep 搜索 TODO 注释,再用 Bash 查 git blame,最后汇总数据写入 structured_output