Claude Code 学习站

结构化输出与 JSON Schema

考点 4.3 · Structured output & schema · 所属域权重 20% · 提示工程与结构化输出

本页目录5

这个考点是什么

这个考点考的是 Anthropic API 里「把输出结构锁死」的两条通道,以及它们各自管什么、不管什么。

第一条是 Structured Outputs:通过 output_config.formattype: "json_schema" + schema)约束 Claude 最终回复消息本身的 JSON 结构,适合抽取、分类这类需要拿到规整数据的场景。

第二条是 strict tool use:在工具定义上加 strict: true,约束的是 Claude 调用某个工具时传入的参数tool_use.input)是否严格符合该工具的 input_schema

两者都靠 grammar-constrained sampling(约束解码)在生成阶段就把非法 token 排除,而不是生成完再校验重试,共用同一套受限 JSON Schema 子集与编译-缓存管线。

这套机制配的 JSON Schema 不是完整规范,而是一个受限子集。

两种模式共用一条硬性门槛:

  • schema 里每个 object 节点都要显式写 additionalProperties: false,这是不能省略、不能设为 true 的强制项;

  • required 数组不需要列全所有字段——只列出真正必需的字段即可,未列入 required 的属性就是允许省略的可选字段(官方 get_weather 示例里 unit 就没进 required,schema 依然合法)。

同时,数值范围(minimum/maximum/multipleOf)、字符串长度(minLength/maxLength)、递归 schema、外部 $refenum 里的复杂类型等都不受支持,只有 minItems 为 0 或 1、以及一组白名单字符串 format(如 date-time/email/uuid)例外,其余约束只能靠 SDK(Pydantic/Zod)在发给模型前剥离、转成描述文字,再在拿到响应后本地二次校验。

这条考点还牵扯到一段正在过渡期的历史遗留:旧的「assistant 消息开头预填一个 { 强制走 JSON」的技巧,在当前主力模型(Opus 4.6/4.7/4.8、Sonnet 5、Fable 5 等)上对最后一轮 assistant 内容做 prefill 已经直接返回 400,官方给出的替代路径就是 Structured Outputs。

参数命名上也有新旧交替——output_config.format 是当前规范用法,不再需要 beta header;旧的顶层 output_format 参数和旧 beta header(structured-outputs-2025-11-13)仍可用但已弃用,处于过渡期。

为什么考

出题角度集中在几件容易混的事:

  • 一是逼考生在「Claude 返回了什么」和「Claude 调用工具时传了什么」之间做二选一,考的是 output_config.formatstrict: true 的作用域边界;

  • 二是考 schema 合法性——additionalProperties: false 是不是硬性要求、required 是不是必须列全字段(常见干扰项会把「必须列全」包装成正确项来诱导误选)、以及哪些 JSON Schema 关键字其实进不了受支持子集;

  • 三是结合模型迁移场景(如 Opus 4.8 弃用 prefill),考是否知道当前推荐替代方案而不是死守旧技巧;

  • 四是用强制 tool_choice 的场景考一个常被忽略的副作用——要求的说明文字会被直接跳过。

核心辨析

  1. 1

    output_config.formatstrict: true 管的是两件不同的事,不能互相替代:

    • 前者约束 Claude 最终回复消息本身的 JSON 结构,适合抽取/分类/生成结构化数据;

    • 后者只约束某次工具调用tool_use.input 是否符合该工具的 input_schema,与最终回复无关。

    两者可以独立开启,也可以同时用,谁都不隐含谁,缺一个不会连带另一个报错。

  2. 2

    additionalProperties: false 是两种机制共同的硬性门槛,schema 里任何 object 节点缺了它或设成别的值都会被拒绝;但 required 不需要列全该 object 的全部字段——只列出真正必须的字段,其余属性留在 properties 里不进 required 即为可选,允许模型省略。

    把「required 必须列全」当成硬性规则是最容易踩的错,官方 get_weather 示例(unit 未列入 required)就是反例。

  3. 3

    受支持的 JSON Schema 是「阉割版」,不能照抄完整规范

    数值范围(minimum/maximum/multipleOf)、字符串长度(minLength/maxLength)、递归 schema、外部 $refenum 里的复杂类型都不支持,array 只认 minItems 为 0 或 1。

    SDK 会把这些约束从发给模型的 schema 里剥离、转成描述文字,再在响应返回后本地校验——这层隐藏兜底是最容易被忽视的细节。

  4. 4

    强制工具调用(tool_choiceany 或某个具体 tool)会顺带吃掉前置文本说明

    API 会预填 assistant 消息强制走向 tool_use,哪怕 prompt 明确要求先给一句理由,那句理由也不会出现,请求本身不会报错。想要「理由 + 工具调用」就得退回 tool_choice: auto,并在 user 消息里显式要求用该工具。

  5. 5

    旧的「assistant 消息开头填 { 强制 JSON」技巧在当前主力模型(Opus 4.6/4.7/4.8、Sonnet 5、Fable 5)上已经行不通,对最后一轮 assistant 内容做 prefill 直接返回 400,不是静默退化。

    官方给的 schema 约束替代路径是 Structured Outputs,而不是调低 temperature——降 temperature 从未保证过合法 JSON,在这些模型上甚至同样会被拒绝。

  6. 6

    必填参数缺失时不会触发 schema 级别的报错

    那是 API 校验 tool_use.input 用的机制,和「用户没给够信息导致模型该不该追问」是两件事。是否追问还是靠猜,由模型自行判断(Opus 更倾向追问,Sonnet 更倾向猜测),可以用 system prompt 引导,但不是 strict/schema 能控制的范畴。

反模式对照

常见做法

以为 required 数组必须列出 object 里的每一个字段,漏列一个就会导致 400 或校验失败。

正确做法

只把真正必需的字段放进 required,其余属性留在 properties 里但不进 required,即可被模型合法省略;additionalProperties: false 才是那条不可省略的硬性要求。

官方 strict tool use 示例里 get_weatherunit 属性就没有列进 required,schema 依然有效——把「required 列全」当成门槛是常见的过度推广。

常见做法

以为 output_config.formatstrict: true 二选一效果等价,或设了一个就默认另一个也生效。

正确做法

按需求分别决定——约束最终回复文本形状用 output_config.format,约束工具调用参数用 strict: true,两者互不隐含,可独立开启也可同时用。

题目常设「缺一个是否会连带另一个 400」的干扰项,答案是两者完全独立,谁都不依赖谁。

常见做法

在 schema 里直接写 minLength/maximum/递归结构等完整 JSON Schema 语法,以为服务端会照标准校验。

正确做法

只用受支持子集(基础类型、enumconst、内部 $ref、白名单字符串 format、minItems 为 0/1 等),把长度/数值范围之类约束写进描述文字,靠 SDK 或客户端二次校验兜底。

官方文档明确列出不支持清单;这类约束在 SDK 场景会被静默剥离转成描述文字,在手写请求场景则可能不生效,两种结果都容易被误判成「已经生效」。

常见做法

在 Opus 4.8 等新模型上沿用「assistant 消息开头填 { 强制 JSON」的老技巧。

正确做法

迁移到 output_config.format 声明 json_schema,或退而求其次用 system prompt 指令、XML 输出标签、带 enum 的工具调用。

新模型上对最后一轮 assistant 内容做 prefill 直接返回 400,是接口层面拒绝而非行为退化,继续沿用旧技巧会直接报错。

练这个考点