# 结构化输出

从代理工作流中获取经过验证的 JSON 结果

---

结构化输出将 Claude 的响应约束为遵循特定模式，确保为下游处理提供有效、可解析的输出。结构化输出提供两个互补的功能：

- **JSON 输出**（`output_config.format`）：以特定 JSON 格式获取 Claude 的响应
- **严格工具使用**（`strict: true`）：保证对工具名称和输入进行模式验证

您可以在同一请求中独立或组合使用这些功能。

<Note>
结构化输出在 Claude API 上对 [Claude Mythos Preview](https://anthropic.com/glasswing)、Claude Opus 4.7、Claude Opus 4.6、Claude Sonnet 4.6、Claude Sonnet 4.5、Claude Opus 4.5 和 Claude Haiku 4.5 已正式发布。在 Amazon Bedrock 上，结构化输出对 Claude Opus 4.6、Claude Sonnet 4.6、Claude Sonnet 4.5、Claude Opus 4.5 和 Claude Haiku 4.5 已正式发布；Claude Opus 4.7 和 Claude Mythos Preview 可通过 [Claude in Amazon Bedrock](/docs/en/build-with-claude/claude-in-amazon-bedrock)（Messages-API Bedrock 端点）使用。结构化输出在 [Claude Platform on AWS](/docs/en/build-with-claude/claude-platform-on-aws) 上可用，在 [Microsoft Foundry](/docs/en/build-with-claude/claude-in-microsoft-foundry) 上处于测试版。在 [Vertex AI](/docs/en/build-with-claude/claude-on-vertex-ai) 上，结构化输出对 Claude Mythos Preview、Claude Opus 4.7、Claude Opus 4.6 和 Claude Sonnet 4.6 已正式发布。
</Note>

<Note>
此功能符合[零数据保留 (ZDR)](/docs/en/build-with-claude/api-and-data-retention) 条件（有限的技术保留）。有关保留内容和原因的详情，请参阅[数据保留](#data-retention)部分。
</Note>

<Tip>
**从 beta 迁移？** `output_format` 参数已移至 `output_config.format`，beta 头不再需要。旧的 beta 头（`structured-outputs-2025-11-13`）和 `output_format` 参数将在过渡期内继续工作。有关更新后的 API 形式，请参阅以下代码示例。
</Tip>

## 为什么使用结构化输出

没有结构化输出时，Claude 可能会生成格式错误的 JSON 响应或无效的工具输入，从而破坏您的应用程序。即使使用精心设计的提示，您也可能会遇到：
- 无效 JSON 语法导致的解析错误
- 缺少必需字段
- 不一致的数据类型
- 需要错误处理和重试的模式违规

结构化输出通过约束解码保证符合模式的响应：
- **始终有效：** 不再有 `JSON.parse()` 错误
- **类型安全：** 保证字段类型和必需字段
- **可靠：** 无需因模式违规而重试

## JSON 输出

JSON 输出控制 Claude 的响应格式，确保 Claude 返回与您的模式匹配的有效 JSON。在以下情况下使用 JSON 输出：

- 控制 Claude 的响应格式
- 从图像或文本中提取数据
- 生成结构化报告
- 格式化 API 响应

### 快速入门

<CodeGroup>

```bash cURL
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-opus-4-7",
    "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
        }
      }
    }
  }'
```

```bash CLI
ant messages create \
  --transform 'content.0.text|@fromstr' \
  --format jsonl <<'YAML'
model: claude-opus-4-7
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
YAML
```

```python Python hidelines={1..2}
import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-7",
    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,
            },
        }
    },
)
print(response.content[0].text)
```

```typescript TypeScript hidelines={1..2}
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-4-7",
  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
      }
    }
  }
});

for (const block of response.content) {
  if (block.type === "text") {
    console.log(block.text);
  }
}
```

```csharp C#
using System.Text.Json;
using Anthropic;
using Anthropic.Models.Messages;

AnthropicClient client = new();

var parameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus4_7,
    MaxTokens = 1024,
    Messages = [new() { Role = Role.User, Content = "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan." }],
    OutputConfig = new OutputConfig
    {
        Format = new JsonOutputFormat
        {
            Schema = new Dictionary<string, JsonElement>
            {
                ["type"] = JsonSerializer.SerializeToElement("object"),
                ["properties"] = JsonSerializer.SerializeToElement(new
                {
                    name = new { type = "string" },
                    email = new { type = "string" },
                    plan_interest = new { type = "string" },
                    demo_requested = new { type = "boolean" },
                }),
                ["required"] = JsonSerializer.SerializeToElement(new[] { "name", "email", "plan_interest", "demo_requested" }),
                ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
            },
        },
    },
};

var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
```

```go Go hidelines={1..10,-1}
package main

import (
	"context"
	"fmt"

	"github.com/anthropics/anthropic-sdk-go"
)

func main() {
	client := anthropic.NewClient()

	response, _ := client.Messages.New(context.Background(),
		anthropic.MessageNewParams{
			Model:     anthropic.ModelClaudeOpus4_7,
			MaxTokens: 1024,
			Messages: []anthropic.MessageParam{
				anthropic.NewUserMessage(
					anthropic.NewTextBlock("Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan."),
				),
			},
			OutputConfig: anthropic.OutputConfigParam{
				Format: anthropic.JSONOutputFormatParam{
					Schema: map[string]any{
						"type": "object",
						"properties": map[string]any{
							"name":           map[string]string{"type": "string"},
							"email":          map[string]string{"type": "string"},
							"plan_interest":  map[string]string{"type": "string"},
							"demo_requested": map[string]string{"type": "boolean"},
						},
						"required":             []string{"name", "email", "plan_interest", "demo_requested"},
						"additionalProperties": false,
					},
				},
			},
		})

	fmt.Println(response.Content[0].Text)
}
```

```java Java hidelines={1..7}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.StructuredMessage;
import com.anthropic.models.messages.StructuredMessageCreateParams;
import com.anthropic.models.messages.Model;

static class ContactInfo {
    public String name;
    public String email;
    public String plan_interest;
    public boolean demo_requested;
}

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .addUserMessage("Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.")
        .outputConfig(ContactInfo.class)
        .build();

    StructuredMessage<ContactInfo> response = client.messages().create(params);
    ContactInfo contact = response.content().stream()
        .flatMap(block -> block.text().stream())
        .findFirst().orElseThrow().text();
    IO.println(contact.name + " (" + contact.email + ")");
}
```

```php PHP hidelines={1..4}
<?php

use Anthropic\Client;

$client = new Client();

$response = $client->messages->create(
    maxTokens: 1024,
    messages: [
        [
            'role' => 'user',
            'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.'
        ]
    ],
    model: 'claude-opus-4-7',
    outputConfig: [
        '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
            ]
        ]
    ],
);

echo $response->content[0]->text;
```

```ruby Ruby hidelines={1..2}
require "anthropic"

client = Anthropic::Client.new

response = client.messages.create(
  model: "claude-opus-4-7",
  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."
    }
  ],
  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
      }
    }
  }
)

puts response.content[0].text
```

</CodeGroup>

**响应格式：** 在 `response.content[0].text` 中返回与您的模式匹配的有效 JSON

```json 输出
{
  "name": "John Smith",
  "email": "john@example.com",
  "plan_interest": "Enterprise",
  "demo_requested": true
}
```

### 工作原理

<Steps>
  <Step title="定义您的 JSON 模式">
    创建描述您希望 Claude 遵循的结构的 JSON 模式。该模式使用标准 JSON Schema 格式，但有一些限制（请参阅 [JSON Schema 限制](#json-schema-limitations)）。
  </Step>
  <Step title="添加 output_config.format 参数">
    在 API 请求中包含 `output_config.format` 参数，设置 `type: "json_schema"` 和您的模式定义。
  </Step>
  <Step title="解析响应">
    Claude 的响应是与您的模式匹配的有效 JSON，在 `response.content[0].text` 中返回。
  </Step>
</Steps>

### 在 SDK 中使用 JSON 输出

SDK 提供了辅助工具，使使用 JSON 输出更加容易，包括模式转换、自动验证以及与流行模式库的集成。

<Note>
Python SDK 的 `client.messages.parse()` 仍然接受 `output_format` 作为便捷参数，并在内部将其转换为 `output_config.format`。其他 SDK 直接要求 `output_config`。以下示例展示 SDK 辅助语法。
</Note>

#### 使用原生模式定义

您可以使用您熟悉的语言中的模式定义工具，而不是编写原始 JSON 模式：

- **Python：** 使用 `client.messages.parse()` 的 [Pydantic](https://docs.pydantic.dev/) 模型
- **TypeScript：** 使用 `zodOutputFormat()` 的 [Zod](https://zod.dev/) 模式，或使用 `jsonSchemaOutputFormat()` 的类型化 JSON Schema 字面量
- **Java：** 使用 `outputConfig(Class<T>)` 自动模式推导的普通 Java 类
- **Ruby：** 使用 `output_config: {format: Model}` 的 `Anthropic::BaseModel` 类
- **PHP：** 使用 `outputConfig: ['format' => MyClass::class]` 实现 `StructuredOutputModel` 的类
- **CLI**、**C#**、**Go：** 通过 `output_config` 传递原始 JSON 模式

<CodeGroup>

```bash CLI
{ read -r _ NAME; read -r _ EMAIL; } < <(
  ant messages create \
    --transform 'content.0.text|@fromstr|{name,email}' --format yaml <<'YAML'
model: claude-opus-4-7
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
YAML
)
printf '%s (%s)\n' "$NAME" "$EMAIL"
```

```python Python
from pydantic import BaseModel
from anthropic import Anthropic


class ContactInfo(BaseModel):
    name: str
    email: str
    plan_interest: str
    demo_requested: bool


client = Anthropic()

response = client.messages.parse(
    model="claude-opus-4-7",
    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_format=ContactInfo,
)

print(response.parsed_output)
```

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

const ContactInfoSchema = 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-opus-4-7",
  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: zodOutputFormat(ContactInfoSchema) }
});

// Automatically parsed and validated
console.log(response.parsed_output);
```

```csharp C#
using System.Text.Json;
using Anthropic;
using Anthropic.Models.Messages;

var client = new AnthropicClient();

var response = await client.Messages.Create(new MessageCreateParams
{
    Model = Model.ClaudeOpus4_7,
    MaxTokens = 1024,
    Messages = [new() {
        Role = 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."
    }],
    OutputConfig = new OutputConfig
    {
        Format = new JsonOutputFormat
        {
            Schema = new Dictionary<string, JsonElement>
            {
                ["type"] = JsonSerializer.SerializeToElement("object"),
                ["properties"] = JsonSerializer.SerializeToElement(new
                {
                    name = new { type = "string" },
                    email = new { type = "string" },
                    plan_interest = new { type = "string" },
                    demo_requested = new { type = "boolean" },
                }),
                ["required"] = JsonSerializer.SerializeToElement(
                    new[] { "name", "email", "plan_interest", "demo_requested" }),
                ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
            },
        },
    },
});

if (response.Content[0].TryPickText(out var textBlock))
{
    // JSON is guaranteed to match the schema
    var contact = JsonSerializer.Deserialize<Dictionary<string, object>>(textBlock.Text)!;
    Console.WriteLine($"{contact["name"]} ({contact["email"]})");
}
```

```go Go hidelines={1..2,4..7,27..29,-1}
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/anthropics/anthropic-sdk-go"
	"github.com/invopop/jsonschema"
)

type ContactInfo struct {
	Name          string `json:"name" jsonschema:"description=Full name"`
	Email         string `json:"email" jsonschema:"description=Email address"`
	PlanInterest  string `json:"plan_interest" jsonschema:"description=Plan type"`
	DemoRequested bool   `json:"demo_requested" jsonschema:"description=Whether a demo was requested"`
}

func generateSchema(v any) map[string]any {
	r := jsonschema.Reflector{AllowAdditionalProperties: false, DoNotReference: true}
	s := r.Reflect(v)
	b, _ := json.Marshal(s)
	var m map[string]any
	json.Unmarshal(b, &m)
	return m
}

func main() {
	client := anthropic.NewClient()
	schema := generateSchema(&ContactInfo{})

	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeOpus4_7,
		MaxTokens: 1024,
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock(
				"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.",
			)),
		},
		OutputConfig: anthropic.OutputConfigParam{
			Format: anthropic.JSONOutputFormatParam{
				Schema: schema,
			},
		},
	})

	for _, block := range message.Content {
		switch variant := block.AsAny().(type) {
		case anthropic.TextBlock:
			var contact ContactInfo
			json.Unmarshal([]byte(variant.Text), &contact)
			fmt.Printf("%s (%s)\n", contact.Name, contact.Email)
		}
	}
}
```

```java Java hidelines={1..7}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.StructuredMessage;
import com.anthropic.models.messages.StructuredMessageCreateParams;
import com.anthropic.models.messages.Model;

static class ContactInfo {
    public String name;
    public String email;
    public String planInterest;
    public boolean demoRequested;
}

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    StructuredMessageCreateParams<ContactInfo> createParams = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .outputConfig(ContactInfo.class)
        .addUserMessage("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.")
        .build();

    StructuredMessage<ContactInfo> response = client.messages().create(createParams);
    ContactInfo contact = response.content().stream()
        .flatMap(block -> block.text().stream())
        .findFirst().orElseThrow().text();
    IO.println(contact.name + " (" + contact.email + ")");
}
```

```php PHP hidelines={1..3}
<?php

use Anthropic\Client;
use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
use Anthropic\Lib\Contracts\StructuredOutputModel;

$client = new Client();

class ContactInfo implements StructuredOutputModel
{
    use StructuredOutputModelTrait;

    public string $name;
    public string $email;
    public string $plan_interest;
    public bool $demo_requested;
}

$message = $client->messages->create(
    maxTokens: 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.'],
    ],
    model: 'claude-opus-4-7',
    outputConfig: ['format' => ContactInfo::class],
);

$contact = $message->parsedOutput();
if ($contact instanceof ContactInfo) {
    echo "{$contact->name} ({$contact->email})\n";
}
```

```ruby Ruby hidelines={1..2}
require "anthropic"

client = Anthropic::Client.new

class ContactInfo < Anthropic::BaseModel
  required :name, String
  required :email, String
  required :plan_interest, String
  required :demo_requested, Anthropic::Boolean
end

message = client.messages.create(
  model: "claude-opus-4-7",
  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: ContactInfo}
)

contact = message.parsed_output
puts "#{contact.name} (#{contact.email})"
```

</CodeGroup>

#### SDK 特定方法

每个 SDK 都提供了使结构化输出更易于使用的辅助工具。完整详情请参阅各个 SDK 页面。

<Tabs>
<Tab title="CLI">

**通过 heredoc 主体传递原始 JSON 模式**

CLI 通过 YAML heredoc 主体传递原始 JSON 模式。使用 GJSON `@fromstr` 修饰符配合 `--transform` 来解析 `content[0].text` 中返回的 JSON 字符串并投影特定字段。

```bash
ant messages create \
  --transform 'content.0.text|@fromstr|{name,email}' \
  --format yaml <<'YAML'
model: claude-opus-4-7
max_tokens: 1024
messages:
  - role: user
    content: >-
      Extract contact info: John Smith, john@example.com,
      interested in the Pro plan
output_config:
  format:
    type: json_schema
    schema:
      type: object
      properties:
        name: {type: string}
        email: {type: string}
        plan_interest: {type: string}
      required: [name, email, plan_interest]
      additionalProperties: false
YAML
```

```yaml 输出
name: John Smith
email: john@example.com
```

</Tab>
<Tab title="Python">

**`client.messages.parse()`（推荐）**

`parse()` 方法自动转换您的 Pydantic 模型，验证响应，并返回 `parsed_output` 属性。

```python hidelines={2..4,9..12}
from pydantic import BaseModel
import anthropic


class ContactInfo(BaseModel):
    name: str
    email: str
    plan_interest: str


client = anthropic.Anthropic()

response = client.messages.parse(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "Extract contact info: John Smith, john@example.com, interested in the Pro plan",
        }
    ],
    output_format=ContactInfo,
)

# Access the parsed output directly
contact = response.parsed_output
print(contact.name, contact.email)
```

**`transform_schema()` 辅助工具**

当您需要在发送前手动转换模式，或者想要修改 Pydantic 生成的模式时使用。与自动转换提供的模式的 `client.messages.parse()` 不同，此工具为您提供转换后的模式，以便您可以进一步自定义。

```python nocheck
from anthropic import transform_schema
from pydantic import TypeAdapter

# First convert Pydantic model to JSON schema, then transform
schema = TypeAdapter(ContactInfo).json_schema()
schema = transform_schema(schema)
# Modify schema if needed
schema["properties"]["custom_field"] = {"type": "string"}

response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "..."}],
    output_config={
        "format": {"type": "json_schema", "schema": schema},
    },
)
```

</Tab>
<Tab title="TypeScript">

**`client.messages.parse()` 配合 `zodOutputFormat()`**

`parse()` 方法接受 Zod 模式，验证响应，并返回带有与模式匹配的推断 TypeScript 类型的 `parsed_output` 属性。

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

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

const client = new Anthropic();

const response = await client.messages.parse({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
    }
  ],
  output_config: { format: zodOutputFormat(ContactInfo) }
});

// Guaranteed type-safe
console.log(response.parsed_output!.email);
```

**`client.messages.parse()` 配合 `jsonSchemaOutputFormat()`**

`jsonSchemaOutputFormat()` 辅助工具接受 JSON Schema 对象，并将其与 `parse()` 集成，无需 Zod。Zod 是您单独安装的可选对等依赖；`jsonSchemaOutputFormat()` 开箱即用，因为 SDK 直接捆绑了 `json-schema-to-ts`。

对于**内联模式字面量**（在源代码中使用 `as const` 声明），您还可以获得编译时类型推断：`parsed_output` 的类型与模式结构匹配。对于**导入或生成的模式**（来自 JSON 文件或 OpenAPI 代码生成），辅助工具仍然发送模式并解析响应，但推断类型为 `unknown`，因为 `as const` 只能应用于字面量表达式。

```typescript hidelines={1}
import Anthropic from "@anthropic-ai/sdk";
import { jsonSchemaOutputFormat } from "@anthropic-ai/sdk/helpers/json-schema";

const client = new Anthropic();

const response = await client.messages.parse({
  model: "claude-opus-4-7",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
    }
  ],
  output_config: {
    format: jsonSchemaOutputFormat({
      type: "object",
      properties: {
        name: { type: "string" },
        email: { type: "string" },
        planInterest: { type: "string" }
      },
      required: ["name", "email", "planInterest"],
      additionalProperties: false
    } as const)
  }
});

// response.parsed_output is typed as { name: string; email: string; planInterest: string } | null
console.log(response.parsed_output!.email);
```

**类型推断需要 `as const`。** 使用带有 `const` 断言的字面量对象表达式，以便 TypeScript 可以缩小属性类型。没有 `as const`，推断类型会退化为 `unknown`。

**模式转换。** 默认情况下，辅助工具以与 `zodOutputFormat()` 相同的方式转换模式：删除不支持的约束，向对象添加 `additionalProperties: false`，并过滤字符串格式。传递 `jsonSchemaOutputFormat(schema, { transform: false })` 可将您的模式原样发送到 API。请参阅 [SDK 转换工作原理](#sdk-转换工作原理)。

</Tab>
<Tab title="C#">

**通过 `OutputConfig` 传递原始 JSON 模式**

C# SDK 使用通过 `JsonSerializer.SerializeToElement` 以编程方式构建的原始 JSON 模式。使用 `JsonSerializer.Deserialize` 反序列化响应 JSON。

```csharp
using System.Text.Json;
using Anthropic;
using Anthropic.Models.Messages;

var client = new AnthropicClient();

var response = await client.Messages.Create(new MessageCreateParams
{
    Model = Model.ClaudeOpus4_7,
    MaxTokens = 1024,
    Messages = [new() {
        Role = Role.User,
        Content = "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan."
    }],
    OutputConfig = new OutputConfig
    {
        Format = new JsonOutputFormat
        {
            Schema = new Dictionary<string, JsonElement>
            {
                ["type"] = JsonSerializer.SerializeToElement("object"),
                ["properties"] = JsonSerializer.SerializeToElement(new
                {
                    name = new { type = "string" },
                    email = new { type = "string" },
                    plan_interest = new { type = "string" },
                }),
                ["required"] = JsonSerializer.SerializeToElement(
                    new[] { "name", "email", "plan_interest" }),
                ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
            },
        },
    },
});

if (response.Content[0].TryPickText(out var textBlock))
{
    // JSON is guaranteed to match the schema
    var contact = JsonSerializer.Deserialize<Dictionary<string, object>>(textBlock.Text)!;
    Console.WriteLine($"{contact["name"]} ({contact["email"]})");
}
```

</Tab>
<Tab title="Go">

**通过 `OutputConfigParam` 传递原始 JSON 模式**

Go SDK 使用原始 JSON 模式。定义带有 json 标签的 Go 结构体，生成 JSON 模式（例如使用 `invopop/jsonschema`），并将响应文本反序列化为您的结构体。

```go hidelines={1..2,4..7,26..28,-1}
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/anthropics/anthropic-sdk-go"
	"github.com/invopop/jsonschema"
)

type ContactInfo struct {
	Name         string `json:"name" jsonschema:"description=Full name"`
	Email        string `json:"email" jsonschema:"description=Email address"`
	PlanInterest string `json:"plan_interest" jsonschema:"description=Plan type"`
}

func generateSchema(v any) map[string]any {
	r := jsonschema.Reflector{AllowAdditionalProperties: false, DoNotReference: true}
	s := r.Reflect(v)
	b, _ := json.Marshal(s)
	var m map[string]any
	json.Unmarshal(b, &m)
	return m
}

func main() {
	client := anthropic.NewClient()
	schema := generateSchema(&ContactInfo{})

	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeOpus4_7,
		MaxTokens: 1024,
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock(
				"Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.",
			)),
		},
		OutputConfig: anthropic.OutputConfigParam{
			Format: anthropic.JSONOutputFormatParam{
				Schema: schema,
			},
		},
	})

	for _, block := range message.Content {
		switch variant := block.AsAny().(type) {
		case anthropic.TextBlock:
			var contact ContactInfo
			json.Unmarshal([]byte(variant.Text), &contact)
			fmt.Printf("%s (%s)\n", contact.Name, contact.Email)
		}
	}
}
```

</Tab>
<Tab title="Java">

此页面上的 Java 示例使用 [JDK 25 紧凑源文件](https://openjdk.org/jeps/512)语法；有关早期 JDK 的替代方案，请参阅 [Java SDK 要求](/docs/en/api/sdks/java#requirements)。

**`outputConfig(Class<T>)` 方法**

将 Java 类传递给 `outputConfig()`，SDK 自动推导 JSON 模式，验证它，并返回 `StructuredMessageCreateParams<T>`。通过 `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()` 访问解析结果。

<Note>
将您的模式类声明为顶级类或 `static` 嵌套类。此要求来自 Jackson Databind 库（`com.fasterxml.jackson.databind`），SDK 使用它将 JSON 响应反序列化为您的类实例，无法实例化非静态内部类。
</Note>

```java hidelines={1..7}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.StructuredMessage;
import com.anthropic.models.messages.StructuredMessageCreateParams;
import com.anthropic.models.messages.Model;

static class ContactInfo {
    public String name;
    public String email;
    public String planInterest;
}

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    StructuredMessageCreateParams<ContactInfo> createParams = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .outputConfig(ContactInfo.class)
        .addUserMessage("Extract contact info: John Smith, john@example.com, interested in the Pro plan")
        .build();

    StructuredMessage<ContactInfo> response = client.messages().create(createParams);
    ContactInfo contact = response.content().stream()
        .flatMap(block -> block.text().stream())
        .findFirst().orElseThrow().text();
    IO.println(contact.name + " (" + contact.email + ")");
}
```

<section title="泛型类型擦除">

Java 在类的元数据中保留字段的泛型类型信息，但泛型类型擦除适用于其他作用域。虽然可以从具有 `List<Book>` 类型的 `BookList.books` 字段派生 JSON 模式，但无法从相同类型的局部变量派生有效的 JSON 模式。

如果将 JSON 响应转换为 Java 类实例时发生错误，错误消息包含 JSON 响应以协助诊断。如果您的 JSON 响应可能包含敏感信息，请避免直接记录它，或确保在错误消息中编辑掉任何敏感细节。

</section>

<section title="本地模式验证">

结构化输出支持 [JSON Schema 语言的子集](/docs/en/build-with-claude/structured-outputs#json-schema-limitations)。SDK 自动从类生成模式以与此子集对齐。`outputConfig(Class<T>)` 方法对从指定类派生的模式执行验证检查。

要点：

- **本地验证**无需向远程 AI 模型发送请求即可发生。
- **远程验证**也由 AI 模式在收到 JSON 模式时执行。
- **版本兼容性：** 如果 SDK 版本过时，本地验证可能失败而远程验证成功。
- **禁用本地验证：** 如果遇到兼容性问题，请传递 `JsonSchemaLocalValidation.NO`：

```java hidelines={2..4}
import com.anthropic.core.JsonSchemaLocalValidation;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.StructuredMessageCreateParams;
import com.anthropic.models.messages.Model;

static class BookList {
    public List<String> books;
}

void main() {
    StructuredMessageCreateParams<BookList> createParams = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(2048)
        .outputConfig(BookList.class, JsonSchemaLocalValidation.NO)
        .addUserMessage("List some famous late twentieth century novels.")
        .build();
}
```

</section>

<section title="流式传输">

结构化输出也适用于流式传输。当响应以流事件到达时，您需要在反序列化 JSON 之前累积完整响应。

使用 `MessageAccumulator` 从流中收集 JSON 字符串。累积后，调用 `MessageAccumulator.message(Class<T>)` 将累积的 `Message` 转换为 `StructuredMessage`，这会自动将 JSON 反序列化为您的 Java 类。

</section>

<section title="JSON 模式属性">

当 SDK 从您的 Java 类派生 JSON 模式时，默认情况下包含由 `public` 字段或 `public` getter 方法表示的所有属性，并排除非 `public` 字段和 getter 方法。

您可以使用注解控制可见性：

- `@JsonIgnore` 排除 `public` 字段或 getter 方法
- `@JsonProperty` 包含非 `public` 字段或 getter 方法

如果您定义了带有 `public` getter 方法的 `private` 字段，SDK 从 getter 派生属性名（例如，带有 `public` 方法 `getMyValue()` 的 `private` 字段 `myValue` 产生 `"myValue"` 属性）。要使用非常规 getter 名称，请用 `@JsonProperty` 注解该方法。

每个类必须至少为 JSON 模式定义一个属性。如果没有字段或 getter 方法可以产生模式属性，则会发生验证错误，例如：

- 类中没有字段或 getter 方法
- 所有 `public` 成员都用 `@JsonIgnore` 注解
- 所有非 `public` 成员缺少 `@JsonProperty` 注解
- 字段使用 `Map` 类型，这会产生空的 `"properties"` 字段

</section>

<section title="组合和继承">

您的 Java 类可以使用组合和继承在定义 JSON 模式时共享结构。每种模式对输出结构的影响不同。

**组合**产生嵌套 JSON 输出。从组合 `A` 和 `B` 的类 `Composed` 派生模式：

```java hidelines={1..7,20..35}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.StructuredMessage;
import com.anthropic.models.messages.StructuredMessageCreateParams;

static class A {
    public String a;
}

static class B {
    public String b;
}

static class Composed {
    public A composedA;
    public B composedB;
}

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();
    StructuredMessageCreateParams<Composed> params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .outputConfig(Composed.class)
        .addUserMessage("Populate field a with 'hello' and field b with 'world'.")
        .build();
    StructuredMessage<Composed> response = client.messages().create(params);
    Composed result = response.content().stream()
        .flatMap(block -> block.text().stream())
        .findFirst().orElseThrow().text();
    IO.println("composedA.a=" + result.composedA.a);
    IO.println("composedB.b=" + result.composedB.b);
}
```

JSON 输出具有此嵌套结构：

```json
{
  "composedA": { "a": "hello" },
  "composedB": { "b": "world" }
}
```

**继承**产生扁平 JSON 输出。从扩展 `Base` 的类 `Derived` 派生模式：

```java hidelines={1..7,15..30}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.StructuredMessage;
import com.anthropic.models.messages.StructuredMessageCreateParams;

static class Base {
    public String a;
}

static class Derived extends Base {
    public String b;
}

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();
    StructuredMessageCreateParams<Derived> params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .outputConfig(Derived.class)
        .addUserMessage("Populate field a with 'hello' and field b with 'world'.")
        .build();
    StructuredMessage<Derived> response = client.messages().create(params);
    Derived result = response.content().stream()
        .flatMap(block -> block.text().stream())
        .findFirst().orElseThrow().text();
    IO.println("a=" + result.a);
    IO.println("b=" + result.b);
}
```

JSON 输出具有此扁平结构：

```json
{
  "a": "hello",
  "b": "world"
}
```

</section>

<section title="注解（Jackson 和 Swagger）">

您可以使用 Jackson Databind 注解来丰富从 Java 类派生的 JSON 模式：

```java hidelines={-2..}
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;

static class Person {

  @JsonPropertyDescription("The first name and surname of the person")
  public String name;

  public int birthYear;

  @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
  public String deathYear;
}

@JsonClassDescription("The details of one published book")
static class Book {

  public String title;
  public Person author;

  @JsonPropertyDescription("The year in which the book was first published.")
  public int publicationYear;

  @JsonIgnore
  public String genre;
}

static class BookList {
  public List<Book> books;
}

void main() {}
```

注解摘要：

- `@JsonClassDescription`：为类添加描述
- `@JsonPropertyDescription`：为字段或 getter 方法添加描述
- `@JsonIgnore`：从模式中排除 `public` 字段或 getter
- `@JsonProperty`：在模式中包含非 `public` 字段或 getter

如果您使用 `@JsonProperty(required = false)`，SDK 会忽略 `false` 值。类派生的模式始终将所有属性标记为必需。

您还可以使用 Swagger Core (OpenAPI 3) `@Schema` 和 `@ArraySchema` 注解来设置特定类型的约束：

```java hidelines={-2..}
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;

static class Article {

  @ArraySchema(minItems = 1)
  public List<String> authors;

  public String title;

  @Schema(format = "date")
  public String publicationDate;

  public int pageCount;
}

void main() {}
```

本地验证会检查您是否使用了任何不支持的约束关键字，但约束值不会在本地验证。例如，不支持的 `"format"` 值可能通过本地验证但导致远程错误。

如果您同时使用 Jackson 和 Swagger 注解设置相同的模式字段，Jackson 注解优先。

</section>

<section title="不使用 Java 类定义模式">

基于类的模式推导是最方便的路径，但要直接控制模式结构，您可以手动构建 `JsonOutputFormat.Schema` 并将其包装在 `OutputConfig` 中。

```java hidelines={1..2,5..6}
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.JsonOutputFormat;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
import com.anthropic.models.messages.OutputConfig;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    JsonOutputFormat.Schema schema = JsonOutputFormat.Schema.builder()
        .putAdditionalProperty("type", JsonValue.from("object"))
        .putAdditionalProperty("properties", JsonValue.from(Map.of(
            "name", Map.of("type", "string"),
            "email", Map.of("type", "string"),
            "plan_interest", Map.of("type", "string"))))
        .putAdditionalProperty("required", JsonValue.from(
            List.of("name", "email", "plan_interest")))
        .putAdditionalProperty("additionalProperties", JsonValue.from(false))
        .build();

    OutputConfig outputConfig = OutputConfig.builder()
        .format(JsonOutputFormat.builder().schema(schema).build())
        .build();

    MessageCreateParams createParams = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_4_7)
        .maxTokens(1024)
        .outputConfig(outputConfig)
        .addUserMessage(
            "John Smith (john@example.com) is interested in our Enterprise plan.")
        .build();

    client.messages().create(createParams).content().stream()
        .flatMap(contentBlock -> contentBlock.text().stream())
        .forEach(textBlock -> IO.println(textBlock.text()));
}
```

有关构建带有数组和描述的嵌套模式的更广泛示例，请参阅 SDK 存储库中的 [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java)。

</section>

</Tab>
<Tab title="PHP">

**通过 `StructuredOutputModel` 接口使用类**

定义一个实现 `StructuredOutputModel` 的 PHP 类（使用 `StructuredOutputModelTrait`），并将类名传递给 `outputConfig: ['format' => MyClass::class]`。SDK 从您的原生 PHP 8 属性类型派生 JSON 模式，并通过 `$message->parsedOutput()` 返回类型化实例。

`parsedOutput()` 在成功时返回您的模型实例，如果解析失败则返回 `null`（或错误数组）。使用 `instanceof` 在访问字段之前缩小类型。

```php hidelines={1..3}
<?php

use Anthropic\Client;
use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
use Anthropic\Lib\Contracts\StructuredOutputModel;

$client = new Client();

class ContactInfo implements StructuredOutputModel
{
    use StructuredOutputModelTrait;

    public string $name;
    public string $email;
    public string $plan_interest;
}

$message = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.'],
    ],
    model: 'claude-opus-4-7',
    outputConfig: ['format' => ContactInfo::class],
);

$contact = $message->parsedOutput();
if ($contact instanceof ContactInfo) {
    echo "{$contact->name} ({$contact->email})\n";
}
```

<section title="类型推断">

SDK 将原生 PHP 8 属性类型映射到 JSON Schema：

| PHP 类型 | JSON Schema |
|---|---|
| `string` | `"string"` |
| `int` | `"integer"` |
| `float` | `"number"` |
| `bool` | `"boolean"` |
| `array` | `"array"`（见下文说明） |
| `?type`（可空） | 可选字段 |
| 实现 `StructuredOutputModel` 的类 | 嵌套对象 |

对于 `array` 属性，仅当元素类型是嵌套的 `StructuredOutputModel` 时，SDK 才添加 `items` 模式，使用 `#[Constrained(itemClass: MyModel::class)]` 或 `/** @var MyModel[] */` 文档块声明。标量数组（`string[]`、`int[]`）发出无约束的 `{"type":"array"}`。

所有非可空属性都成为必需字段。

</section>

<section title="使用 #[Constrained] 属性添加约束">

使用 `#[Constrained]` 属性添加约束：

```php hidelines={..2} highlight={3}
<?php

use Anthropic\Lib\Attributes\Constrained;
use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
use Anthropic\Lib\Contracts\StructuredOutputModel;

class Address implements StructuredOutputModel { use StructuredOutputModelTrait; public string $street; }

class Profile implements StructuredOutputModel
{
    use StructuredOutputModelTrait;

    #[Constrained(description: 'Age in years', minimum: 0, maximum: 150)]
    public int $age;

    #[Constrained(format: 'email')]
    public string $email;

    #[Constrained(itemClass: Address::class, minItems: 1)]
    public array $addresses;
}
```

**API 强制约束**（在模式中发送）：`description`、`format`、`const`、`itemClass`、`minItems`（仅 0 或 1）。

**SDK 验证约束**（从线上模式中剥离，附加到描述中，并针对响应验证）：`minimum`、`maximum`、`multipleOf`、`minLength`、`maxLength`。

</section>

<section title="原始 JSON 模式回退">

对于 PHP 类型提示无法表达的模式，通过 `OutputConfig::with()` 传递原始关联数组。此路径跳过 `parsedOutput()` 辅助工具；使用 `json_decode()` 解码响应：

```php hidelines={1..3}
<?php

use Anthropic\Client;
use Anthropic\Messages\OutputConfig;
use Anthropic\Messages\JSONOutputFormat;

$client = new Client();

$message = $client->messages->create(
    maxTokens: 1024,
    messages: [
        ['role' => 'user', 'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.'],
    ],
    model: 'claude-opus-4-7',
    outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [
        'type' => 'object',
        'properties' => [
            'name' => ['type' => 'string'],
            'email' => ['type' => 'string'],
            'plan_interest' => ['type' => 'string'],
        ],
        'required' => ['name', 'email', 'plan_interest'],
        'additionalProperties' => false,
    ])),
);

$contact = json_decode($message->content[0]->text, associative: true);
echo "{$contact['name']} ({$contact['email']})\n";
```

</section>

</Tab>
<Tab title="Ruby">

**`output_config: {format: Model}` 配合 `parsed_output`**

定义一个扩展 `Anthropic::BaseModel` 的模型类，并将其作为格式传递给 `messages.create()`。响应包含一个带有类型化 Ruby 对象的 `parsed_output` 属性。

```ruby hidelines={1..2}
require "anthropic"

class ContactInfo < Anthropic::BaseModel
  required :name, String
  required :email, String
  required :plan_interest, String
end

client = Anthropic::Client.new

message = client.messages.create(
  model: "claude-opus-4-7",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
    }
  ],
  output_config: {format: ContactInfo}
)

contact = message.parsed_output
puts "#{contact.name} (#{contact.email})"
```

<section title="高级模型功能">

Ruby SDK 支持额外的模型定义功能以实现更丰富的模式：

- **`doc:` 关键字：** 为字段添加描述以获得更具信息性的模式输出
- **`Anthropic::ArrayOf[T]`：** 类型化数组。将数组级约束（`min_items:`、`max_items:`）作为 `required`/`optional` 上的关键字传递，而不是在 `ArrayOf` 本身上
- **`Anthropic::EnumOf[:a, :b]`：** 具有约束值的枚举字段
- **`Anthropic::UnionOf[T1, T2]`：** 映射到 `anyOf` 的联合类型

```ruby
class FamousNumber < Anthropic::BaseModel
  required :value, Float
  optional :reason, String, doc: "why is this number mathematically significant?"
end

class Output < Anthropic::BaseModel
  required :numbers, Anthropic::ArrayOf[FamousNumber], min_items: 3, max_items: 5
end

message = client.messages.create(
  model: "claude-opus-4-7",
  max_tokens: 1024,
  messages: [{role: "user", content: "give me some famous numbers"}],
  output_config: {format: Output}
)

message.parsed_output
# => #<Output numbers=[#<FamousNumber value=3.14159... reason="Pi is...">...]>
```

</section>

</Tab>
</Tabs>

#### SDK 转换工作原理

Python、TypeScript、Ruby 和 PHP SDK 自动转换具有不支持功能的模式：

1. **删除不支持的约束**（例如 `minimum`、`maximum`、`minLength`、`maxLength`）
2. **更新描述**，当约束不被结构化输出直接支持时添加约束信息（例如 "Must be at least 100"）
3. **向所有对象添加 `additionalProperties: false`**
4. **过滤字符串格式**仅保留支持的列表
5. **根据您的原始模式验证响应**（包含所有约束）

这意味着 Claude 接收简化的模式，但您的代码仍然通过验证强制执行所有约束。

**示例：** 具有 `minimum: 100` 的 Pydantic 字段在发送的模式中变为普通整数，但 SDK 将描述更新为 "Must be at least 100" 并根据原始约束验证响应。

### 常见用例

<section title="数据提取">

从非结构化文本中提取结构化数据。所有代码示例保持原样。

</section>

<section title="分类">

使用结构化类别对内容进行分类。

</section>

<section title="API 响应格式化">

生成 API 就绪的响应。

</section>

## 严格工具使用

有关使用语法约束采样对工具输入强制执行 JSON Schema 合规性，请参阅[严格工具使用](/docs/en/agents-and-tools/tool-use/strict-tool-use)。

## 同时使用两个功能

JSON 输出和严格工具使用解决不同的问题并协同工作：

- **JSON 输出**控制 Claude 的响应格式（Claude 说什么）
- **严格工具使用**验证工具参数（Claude 如何调用您的函数）

组合使用时，Claude 可以使用保证有效的参数调用工具并返回结构化 JSON 响应。这对于同时需要可靠工具调用和结构化最终输出的代理工作流非常有用。

## 重要注意事项

### 语法编译和缓存

结构化输出使用带有编译语法制品的约束采样。这引入了一些需要注意的性能特征：

- **首次请求延迟：** 第一次使用特定模式时，在语法编译期间会有额外延迟
- **自动缓存：** 编译的语法从上次使用起缓存 24 小时，使后续请求更快
- **缓存失效：** 如果您更改以下内容，缓存将失效：
  - JSON 模式结构
  - 请求中的工具集（当同时使用结构化输出和工具使用时）
  - 仅更改 `name` 或 `description` 字段不会使缓存失效

### 提示修改和 token 成本

使用结构化输出时，Claude 会自动接收额外的系统提示，解释预期的输出格式。这意味着：

- 您的输入 token 计数略高
- 注入的提示像任何其他系统提示一样消耗您的 token
- 更改 `output_config.format` 参数会使该对话线程的任何[提示缓存](/docs/en/build-with-claude/prompt-caching)失效

### JSON Schema 限制

结构化输出支持标准 JSON Schema，但有一些限制。JSON 输出和严格工具使用共享这些限制。

<section title="支持的功能">

- 所有基本类型：object、array、string、integer、number、boolean、null
- `enum`（仅支持字符串、数字、布尔值或 null - 不支持复杂类型）
- `const`
- `anyOf` 和 `allOf`（有限制 - 不支持带 `$ref` 的 `allOf`）
- `$ref`、`$def` 和 `definitions`（不支持外部 `$ref`）
- 所有支持类型的 `default` 属性
- `required` 和 `additionalProperties`（对象必须设置为 `false`）
- 字符串格式：`date-time`、`time`、`date`、`duration`、`email`、`hostname`、`uri`、`ipv4`、`ipv6`、`uuid`
- 数组 `minItems`（仅支持值 0 和 1）

</section>

<section title="不支持">

- 递归模式
- 枚举中的复杂类型
- 外部 `$ref`（例如 `'$ref': 'http://...'`）
- 数值约束（`minimum`、`maximum`、`multipleOf` 等）
- 字符串约束（`minLength`、`maxLength`）
- 超出 `minItems` 为 0 或 1 的数组约束
- `additionalProperties` 设置为 `false` 以外的任何值

如果使用不支持的功能，您将收到包含详细信息的 400 错误。

</section>

<section title="模式支持（正则表达式）">

**支持的正则表达式功能：**
- 完全匹配（`^...$`）和部分匹配
- 量词：`*`、`+`、`?`、简单的 `{n,m}` 情况
- 字符类：`[]`、`.`、`\d`、`\w`、`\s`
- 分组：`(...)`

**不支持：**
- 对分组的反向引用（例如 `\1`、`\2`）
- 前瞻/后顾断言（例如 `(?=...)`、`(?!...)`）
- 单词边界：`\b`、`\B`
- 具有大范围的复杂 `{n,m}` 量词

简单的正则表达式模式效果良好。复杂模式可能导致 400 错误。

</section>

<Tip>
Python、TypeScript、Ruby 和 PHP SDK 可以通过删除不支持的功能并将约束添加到字段描述来自动转换模式。详情请参阅 [SDK 特定方法](#sdk-特定方法)。
</Tip>

### 属性排序

使用结构化输出时，对象中的属性保持您模式中定义的顺序，但有一个重要注意事项：**必需属性出现在前面，可选属性跟在后面**。

例如，给定此模式：

```json
{
  "type": "object",
  "properties": {
    "notes": { "type": "string" },
    "name": { "type": "string" },
    "email": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}
```

输出将按以下顺序排列属性：

1. `name`（必需，按模式顺序）
2. `email`（必需，按模式顺序）
3. `notes`（可选，按模式顺序）
4. `age`（可选，按模式顺序）

这意味着输出可能如下所示：

```json
{
  "name": "John Smith",
  "email": "john@example.com",
  "notes": "Interested in enterprise plan",
  "age": 35
}
```

如果输出中的属性顺序对您的应用程序很重要，请将所有属性标记为必需，或在解析逻辑中考虑这种重新排序。

### 无效输出

虽然结构化输出在大多数情况下保证模式合规性，但在某些场景下输出可能与您的模式不匹配：

**拒绝**（`stop_reason: "refusal"`）

即使使用结构化输出，Claude 也保持其安全性和有用性属性。如果 Claude 因安全原因拒绝请求：

- 响应具有 `stop_reason: "refusal"`
- 您将收到 200 状态码
- 您将被收取生成的 token 费用
- 输出可能与您的模式不匹配，因为拒绝消息优先于模式约束

**达到 token 限制**（`stop_reason: "max_tokens"`）

如果响应因达到 `max_tokens` 限制而被截断：

- 响应具有 `stop_reason: "max_tokens"`
- 输出可能不完整且与您的模式不匹配
- 使用更高的 `max_tokens` 值重试以获取完整的结构化输出

### 模式复杂性限制

结构化输出通过将您的 JSON 模式编译为约束 Claude 输出的语法来工作。更复杂的模式产生更大的语法，编译时间更长。为了防止过度的编译时间，API 强制执行几个复杂性限制。

#### 明确限制

以下限制适用于所有具有 `output_config.format` 或 `strict: true` 的请求：

| 限制 | 值 | 描述 |
|-------|-------|-------------|
| 每个请求的严格工具 | 20 | 具有 `strict: true` 的工具的最大数量。非严格工具不计入此限制。 |
| 可选参数 | 24 | 所有严格工具模式和 JSON 输出模式中的可选参数总数。未在 `required` 中列出的每个参数都计入此限制。 |
| 具有联合类型的参数 | 16 | 在所有严格模式中使用 `anyOf` 或类型数组（例如 `"type": ["string", "null"]`）的参数总数。这些特别昂贵，因为它们会产生指数级的编译成本。 |

<Note>
这些限制适用于单个请求中所有严格模式的合计总数。例如，如果您有 4 个严格工具，每个有 6 个可选参数，即使没有单个工具看起来很复杂，您也会达到 24 个参数的限制。
</Note>

#### 额外的内部限制

除了上表中的明确限制外，编译语法大小还有额外的内部限制。这些限制存在是因为模式复杂性不会简化为单一维度：可选参数、联合类型、嵌套对象和工具数量等功能以可能使编译语法不成比例地大的方式相互作用。

当超过这些限制时，您将收到 400 错误，消息为 "Schema is too complex for compilation." 这些错误意味着您的模式的综合复杂性超过了可以有效编译的范围，即使上表中的每个单独限制都满足。作为最后的防线，API 还强制执行 **180 秒的编译超时**。通过所有明确检查但产生非常大编译语法的模式可能会遇到此超时。

#### 降低模式复杂性的技巧

如果遇到复杂性限制，请按顺序尝试以下策略：

1. **仅将关键工具标记为严格。** 如果您有许多工具，请将其保留给模式违规会导致实际问题的工具，并依赖 Claude 的自然遵守来处理更简单的工具。

2. **减少可选参数。** 尽可能将参数设为 `required`。每个可选参数大致使语法状态空间的一部分翻倍。如果参数始终具有合理的默认值，请考虑将其设为必需，并让 Claude 显式提供该默认值。

3. **简化嵌套结构。** 具有可选字段的深度嵌套对象会复合复杂性。尽可能扁平化结构。

4. **拆分为多个请求。** 如果您有许多严格工具，请考虑将它们拆分到单独的请求或子代理中。

有关有效模式的持续问题，请使用您的模式定义[联系支持](https://support.claude.com/en/articles/9015913-how-to-get-support)。

## 数据保留

使用结构化输出时，提示和响应使用 ZDR 处理。但是，JSON 模式本身会出于优化目的临时缓存最多 24 小时。除了 API 响应之外，不会保留任何提示或响应数据。

结构化输出符合 HIPAA 条件，但 **PHI 不得包含在 JSON 模式定义中**。API 将 JSON 模式编译为与消息内容分开缓存的语法，这些缓存的模式不会获得与提示和响应相同的 PHI 保护。不要在模式属性名、`enum` 值、`const` 值或 `pattern` 正则表达式中包含 PHI。PHI 只应出现在消息内容（提示和响应）中，在那里它受到 HIPAA 保护措施的保护。

有关所有功能的 ZDR 和 HIPAA 资格，请参阅 [API 和数据保留](/docs/en/manage-claude/api-and-data-retention)。

## 功能兼容性

**兼容：**
- **[批处理](/docs/en/build-with-claude/batch-processing)：** 以 50% 折扣大规模处理结构化输出
- **[Token 计数](/docs/en/build-with-claude/token-counting)：** 无需编译即可计算 token
- **[流式传输](/docs/en/build-with-claude/streaming)：** 像普通响应一样流式传输结构化输出
- **组合使用：** 在同一请求中同时使用 JSON 输出（`output_config.format`）和严格工具使用（`strict: true`）

**不兼容：**
- **[引用](/docs/en/build-with-claude/citations)：** 引用需要将引用块与文本交错，这与严格的 JSON 模式约束冲突。如果在启用 `output_config.format` 的情况下启用引用，将返回 400 错误。
- **消息预填充：** 与 JSON 输出不兼容

<Tip>
**语法范围：** 语法仅适用于 Claude 的直接输出，不适用于工具使用调用、工具结果或思考标签（使用[扩展思考](/docs/en/build-with-claude/extended-thinking)时）。语法状态在各部分之间重置，允许 Claude 自由思考，同时仍在最终响应中产生结构化输出。
</Tip>
