# Claude 结构化输出（Structured Outputs）用法：哪些 schema 会 400

> 用 output_config.format 传 JSON Schema 即可拿到合法 JSON；经网关实测 minimum 报 400，minLength 被接受却不执行。

- URL: https://blog.laozhang.ai/zh/posts/claude-structured-output
- Published: 2026-10-30
- Updated: 2026-10-30
- Author: LaoZhang AI Team (https://blog.laozhang.ai/zh/about)
- Topic: API 接入指南
- Tags: Claude API, 结构化输出, Structured Outputs, JSON Schema, strict tool use

---
想让 Claude 返回程序能直接 `json.loads` 的结果，不要再靠提示词里写"只返回 JSON"，改用结构化输出：在 Messages API 的请求体里加 `output_config.format`，`type` 写 `json_schema`，`schema` 放一份 JSON Schema，Claude 会把符合这份 schema 的 JSON 写进响应 `content` 里的 `text` 块。

截至 2026 年 10 月 30 日，结构化输出是正式功能，不需要 beta 请求头；[Anthropic 结构化输出文档](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)列出的支持模型包括 Opus 5.5、Sonnet 5.5、Haiku 5.5，以及 Sonnet 4.5、Opus 4.5、Haiku 4.5 之后的各个版本，Claude API、Amazon Bedrock、Google Cloud 和 Microsoft Foundry 上都能用。如果要约束的是工具调用参数而不是回复本身，用的是另一个开关：在工具定义上加 `strict: true`。

这项功能保证输出能按 schema 解析，不保证每次都能用。2026 年 10 月 30 日经老张API 用 claude-haiku-5-5 测试：schema 里写 `minimum` 直接返回 400；写 `minLength: 12` 却被接受，要求返回 "ABC" 时照样给了 3 个字符；`max_tokens` 不够时返回 200 和半截 JSON；旧教程里的顶层 `output_format` 也返回 200，但 schema 被悄悄丢掉，回的是普通文字。所以代码里至少要做三件事：先看 `stop_reason`，再用 Pydantic 或 Zod 在本地校验长度、范围这类约束，换了网关或平台后确认输入 token 确实变多了。

## 最小可用请求：把 JSON Schema 放进 output_config.format

直连 Anthropic 官方 API 时，下面这个请求就能拿到结构化结果。schema 里每个 object 都写上 `additionalProperties: false`，需要返回的字段全部放进 `required`，这两处的原因在后面"哪些写法会 400"和"拿到 200 之后"两节里。

```bash
curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "claude-haiku-5-5",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
      }
    ],
    "output_config": {
      "format": {
        "type": "json_schema",
        "schema": {
          "type": "object",
          "properties": {
            "name": {"type": "string"},
            "email": {"type": "string"},
            "plan_interest": {"type": "string"},
            "demo_requested": {"type": "boolean"}
          },
          "required": ["name", "email", "plan_interest", "demo_requested"],
          "additionalProperties": false
        }
      }
    }
  }'
```

2026 年 10 月 30 日，这个请求原样发到老张API（laozhang.ai）的 Anthropic 兼容端点 `https://api.laozhang.ai/v1/messages`，只把地址换掉、`x-api-key` 换成老张API的密钥，返回 HTTP 200，`stop_reason` 是 `end_turn`，耗时 3.61 秒。JSON 在 `content[0].text` 里，是一个字符串，需要你自己解析一次：

*说明：[laozhang.ai 是我们自己的服务](https://blog.laozhang.ai/zh/about#laozhang)，其价格是平台报价，不是独立比较的结论。*

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"demo_requested\": true, \"email\": \"john@example.com\", \"name\": \"John Smith\", \"plan_interest\": \"Enterprise\"}"
    }
  ],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 333, "output_tokens": 46 }
}
```

下文所有"经老张API"的结果都来自这条通道，直连 api.anthropic.com 的行为没有对照。老张API的文档说明，部分 Claude 请求会经过 AWS 官方通道或 Bedrock 相关通道，这些通道对请求体的校验规则可能和 Anthropic 自己的 API 不一样，所以网关上的测试结果只代表网关，和官方文档冲突的地方两边都会写出来。

## Python 用 messages.parse，TypeScript 用 zodOutputFormat

用官方 SDK 时不必手写 JSON Schema：Python 把 Pydantic 模型传给 `client.messages.parse()`，TypeScript 把 Zod schema 包进 `zodOutputFormat()`，SDK 会转换 schema、发出 `output_config.format`、校验返回值，结果放在 `parsed_output` 里。下面的 Python 代码用 anthropic 2.3.0 和 pydantic 2.14 跑过，`Lead` 故意带了几个 API 不支持的约束：

```python
from typing import Literal, Optional

import anthropic
from pydantic import BaseModel, Field


class Lead(BaseModel):
    name: str = Field(min_length=2)
    email: str
    plan: Literal["Starter", "Team", "Enterprise"]
    seats: int = Field(ge=1, le=500)
    phone: Optional[str] = None


client = anthropic.Anthropic()  # 走网关时加 base_url="https://api.laozhang.ai"

resp = client.messages.parse(
    model="claude-haiku-5-5",
    max_tokens=512,
    messages=[{"role": "user", "content": "John Smith (john@example.com) wants the Enterprise plan for 40 seats."}],
    output_format=Lead,
)
print(resp.parsed_output)
# name='John Smith' email='john@example.com' plan='Enterprise' seats=40 phone=None
```

这里的 `output_format` 是 `parse()` 方法的参数名，SDK 实际发出去的是 `output_config.format`；已经弃用的是请求体顶层的同名字段，两者不是一回事。

SDK 发出去之前改了什么，可以用 `anthropic.transform_schema(Lead)` 在本地看，它只转换、不发请求。2026 年 10 月 30 日的输出里，`min_length=2` 从约束变成了字段描述 `{minLength: 2}`，`ge=1, le=500` 变成描述 `{maximum: 500, minimum: 1}`，`Optional[str]` 变成 `anyOf` 字符串或 null 且不在 `required` 里，每个 object 补上了 `additionalProperties: false`。也就是说，Claude 收到的只是描述里的一句提示，真正执行约束的是你本地的 Pydantic。

同一天经老张API发了一条会违反约束的请求，内容写明对方"现在 0 个席位，请照原样报告"。API 正常返回 `seats: 0`，`messages.parse()` 随即抛出 pydantic 的 `ValidationError`："seats Input should be greater than or equal to 1"。所以 `parse()` 要包在 `try/except ValidationError` 里，违反约束时决定重问、转人工还是丢弃。

TypeScript 的写法来自官方文档，`parse()` 同样会校验并返回带类型的 `parsed_output`：

```typescript
import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";

const ContactInfo = z.object({
  name: z.string(),
  email: z.string(),
  plan_interest: z.string(),
  demo_requested: z.boolean(),
});

const client = new Anthropic();

const response = await client.messages.parse({
  model: "claude-haiku-5-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Extract the key information from this email: ..." }],
  output_config: { format: zodOutputFormat(ContactInfo) },
});

console.log(response.parsed_output);
```

如果你手里是现成的 JSON Schema 而不是 Zod，`jsonSchemaOutputFormat()` 只做转换、不校验返回值，校验要自己补。Java SDK 的做法不同：遇到不支持的约束直接在本地报错，而不是挪进描述。

## JSON 输出还是 strict 工具：看结构化数据交给谁

判断标准是这份结构化数据最后交给谁。要把 Claude 的回答本身存库、渲染或交给下游程序，用 JSON 输出（`output_config.format`）；要 Claude 调用你的函数、参数直接进入业务逻辑，用 strict 工具（`strict: true`）；Agent 既要可靠地调工具、最后又要一份结构化结论时，两者可以放在同一个请求里。Claude 没有 OpenAI 那种单独的"JSON mode"开关，早年用强制工具调用模拟 JSON 的做法，现在由 JSON 输出直接替代。

| 对照项 | JSON 输出 | strict 工具 |
| --- | --- | --- |
| 约束的对象 | Claude 的回复正文 | 工具名和工具的 `input` |
| 写在哪里 | 请求体的 `output_config.format` | 每个工具定义顶层的 `"strict": true` |
| 结果在响应的位置 | `text` 块里的 JSON 字符串，要自己解析 | `tool_use` 块的 `input`，已经是对象 |
| 正常的 `stop_reason` | `end_turn` | `tool_use` |
| 适合的任务 | 信息抽取、分类、生成报告 | 预约、写库、下单这类函数调用 |
| 数量限制 | 计入同一请求的 schema 复杂度 | 每个请求最多 20 个 strict 工具 |

两者共用同一套 JSON Schema 限制，下一节的 400 和"不生效"对它们都适用。[Anthropic 的 strict 工具文档](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use)举的例子是：订票函数要求 `passengers` 是整数，不开 strict 时 Claude 可能给出 `"two"` 或 `"2"`，开了以后总是 `2`。

2026 年 10 月 30 日经老张API，用同一封邮件测了 strict 工具：工具 `create_demo_booking` 带 `"strict": true`，`plan` 和 `weekday` 用 enum，`email` 用 `format: email`，`hour_24` 是整数，并用 `tool_choice` 强制调用。返回 `stop_reason: "tool_use"`，`input` 是 `{"email": "john@example.com", "hour_24": 14, "plan": "Enterprise", "weekday": "Tue"}`，"下午 2 点"被换成了整数 14，"下周二"落在了 enum 里的 `Tue`。

```json
{
  "name": "create_demo_booking",
  "description": "Book a product demo for a lead.",
  "strict": true,
  "input_schema": {
    "type": "object",
    "properties": {
      "email": {"type": "string", "format": "email"},
      "plan": {"type": "string", "enum": ["Starter", "Team", "Enterprise"]},
      "weekday": {"type": "string", "enum": ["Mon", "Tue", "Wed", "Thu", "Fri"]},
      "hour_24": {"type": "integer"}
    },
    "required": ["email", "plan", "weekday", "hour_24"],
    "additionalProperties": false
  }
}
```

两者一起用时，JSON 输出的约束只作用于 Claude 最后的回复，不管工具调用、工具结果和思考内容；工具参数由 strict 负责。OpenAI 那边的 `text.format` 和函数工具怎么选，见[Structured Outputs 与 Function Calling：怎么选、怎么验收](https://blog.laozhang.ai/zh/posts/structured-outputs-vs-function-calling)。

## 哪些 schema 写法会 400，哪些被接受却不生效

能不能用，不能只看有没有报错。官方文档说，用了不支持的关键字会收到带细节的 400；但 2026 年 10 月 30 日经老张API测试，`minimum` 和递归 `$ref` 确实返回 400，`minLength`、`maxLength` 和省略 `additionalProperties` 却都返回 200，其中长度约束完全没有执行。下表左边是官方文档的说法，右边是网关上的实际结果，模型都是 claude-haiku-5-5。

| schema 写法 | 官方文档 | 经老张API（2026-10-30） | 你的代码该怎么做 |
| --- | --- | --- | --- |
| `enum`、`format: email`、`anyOf` 加 null | 支持 | 200，结果符合 schema | 直接用 |
| 省略 `additionalProperties` | 必须写 `false` | 200，输出仍是 4 个字段 | 显式写上 `false` |
| 整数上写 `minimum` | 不支持，返回 400 | 400 | 从 schema 删掉，在本地校验 |
| 字符串上写 `minLength`、`maxLength` | 不支持，返回 400 | 200，但要求 12 位时返回了 `"ABC"` | 必须在本地校验 |
| 递归 `$ref`（节点引用自己） | 不支持 | 400 | 按最大层数展开成固定结构 |

两条 400 的原文如下，`type` 都是 `invalid_request_error`。看到 `output_config.format.schema:` 开头的报错，就去 schema 里找它点名的关键字：

```text
output_config.format.schema: For 'integer' type, property 'minimum' is not supported

output_config.format.schema: Invalid schema: Circular reference detected in schema definitions: node -> node. Self-referencing or mutually-referencing definitions are not supported.
```

`minLength` 那一行最危险。schema 写着 `"minLength": 12, "maxLength": 12`，提示词让 Claude 返回代码 ABC，返回的就是 `{"code": "ABC"}`，没有报错，也没有特殊的 `stop_reason`。官方文档说这种写法会被 400 拒绝，网关上却被接受了，直连官方 API 是哪种结果没有对照；无论哪种，长度和数值范围都要在你自己的代码里检查。用 Python 的 `messages.parse()` 或 TypeScript 的 `zodOutputFormat()` 时，SDK 会把这些约束挪进描述并在本地校验，正好补上这一层。

表里没列的几类，官方文档同样列为不支持：`maximum` 和 `multipleOf`，enum 里放对象或数组，外部 URL 形式的 `$ref`，`minItems` 取 0 和 1 以外的值以及其他数组约束。字符串 `format` 只支持 date-time、time、date、duration、email、hostname、uri、ipv4、ipv6、uuid 这十种。`pattern` 正则支持锚点、`* + ?`、简单的 `{n,m}`、字符类和分组，不支持反向引用、前后断言和 `\b`，复杂的正则也可能触发 400。

schema 单个写法都合规，合在一起也可能太复杂。官方文档给的上限按一次请求里所有 strict schema 合计：strict 工具最多 20 个，可选参数（不在 `required` 里的字段）最多 24 个，用 `anyOf` 或 `["string", "null"]` 这类联合类型的参数最多 16 个；超过内部的文法规模会返回 400 "Schema is too complex for compilation"，编译超过 180 秒也会失败。常见的处理是把可选字段改成必填，但如果改成"必填 + 可为 null"，用的就是 `anyOf`，会从可选参数的 24 个额度转到联合类型的 16 个额度，字段多时要两边一起算。

## 返回普通文字或报 Extra inputs are not permitted：先看调用通道

请求成功却拿回一段普通文字，而且 `usage.input_tokens` 和不带 schema 时一样，说明格式参数根本没到模型那里，最常见的原因是代码还在用 beta 时期的写法：请求体顶层的 `output_format`，有时还带着 `anthropic-beta: structured-outputs-2025-11-13`。按官方迁移说明，这个字段已经弃用，直连官方 API 时不带 beta 头会返回 400，Python SDK 1.0 及以上在 `client.beta.messages.create()` 里传 `output_format={...}` 会直接抛 `TypeError`。

经过网关时可能连报错都没有。2026 年 10 月 30 日经老张API，用顶层 `output_format` 发了两次，一次不带 beta 头、一次带，都返回 HTTP 200，回复是一段 Markdown 列表，输入 64 个 token，和完全不带 schema 的请求一模一样；换成 `output_config.format` 后，同一请求是 333 个 token 加合法 JSON。网关丢掉不认识的字段、照常转发，你的程序只会在 `json.loads` 那一步才发现问题。

![同一请求两种写法对照：output_config.format 返回 200、输入 333 token、text 为 JSON；顶层 output_format 返回 200、输入 64 token、text 为 Markdown 列表](https://blog.laozhang.ai/posts/zh/claude-structured-output/img/format-field-check.webp)

另一种情况是直接报 400 `Extra inputs are not permitted`，完整报错常见的样子是 "Api error 400 output_config effort extra inputs are not permitted"。这类报错来自对请求体做严格校验的通道：老张API的文档写明，走 AWS 官方通道或 Bedrock 相关通道时，通道不接受的字段会返回 400 ValidationException 或 `Extra inputs are not permitted`。同一天在老张API上用 Sonnet 5.5 同时发 `output_config.effort` 和 `output_config.format`，返回的是 200，所以会不会报这个错，取决于你的请求最后落在哪条通道。Bedrock 上其他 400 报错原文的对照，见[AWS Claude 报 400 怎么办：按报错原文逐条排查](https://blog.laozhang.ai/zh/posts/aws-claude-400-error)。

换平台、换网关或升级 SDK 之后，按这几项确认格式参数真的生效了：

- 请求体里是 `output_config.format`，不是顶层的 `output_format`，也不需要任何 beta 请求头。
- 模型 ID 在官方支持列表里；Claude 3.x 和 Opus 4.1 不在列表中。
- 对同一个提示词，带 schema 的 `usage.input_tokens` 明显高于不带 schema，4 个字段的 schema 大约高 269 个。
- `text` 块能被 `json.loads` 解析，而不是 Markdown 或自然语言。
- 报 `Extra inputs are not permitted` 时，逐个去掉 `output_config.effort`、beta 请求头这类附加字段，找出通道不接受的那一个。
- 请求里没有同时开启 Citations，两者一起用时官方 API 返回 400。

## 拿到 200 之后还要检查：拒答、截断、null、键顺序和 enum

HTTP 200 加上结构化输出，仍然有五种情况会让你拿到不能直接用的数据，官方文档点名了其中三种，另外两种是 2026 年 10 月 30 日经老张API测出来的。逐项写进代码比事后排查便宜得多。

- `stop_reason` 是 `refusal`：Claude 出于安全原因拒答，状态码仍是 200，生成的 token 照样计费，内容可能不符合 schema。官方文档还提醒，schema 里放一个"逐步推理过程"字段可能触发 `reasoning_extraction` 拒答，要解释就改成"简短说明"。
- `stop_reason` 是 `max_tokens`：输出被截断。经老张API把 `max_tokens` 设成 30、让 Claude 列 15 个选题，返回 200，`text` 是 `{"ideas": ["REST vs GraphQL vs gRPC: How to Choose the`，没法解析。调高 `max_tokens` 重试；走老张API时单次输出上限是 32,768 tokens。
- 可选字段返回 `null`：schema 里 `phone` 是普通字符串、不在 `required` 里，邮件中也没有电话，经老张API返回的是 `"phone": null`，而不是省略这个键。可选字段要按"可能缺失，也可能是 null"处理，Pydantic 里写成 `Optional[str] = None`。
- 键顺序和 schema 不同：官方文档说输出按 schema 顺序、必填字段排在前面；经老张API的每一次 JSON 输出都是按字母排序（`demo_requested`、`email`、`name`……）。任何时候都按键名取值，不要按位置。
- enum 大小写不一致：官方文档说 enum 和 const 的大小写不受保证，`"Conversation topic 3"` 可能回成 `"Conversation Topic 3"`，响应正常结束、没有报错。经老张API连续 5 次都原样返回，但比较时仍然要忽略大小写，也不要设计只差大小写的 enum 值。

![HTTP 200 之后的处理流程：先按 stop_reason 区分 refusal、max_tokens 和 end_turn，再用本地校验区分 ValidationError 与通过](https://blog.laozhang.ai/posts/zh/claude-structured-output/img/after-200-checks.webp)

这些检查可以收进一个函数。下面的写法用 `transform_schema()` 把 Pydantic 模型转成 API 接受的 schema，自己发请求，所以能在解析之前先看 `stop_reason`，最后再用原模型做完整校验：

```python
import logging

import anthropic
from anthropic import transform_schema
from pydantic import ValidationError

log = logging.getLogger(__name__)
client = anthropic.Anthropic()
LEAD_SCHEMA = transform_schema(Lead)  # 上一节定义的 Lead


def extract_lead(text: str, max_tokens: int = 1024) -> Lead | None:
    resp = client.messages.create(
        model="claude-haiku-5-5",
        max_tokens=max_tokens,
        messages=[{"role": "user", "content": text}],
        output_config={"format": {"type": "json_schema", "schema": LEAD_SCHEMA}},
    )
    if resp.stop_reason == "refusal":
        log.warning("refused: %s", resp.id)  # 不要当 JSON 解析，记录后转人工
        return None
    if resp.stop_reason == "max_tokens":
        if max_tokens >= 8192:
            raise RuntimeError("输出太长：拆分任务或精简 schema")
        return extract_lead(text, max_tokens * 2)
    raw = next(b.text for b in resp.content if b.type == "text")
    try:
        return Lead.model_validate_json(raw)  # min_length、ge、le 在这里执行
    except ValidationError as err:
        log.warning("schema ok but constraint failed: %s", err)
        return None
```

预填充 `{` 来逼出 JSON 的老办法不能和结构化输出一起用：同一天在 Haiku 5.5 上把 assistant 消息 `{` 和 `output_config.format` 一起发出，返回 400 "This model does not support assistant message prefill. The conversation must end with a user message."。哪些模型拒收预填充、原来的用途改用什么，见[Claude 预填充（prefill）400 报错：4.6 起模型全拒收，按用途替换](https://blog.laozhang.ai/zh/posts/claude-opus-prefill-error-fix)。

## 结构化输出多花多少 token：4 个字段多 269 个输入 token

开启结构化输出后，Claude 会额外收到一段说明输出格式的系统提示，这段提示和你的 schema 一起按输入 token 计费。2026 年 10 月 30 日经老张API，同一句抽取请求在 Haiku 5.5 上不带 schema 时输入 64 个 token，带上前面那份 4 个字段的 schema 是 333 个，多出 269 个。按截至同日的 [Anthropic 价格页](https://platform.claude.com/docs/en/about-claude/pricing)标准输入价算，每 100 万次请求多出 269 × 1,000,000 = 2.69 亿个 token，也就是 269 MTok：

| 模型 | 输入标价（每 MTok） | 每次请求多付 | 每 100 万次请求多付 |
| --- | --- | --- | --- |
| Haiku 5.5 | $0.10（提示不超过 10 万 token） | $0.0000269 | $26.90 |
| Sonnet 5.5 | $2 | $0.000538 | $538 |
| Opus 5.5 | $4 | $0.001076 | $1,076 |

这组数字只对应一份 4 个字段、没有描述的 schema。字段越多、描述越长，多出来的 token 越多；同一封邮件改用 strict 工具抽取时，输入是 682 个 token，工具定义本身也要计费。输出那边反而可能变少：同一个样本里，普通文字回复是 91 个输出 token，JSON 只有 46 个。以 Sonnet 5.5 每 MTok $10 的输出价算，少的 45 个输出 token 约省 $0.00045，和多付的 $0.000538 输入费差不多抵消，但这取决于你的提示词原本会让 Claude 写多少废话，只能用自己的请求量去算。

还有两处会影响账单。修改 `output_config.format` 会让该对话已有的提示缓存失效，所以同一条对话里不要来回切换 schema。官方文档说结构化输出可以配合批处理接口享受 5 折，但老张API的文档写明不提供 Message Batches 和 `/v1/messages/count_tokens` 接口，要用批处理得直连官方或其他支持批处理的平台。Claude 和 OpenAI 按任务算的整体价格差，见[Claude 与 OpenAI API 价格对比：按任务算谁更省](https://blog.laozhang.ai/zh/posts/claude-api-vs-openai-api-pricing)。

## Claude 结构化输出的常见问题

### 开启思考模式时还能用结构化输出吗？

能。官方文档说明，结构化输出的约束只作用于 Claude 最后的回复，不作用于思考内容，所以思考过程可以自由展开，最终回复仍按 schema 输出。2026 年 10 月 30 日经老张API，在 Sonnet 5.5 上同时设置 `thinking: {"type": "adaptive"}`、`output_config.effort: "low"` 和 `output_config.format`，返回 200，响应里只有一个 `text` 块（低 effort 下没有出现思考块），内容是合法 JSON。

### 流式输出能用结构化输出吗？

能。官方文档列明结构化输出支持流式，按普通流式响应接收即可；完整 JSON 要等流结束后再解析，中途的片段本身不是合法 JSON。

### Amazon Bedrock 和 Google Cloud Vertex AI 上能用 Claude 结构化输出吗？

能。截至 2026 年 10 月 30 日，Anthropic 文档把 Claude API、Claude Platform on AWS、Amazon Bedrock（包括 InvokeModel）、Google Cloud 上的 Claude 和 Microsoft Foundry 都列为正式可用；Claude Managed Agents 不支持。不同平台对请求体的校验不一样，迁移后按前面"调用通道"一节的清单确认一次。

### Claude Code 和 Agent SDK 的 JSON 输出也是 output_config 吗？

不是。Agent SDK 用的是另一套机制：给 `query()` 传 `outputFormat`（TypeScript）或 `output_format`（Python），SDK 校验结果、不符合时让 Claude 重写，结果放在 `structured_output` 里，具体见 [Claude Agent SDK 结构化输出文档](https://code.claude.com/docs/en/agent-sdk/structured-outputs)。

### 第一次用一个新的 schema 会更慢吗？

官方文档说会：每个 schema 第一次使用时要编译成文法，之后缓存 24 小时（从最后一次使用算起），改了 schema 的结构或工具集合才会重新编译，只改 `name` 和 `description` 不会。2026 年 10 月 30 日经老张API发了一份从没用过的 schema，再原样重复两次，耗时分别是 4.23、3.33 和 4.28 秒，经网关时这点差异被网络波动盖住了，看不出来。

### 零数据保留（ZDR）和 HIPAA 场景下，schema 会被保存吗？

会短期保存 schema，但不保存内容。按 Anthropic 文档，开启零数据保留时提示和输出都不存储，只有 JSON schema 会被缓存，最长 24 小时（从最后一次使用算起）。结构化输出符合 HIPAA 要求，但受保护的健康信息不能出现在 schema 的属性名、enum 和 const 值或 `pattern` 正则里，只能放在消息内容中。

## 参考来源

本文引用的外部页面，按正文出现顺序排列。最后更新于 2026-10-30。

- [Anthropic 结构化输出文档](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) (platform.claude.com)
- [Anthropic 的 strict 工具文档](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use) (platform.claude.com)
- [Anthropic 价格页](https://platform.claude.com/docs/en/about-claude/pricing) (platform.claude.com)
- [Claude Agent SDK 结构化输出文档](https://code.claude.com/docs/en/agent-sdk/structured-outputs) (code.claude.com)
