Typography

学习笔记

Claude Agent SDK学习07 结构化输出

发布于 # Claude Agent SDK

当前 Agent 的问题

第六章之后,你的 agent 已经能安全地执行工具,但结果仍然有一个根本性的工程缺口:输出还是自由文本。

自由文本适合人类阅读,但一旦你要接系统,就会变得脆弱:

所以这一章的目标,是让 agent 的最终输出从自然语言升级为被 schema 约束的数据。

本章功能的作用

这一章会引入结构化输出能力。

你要掌握的对象包括:

它带来的核心收益,是把 Claude 的最终结果从“适合人读的自然语言”升级成“适合程序读的稳定对象”。一旦结果要进入前端渲染、数据库写入、自动工作流或后续 agent 链路,这种稳定结构就会变得非常重要。

官方实现里,structured output 并不是“要求模型吐一段 JSON 文本”这么简单。SDK 会根据 schema 校验最终结果,不符合就会驱动重试;如果多次校验都失败,最终结果会以错误结束,而不是悄悄给你一个结构不稳定的对象。

具体使用方式

第一步:先定义一个足够简单的 JSON Schema

最稳妥的做法是先从对象、字符串、数组这些基本结构开始。Schema 的职责是定义“最终结果必须长什么样”,而不是把 Claude 的全部思考过程都结构化。

很多学习者一开始就会把 schema 设计得过深、过细,结果很难判断是模型理解错了,还是结构本身不合理。教学阶段先用扁平结构,能更快建立“Claude 可以被约束到什么程度”的直觉。

等你进入真实项目后,再逐步引入 Zod 或 Pydantic 会更自然。这样 schema 不只是用来约束 Claude,还能同时承担运行时校验和类型推导,让 structured_output 真正成为类型安全的数据源。

第二步:把 schema 挂到 outputFormat

query()options 中传入:

outputFormat: {
  type: "json_schema",
  schema
}

这一步告诉 SDK:最终结果必须通过这个 schema 校验。

第三步:在 result 中读取 structured_output

真正可供程序消费的对象不在自由文本里,而在 message.structured_output。如果你要把结果交给前端、数据库或工作流系统,应该优先读取这个字段。

第四步:把 schema 设计成“业务结果结构”,不是“展示格式”

例如 stepsriskssummary 很适合放入 schema;而复杂的解释性长文通常更适合保留在自然语言层。这样 schema 才稳定、可复用。

关键概念

结构化输出的核心思想

Claude 在执行过程中依然可以自由使用工具、自由思考,但在最终收尾时,SDK 会要求它返回一份满足指定 schema 的结果。

为什么它比“让模型输出 JSON 文本”更稳

因为 SDK 会:

适合放进 schema 的内容

最适合的是业务边界清晰的数据,例如:

可运行示例

把下面代码保存为 chapter-07-structured-output.ts

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

const schema = {
  type: "object",
  properties: {
    feature_name: { type: "string" },
    summary: { type: "string" },
    steps: {
      type: "array",
      items: { type: "string" }
    },
    risks: {
      type: "array",
      items: { type: "string" }
    }
  },
  required: ["feature_name", "summary", "steps", "risks"]
};

async function main() {
  for await (const message of query({
    prompt: "Plan how to add password reset support to a web application.",
    options: {
      outputFormat: {
        type: "json_schema",
        schema
      }
    }
  })) {
    if (message.type === "result" && message.subtype === "success") {
      console.log("Structured output:\n");
      console.dir(message.structured_output, { depth: null });
    }
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

运行:

npx tsx chapter-07-structured-output.ts

示例拆解

第一步:先声明一个规划类 schema

示例把输出固定为 feature_namesummarystepsrisks 四个字段。这种结构很适合教学,因为它同时覆盖了单值字段和数组字段。

第二步:让 Claude 处理一个明确但非代码型任务

示例 prompt 是“为密码重置功能做规划”。这类任务最容易产生稳定的结构化结果,也能避免文件工具等额外因素干扰学习重点。

第三步:只在成功结果里读取 structured_output

代码明确判断 message.type === "result" && message.subtype === "success",然后才打印 structured_output。这一步强调了结构化输出是结果收口能力,而不是中间过程能力。

第四步:用 console.dir() 观察对象真实形状

示例不用普通字符串拼接,而是用 console.dir(..., { depth: null }) 输出对象。这样读者能直接看到结构,而不是阅读一段看起来像 JSON 的文本。

运行时你应该观察什么

一个很实用的延伸

如果你在 TypeScript 项目里工作,下一步通常会接入 zod,让 schema 同时承担:

这样 structured_output 可以直接变成强类型对象。

易错点

本章结束后你应该掌握

本章小结

到这里,你的 agent 已经不只是“能帮人看懂结果”,而是“能把结果交给别的程序继续处理”。这通常是产品化分水岭。