---
title: 結構化輸出
url: https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs
description: 從代理工作流程取得經過驗證的 JSON 結果
featureMetadata:
  status: ga
  zdr:
    eligibility: eligible
    note: Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements).
  supportedModels:
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-sonnet-4-6
    - claude-sonnet-4-5-20250929
    - claude-opus-4-5-20251101
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
  supportedPlatforms:
    Claude API: ga
    Claude Platform on AWS: ga
    Amazon Bedrock:
      availability: ga
      note: 在 Amazon Bedrock 上，結構化輸出可透過舊版 [Amazon Bedrock（Opus 4.6 及更早版本）](https://platform.claude.com/docs/zh-TW/build-with-claude/claude-on-amazon-bedrock-legacy) 整合用於 Claude Opus 4.6、Claude Sonnet 4.6、Claude Sonnet 4.5、Claude Opus 4.5 和 Claude Haiku 4.5，但不適用於 [Claude in Amazon Bedrock](https://platform.claude.com/docs/zh-TW/build-with-claude/claude-in-amazon-bedrock)。
    Google Cloud: ga
    Microsoft Foundry: ga
---

「Structured outputs」（結構化輸出）會限制 Claude 的回應遵循特定的 schema（結構描述），確保輸出有效且可解析，以供下游處理。結構化輸出提供兩項互補的功能：

* **JSON 輸出**（`output_config.format`）：以特定 JSON 格式取得 Claude 的回應，例如從圖片或文字中擷取資料、產生結構化報告，或格式化 API 回應。本頁介紹 JSON 輸出。
* **嚴格工具使用**（`strict: true`）：保證對工具名稱和輸入進行 schema 驗證。請參閱[嚴格工具使用](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/strict-tool-use)。

您可以獨立使用這些功能，也可以[在同一個請求中一起使用](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#using-both-features-together)。

## 為何使用結構化輸出

若沒有結構化輸出，Claude 可能會產生格式錯誤的 JSON 回應或無效的工具輸入，進而破壞您的應用程式。即使經過仔細的提示設計，您仍可能遇到：

* 因無效 JSON 語法導致的解析錯誤
* 缺少必要欄位
* 資料型別不一致
* 需要錯誤處理和重試的 schema 違規

結構化輸出透過「constrained decoding」（受限解碼）保證回應符合 schema：

* **永遠有效：** 不再有 `JSON.parse()` 錯誤
* **型別安全：** 保證欄位型別和必要欄位
* **可靠：** 不需要因 schema 違規而重試

## 運作方式

<Steps>
  <Step title="定義您的 schema">
    以 JSON schema 或您所用語言中的型別來描述您想要的結構。該 schema 遵循 JSON Schema，但有一些[限制](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)。
  </Step>

  <Step title="在 output_config.format 中傳送">
    請求會在 `output_config.format` 中攜帶 schema，並設定 `type: "json_schema"`。SDK 輔助工具會為您設定這些內容。
  </Step>

  <Step title="讀取回應">
    Claude 會在回應的文字內容區塊中傳回符合您 schema 的有效 JSON。SDK 輔助工具會將其解析為您的型別。
  </Step>
</Steps>

## 使用方式

<Tabs>
  <Tab title="cURL">
    ```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-opus-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
            }
          }
        }
      }'
    ```

    Claude 會在回應的文字內容區塊中傳回如下的 JSON：

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-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
    YAML
    ```

    若只想印出 JSON 輸出的部分屬性，請將 [GJSON 路徑](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/cli/using#transform-output-with-gjson)傳給 `--transform`。

    上述範例的輸出為：

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    from pydantic import BaseModel
    # ...


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


    client = Anthropic()

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

    print(response.parsed_output)
    ```

    將 [Pydantic](https://docs.pydantic.dev/) 模型作為 `output_format` 傳給建議使用的方法 `client.messages.parse()`。SDK 會轉換模型的 schema、將其作為 `output_config.format` 傳送、驗證回應，並在 `parsed_output` 中傳回解析後的模型。

    上述範例的輸出為：

    ```text Output wrap
    name='John Smith' email='john@example.com' plan_interest='Enterprise' demo_requested=True
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    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-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: zodOutputFormat(ContactInfoSchema) }
    });

    // 自動剖析並驗證
    console.log(response.parsed_output);
    ```

    將 [Zod](https://zod.dev/) schema 包裝在 `zodOutputFormat()` 中，並將其作為 `output_config.format` 傳給 `client.messages.parse()`。`zodOutputFormat()` 會轉換 schema，而 `parse()` 會驗證回應，並在 `parsed_output` 中傳回解析結果，其型別與 schema 相符。

    上述範例的輸出為：

    ```javascript Output
    {
      name: 'John Smith',
      email: 'john@example.com',
      plan_interest: 'Enterprise',
      demo_requested: true
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using Anthropic;
    using Anthropic.Models.Messages;
    using Anthropic.Services;

    var client = new AnthropicClient();

    var response = await client.Messages.Create<ContactInfo>(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        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."
        }],
    });

    if (response.Content.Select(block => block.Parsed()).OfType<ContactInfo>().FirstOrDefault() is { } contact)
    {
        Console.WriteLine(contact);
    }

    public record ContactInfo
    {
        public string Name { get; set; } = "";
        public string Email { get; set; } = "";
        public string PlanInterest { get; set; } = "";
        public bool DemoRequested { get; set; }
    }
    ```

    將一般的 C# 類別或 record 傳給泛型 `Create<T>()` 多載。SDK 會從該型別衍生 JSON schema、進行轉換，並將每個文字區塊解析回該型別。

    上述範例的輸出為：

    ```text Output wrap
    ContactInfo { Name = John Smith, Email = john@example.com, PlanInterest = Enterprise, DemoRequested = True }
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

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

    	var contact ContactInfo
    	_, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.BetaMessageParam{
    			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
    				"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.BetaOutputConfigParam{
    			Format: anthropic.BetaJSONOutputFormatParam{Schema: &contact},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}

    	fmt.Printf("%#v\n", contact)
    }
    ```

    <Note>
      在 Go 中，將 struct 作為 schema 傳遞仍處於 beta 階段，因此本範例使用 `client.Beta.Messages.New`。若要使用 GA 版的 `client.Messages.New`，請依照[使用原始 JSON schema](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#using-a-raw-json-schema) 中的說明傳遞 JSON schema。
    </Note>

    SDK 會從 struct 產生並轉換 JSON schema，並將回應反序列化（unmarshal）至該 struct。

    上述範例的輸出為：

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```
  </Tab>

  <Tab title="Java">
    ```java
    static class ContactInfo {
        public String name;
        public String email;
        public String plan_interest;
        public boolean demo_requested;
    }

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

        StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .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.")
            .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(new ObjectMapper().writeValueAsString(contact));
    }
    ```

    當您將 Java 類別傳給 `outputConfig()` 時，SDK 會從中衍生 JSON schema 並驗證該 schema。接著 builder 會產生 `StructuredMessageCreateParams<T>`。SDK 不會轉換 schema，而是透過本機驗證拒絕[API 不支援的限制條件](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)。使用 `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()` 讀取解析結果。

    上述範例的輸出為：

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```

    <Note>
      請將您的 schema 類別宣告為頂層類別或 `static` 巢狀類別。此要求來自 Jackson Databind 函式庫（`com.fasterxml.jackson.databind`），SDK 使用它將 JSON 回應反序列化為您的類別實例，而它無法實例化非 static 的內部類別。
    </Note>

    <Accordion title="泛型型別抹除">
      Java 會在類別的中繼資料中保留欄位的泛型型別資訊，但在其他範圍中會套用泛型型別抹除。雖然可以從型別為 `List<Book>` 的 `BookList.books` 欄位推導出 JSON schema，但無法從相同型別的區域變數推導出有效的 JSON schema。

      若在將 JSON 回應轉換為 Java 類別實例時發生錯誤，錯誤訊息會包含 JSON 回應以協助診斷。若您的 JSON 回應可能包含敏感資訊，請避免直接記錄，或確保您已從錯誤訊息中遮蔽任何敏感細節。
    </Accordion>

    <Accordion title="本機 schema 驗證">
      結構化輸出支援 [JSON Schema 語言的子集](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)。SDK 會從類別自動產生 schema 以符合此子集。`outputConfig(Class<T>)` 方法會對從指定類別推導出的 schema 執行驗證檢查。

      重點：

      * **本機驗證**在不向遠端 AI 模型傳送請求的情況下進行。
      * **遠端驗證**也會由 AI 模型在收到 JSON schema 時執行。
      * **版本相容性：** 若 SDK 版本過舊，本機驗證可能失敗而遠端驗證成功。
      * **停用本機驗證：** 若遇到相容性問題，請傳入 `JsonSchemaLocalValidation.NO`：

      ```java
      import com.anthropic.core.JsonSchemaLocalValidation;
      // ...

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

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

    <Accordion title="串流">
      結構化輸出也適用於串流。由於回應以串流事件的形式抵達，您需要先累積完整回應，再反序列化 JSON。

      使用 `MessageAccumulator` 從串流中收集 JSON 字串。累積完成後，呼叫 `MessageAccumulator.message(Class<T>)` 將累積的 `Message` 轉換為 `StructuredMessage`，它會自動將 JSON 反序列化為您的 Java 類別。
    </Accordion>

    <Accordion title="JSON schema 屬性">
      當 SDK 從您的 Java 類別推導 JSON schema 時，預設會包含所有由 `public` 欄位或 `public` getter 方法所代表的屬性，並排除非 `public` 的欄位和 getter 方法。

      您可以使用註解控制可見性：

      * `@JsonIgnore` 排除 `public` 欄位或 getter 方法
      * `@JsonProperty` 包含非 `public` 欄位或 getter 方法

      若您定義 `private` 欄位並搭配 `public` getter 方法，SDK 會從 getter 推導屬性名稱（例如，`private` 欄位 `myValue` 搭配 `public` 方法 `getMyValue()` 會產生 `"myValue"` 屬性）。若要使用非慣例的 getter 名稱，請以 `@JsonProperty` 註解該方法。

      每個類別必須為 JSON schema 定義至少一個屬性。若沒有欄位或 getter 方法能產生 schema 屬性，就會發生驗證錯誤，例如：

      * 類別中沒有欄位或 getter 方法
      * 所有 `public` 成員都以 `@JsonIgnore` 註解
      * 所有非 `public` 成員都缺少 `@JsonProperty` 註解
      * 欄位使用 `Map` 型別，這會產生空的 `"properties"` 欄位
    </Accordion>

    <Accordion title="組合與繼承">
      您的 Java 類別在定義 JSON schema 時可以使用組合與繼承來共用結構。每種模式對輸出結構的影響不同。

      **組合**會產生巢狀 JSON 輸出。從組合了 `A` 和 `B` 的類別 `Composed` 推導 schema：

      ```java
      static class A {
          public String a;
      }

      static class B {
          public String b;
      }

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

      JSON 輸出具有以下巢狀結構：

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

      **繼承**會產生扁平的 JSON 輸出。從繼承 `Base` 的類別 `Derived` 推導 schema：

      ```java
      static class Base {
          public String a;
      }

      static class Derived extends Base {
          public String b;
      }
      ```

      JSON 輸出具有以下扁平結構：

      ```json
      {
        "a": "hello",
        "b": "world"
      }
      ```
    </Accordion>

    <Accordion title="註解（Jackson 與 Swagger）">
      您可以使用 Jackson Databind 註解來豐富從 Java 類別推導出的 JSON schema：

      ```java
      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;
      }
      ```

      註解摘要：

      * `@JsonClassDescription`：為類別加入描述
      * `@JsonPropertyDescription`：為欄位或 getter 方法加入描述
      * `@JsonIgnore`：從 schema 中排除 `public` 欄位或 getter
      * `@JsonProperty`：在 schema 中包含非 `public` 欄位或 getter

      若您使用 `@JsonProperty(required = false)`，SDK 會忽略 `false` 值。從類別推導的 schema 一律將所有屬性標記為必要。

      您也可以使用 Swagger Core（OpenAPI 3）的 `@Schema` 和 `@ArraySchema` 註解來設定型別專屬的限制條件：

      ```java
      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;
      }
      ```

      本機驗證會檢查您是否使用了任何不支援的限制條件關鍵字，但限制條件的值不會在本機驗證。例如，不支援的 `"format"` 值可能通過本機驗證，但會導致遠端錯誤。

      若您同時使用 Jackson 和 Swagger 註解設定相同的 schema 欄位，Jackson 註解優先。
    </Accordion>
  </Tab>

  <Tab title="PHP">
    ```php
    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-5-5',
        outputConfig: ['format' => ContactInfo::class],
    );

    $contact = $message->parsedOutput();
    if ($contact instanceof ContactInfo) {
        var_dump($contact);
    }
    ```

    定義一個實作 `StructuredOutputModel` 並使用 `StructuredOutputModelTrait` 的類別，並在 `outputConfig: ['format' => MyClass::class]` 中傳入其名稱。SDK 會從該類別的 PHP 8 屬性型別衍生 JSON schema、進行轉換，並從 `$message->parsedOutput()` 傳回具型別的實例。

    `parsedOutput()` 在成功時回傳您的模型實例，若解析失敗則回傳 `null`（或錯誤陣列）。在存取欄位前，請使用 `instanceof` 縮小型別範圍。

    上述範例的輸出為：

    ```text Output wrap
    object(ContactInfo)#42 (4) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
      ["demo_requested"]=>
      bool(true)
    }
    ```

    <Accordion title="型別推斷">
      SDK 將原生 PHP 8 屬性型別對應至 JSON Schema：

      | PHP 型別                         | JSON Schema        |
      | ------------------------------ | ------------------ |
      | `string`                       | `"string"`         |
      | `int`                          | `"integer"`        |
      | `float`                        | `"number"`         |
      | `bool`                         | `"boolean"`        |
      | `array`                        | `"array"`（請參閱以下說明） |
      | `?type`（可為 null）               | 選用欄位               |
      | 實作 `StructuredOutputModel` 的類別 | 巢狀物件               |

      對於 `array` 屬性，SDK 僅在元素型別為巢狀 `StructuredOutputModel` 時才會加入 `items` schema，可透過 `#[Constrained(itemClass: MyModel::class)]` 或 `/** @var MyModel[] */` docblock 宣告。純量陣列（`string[]`、`int[]`）會產生不受限制的 `{"type":"array"}`。

      所有不可為 null 的屬性都會成為必要欄位。
    </Accordion>

    <Accordion title="使用 #[Constrained] 屬性設定限制條件">
      使用 `#[Constrained]` 屬性加入限制條件：

      ```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 強制執行的限制條件**（在 schema 中傳送）：`description`、`format`、`const`、`itemClass`、`minItems`（僅限 0 或 1）。

      **由 SDK 驗證的限制條件**（從傳輸的 schema 中移除、附加至描述，並對回應進行驗證）：`minimum`、`maximum`、`multipleOf`、`minLength`、`maxLength`。
    </Accordion>
  </Tab>

  <Tab title="Ruby">
    ```ruby
    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-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: ContactInfo}
    )

    contact = message.parsed_output
    puts contact
    ```

    定義一個繼承 `Anthropic::BaseModel` 的類別，並將其作為 `output_config: {format: Model}` 傳給 `messages.create()`。SDK 會從該類別衍生 JSON schema、進行轉換，並將解析結果以具型別的 Ruby 物件指派給回應的 `parsed_output` 屬性。

    上述範例的輸出為：

    ```text Output wrap
    {name: "John Smith", email: "john@example.com", plan_interest: "Enterprise", demo_requested: true}
    ```

    <Accordion title="進階模型功能">
      Ruby SDK 支援額外的模型定義功能，以建立更豐富的 schema：

      * **`doc:` 關鍵字：** 為欄位加入描述，以產生資訊更豐富的 schema 輸出
      * **`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-5-5",
        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...">...]>
      ```
    </Accordion>
  </Tab>
</Tabs>

## SDK 轉換的運作方式

大多數 SDK 輔助工具會轉換使用了不支援功能的 schema。轉換步驟如下：

1. **移除不支援的限制條件**（例如 `minimum`、`maximum`、`minLength`、`maxLength`）
2. **更新描述**，將每個不支援的限制條件加入欄位的描述中（例如 `{minimum: 100}`）
3. **為所有物件新增 `additionalProperties: false`**
4. **篩選字串格式**，僅保留支援的清單
5. **驗證回應**，若輔助工具會驗證回應，則依據您原始的 schema 及其所有限制條件進行驗證

Claude 收到的是簡化後的 schema，但會驗證回應的輔助工具仍會在您的程式碼中強制執行每一項限制條件。

**範例：** 具有 `minimum: 100` 的欄位在傳送的 schema 中會變成一般整數，而 SDK 會將 `{minimum: 100}` 加入該欄位的描述中。會驗證回應的輔助工具仍會依據 `minimum: 100` 檢查回應。

## 使用原始 JSON schema

若要使用來自檔案、OpenAPI 規格，或在執行時期建構的 JSON schema，請將其傳入 `output_config.format`。

<Tabs>
  <Tab title="cURL">
    ```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-opus-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
            }
          }
        }
      }'
    ```

    Claude 會在回應的文字內容區塊中傳回如下的 JSON：

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-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
    YAML
    ```

    上述範例的輸出為：

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    client = anthropic.Anthropic()

    response = client.messages.create(
        model="claude-opus-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,
                },
            }
        },
    )
    text = next(block.text for block in response.content if block.type == "text")
    contact = json.loads(text)
    print(contact)
    ```

    上述範例的輸出為：

    ```text Output wrap
    {'name': 'John Smith', 'email': 'john@example.com', 'plan_interest': 'Enterprise', 'demo_requested': True}
    ```

    若要將[API 不支援的限制條件](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)移至欄位描述中，請在傳送前將 schema 傳入 `anthropic` 套件中的 `transform_schema()`，並視需要編輯結果。`transform_schema()` 也接受 Pydantic 模型。與 `client.messages.parse()` 不同，它會傳回轉換後的 schema，而不會傳送它。
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { jsonSchemaOutputFormat } from "@anthropic-ai/sdk/helpers/json-schema";

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      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 的型別為 { name: string; email: string; planInterest: string } | null
    console.log(response.parsed_output);
    ```

    使用 `jsonSchemaOutputFormat()` 將一般 JSON schema 傳給 `parse()`，無需安裝 Zod。API 會傳回符合該 schema 的回應，如果您以 `as const` 宣告 schema，TypeScript 會根據您的 schema 自動推斷 `parsed_output` 的型別。對於匯入或產生的 schema，`parsed_output` 的型別為 `unknown`。

    預設情況下，此輔助工具會以與 `zodOutputFormat()` 相同的方式[轉換 schema](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#how-sdk-transformation-works)。將 `{ transform: false }` 作為第二個引數傳入，即可原封不動地傳送。與 `zodOutputFormat()` 不同，它不會驗證回應，因此無法捕捉違反[API 不支援的限制條件](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)（例如 `minimum`）的情況。

    上述範例的輸出為：

    ```javascript Output
    { name: 'John Smith', email: 'john@example.com', planInterest: 'Pro' }
    ```
  </Tab>

  <Tab title="C#">
    ```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.ClaudeOpus5_5,
        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.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
    {
        // JSON 保證符合該 schema
        var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
        Console.WriteLine(contact);
    }
    ```

    上述範例的輸出為：

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

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

    	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		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: 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,
    				},
    			},
    		},
    	})

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

    上述範例的輸出為：

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```

    若要讓 SDK 轉換 map schema，請在 beta API 上將其傳入 `BetaJSONSchemaOutputFormat()`。
  </Tab>

  <Tab title="Java">
    ```java
    import com.anthropic.core.JsonValue;
    import com.anthropic.models.messages.JsonOutputFormat;
    // ...
    import com.anthropic.models.messages.OutputConfig;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;

    void main() throws Exception {
        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_5_5)
            .maxTokens(1024)
            .outputConfig(outputConfig)
            .addUserMessage(
                "John Smith (john@example.com) is interested in our Enterprise plan.")
            .build();

        String json = client.messages().create(createParams).content().stream()
            .flatMap(contentBlock -> contentBlock.text().stream())
            .findFirst()
            .orElseThrow()
            .text();

        JsonNode contact = new ObjectMapper().readTree(json);
        IO.println(contact);
    }
    ```

    上述範例的輸出為：

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise"}
    ```

    如需建構包含陣列和描述的巢狀 schema 的更完整範例，請參閱 SDK 儲存庫中的 [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java)。
  </Tab>

  <Tab title="PHP">
    ```php
    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-5-5',
        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,
        ])),
    );

    $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
    $contact = json_decode($textBlock->text, associative: true);
    var_dump($contact);
    ```

    將 schema 以關聯陣列的形式傳給 `OutputConfig::with()`，並使用 `json_decode()` 解碼回應。

    上述範例的輸出為：

    ```text Output wrap
    array(3) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
    }
    ```
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

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

    text = response.content.find { it.type == :text }.text
    contact = JSON.parse(text)
    p contact
    ```

    上述範例的輸出為：

    ```text Output wrap
    {"name" => "John Smith", "email" => "john@example.com", "plan_interest" => "Enterprise", "demo_requested" => true}
    ```
  </Tab>
</Tabs>

## 常見使用案例

<AccordionGroup>
  <Accordion title="資料擷取">
    從非結構化文字中擷取結構化資料：

    <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-5-5",
          "max_tokens": 4096,
          "messages": [
            {
              "role": "user",
              "content": "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "invoice_number": {"type": "string"},
                  "date": {"type": "string"},
                  "total_amount": {"type": "number"},
                  "line_items": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "customer_name": {"type": "string"}
                },
                "required": ["invoice_number", "date", "total_amount", "line_items", "customer_name"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 4096
      messages:
        - role: user
          content: "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              invoice_number: {type: string}
              date: {type: string}
              total_amount: {type: number}
              line_items:
                type: array
                items: {type: object, additionalProperties: false}
              customer_name: {type: string}
            required: [invoice_number, date, total_amount, line_items, customer_name]
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel


      class Invoice(BaseModel):
          invoice_number: str
          date: str
          total_amount: float
          line_items: list[dict]
          customer_name: str


      client = anthropic.Anthropic()
      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=4096,
          output_format=Invoice,
          messages=[
              {"role": "user", "content": f"Extract invoice data from: {invoice_text}"}
          ],
      )

      print(response.parsed_output)
      ```

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

      const client = new Anthropic();

      const InvoiceSchema = z.object({
        invoice_number: z.string(),
        date: z.string(),
        total_amount: z.number(),
        line_items: z.array(z.record(z.string(), z.any())),
        customer_name: z.string()
      });

      const invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: { format: zodOutputFormat(InvoiceSchema) },
        messages: [{ role: "user", content: `Extract invoice data from: ${invoiceText}` }]
      });
      console.log(response.parsed_output);
      ```

      ```csharp C#
      AnthropicClient client = new();

      string invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 4096,
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          invoice_number = new { type = "string" },
                          date = new { type = "string" },
                          total_amount = new { type = "number" },
                          line_items = new
                          {
                              type = "array",
                              items = new
                              {
                                  type = "object",
                                  additionalProperties = false,
                              },
                          },
                          customer_name = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "invoice_number", "date", "total_amount", "line_items", "customer_name" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
          Messages = [new() { Role = Role.User, Content = $"Extract invoice data from: {invoiceText}" }]
      };

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

      ```go Go
      client := anthropic.NewClient()

      invoiceText := "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      schema := map[string]any{
      	"type":                 "object",
      	"additionalProperties": false,
      	"properties": map[string]any{
      		"invoice_number": map[string]any{"type": "string"},
      		"date":           map[string]any{"type": "string"},
      		"total_amount":   map[string]any{"type": "number"},
      		"line_items": map[string]any{
      			"type": "array",
      			"items": map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"description": map[string]any{"type": "string"},
      					"quantity":    map[string]any{"type": "number"},
      					"unit_price":  map[string]any{"type": "number"},
      				},
      				"required": []string{"description", "quantity", "unit_price"},
      			},
      		},
      		"customer_name": map[string]any{"type": "string"},
      	},
      	"required": []string{"invoice_number", "date", "total_amount", "line_items", "customer_name"},
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 4096,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Extract invoice data from: %s", invoiceText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class LineItem {
          @JsonProperty("description")
          public String description;

          @JsonProperty("quantity")
          public int quantity;

          @JsonProperty("unit_price")
          public double unitPrice;
      }

      static class Invoice {
          @JsonProperty("invoice_number")
          public String invoiceNumber;

          @JsonProperty("date")
          public String date;

          @JsonProperty("total_amount")
          public double totalAmount;

          @JsonProperty("line_items")
          public List<LineItem> lineItems;

          @JsonProperty("customer_name")
          public String customerName;
      }

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

          String invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

          StructuredMessageCreateParams<Invoice> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(4096L)
              .outputConfig(Invoice.class)
              .addUserMessage("Extract invoice data from: " + invoiceText)
              .build();

          StructuredMessage<Invoice> response = client.messages().create(params);
          Invoice invoice = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(invoice.invoiceNumber + ": $" + invoice.totalAmount);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Invoice implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $invoice_number;
          public string $date;
          public float $total_amount;
          public array $line_items;
          public string $customer_name;
      }

      $invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      $message = $client->messages->create(
          maxTokens: 4096,
          messages: [
              ['role' => 'user', 'content' => "Extract invoice data from: $invoiceText"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Invoice::class],
      );

      $invoice = $message->parsedOutput();
      if ($invoice instanceof Invoice) {
          echo "Invoice {$invoice->invoice_number}: \${$invoice->total_amount}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class LineItem < Anthropic::BaseModel
        required :description, String
        required :amount, Float
      end

      class Invoice < Anthropic::BaseModel
        required :invoice_number, String
        required :date, String
        required :total_amount, Float
        required :line_items, Anthropic::ArrayOf[LineItem]
        required :customer_name, String
      end

      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: {format: Invoice},
        messages: [
          {role: "user", content: "Extract invoice data from: #{invoice_text}"}
        ]
      )

      invoice = message.parsed_output
      puts "Invoice #{invoice.invoice_number}: $#{invoice.total_amount}"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="分類">
    以結構化類別對內容進行分類：

    <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-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Classify this feedback: Great product, fast shipping!"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "category": {"type": "string"},
                  "confidence": {"type": "number"},
                  "tags": {"type": "array", "items": {"type": "string"}},
                  "sentiment": {"type": "string"}
                },
                "required": ["category", "confidence", "tags", "sentiment"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      messages:
        - role: user
          content: "Classify this feedback: Great product, fast shipping!"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              category:
                type: string
              confidence:
                type: number
              tags:
                type: array
                items:
                  type: string
              sentiment:
                type: string
            required:
              - category
              - confidence
              - tags
              - sentiment
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class Classification(BaseModel):
          category: str
          confidence: float
          tags: list[str]
          sentiment: str


      feedback_text = "Great product, but the delivery was slow."
      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=Classification,
          messages=[{"role": "user", "content": f"Classify this feedback: {feedback_text}"}],
      )

      print(response.parsed_output)
      ```

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

      const client = new Anthropic();

      const ClassificationSchema = z.object({
        category: z.string(),
        confidence: z.number(),
        tags: z.array(z.string()),
        sentiment: z.string()
      });

      const feedbackText = "Great product, but the delivery was slow.";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(ClassificationSchema) },
        messages: [{ role: "user", content: `Classify this feedback: ${feedbackText}` }]
      });

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

      ```csharp C#
      string feedbackText = "Great product, fast shipping!";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = $"Classify this feedback: {feedbackText}" }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          category = new { type = "string" },
                          confidence = new { type = "number" },
                          tags = new { type = "array", items = new { type = "string" } },
                          sentiment = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "category", "confidence", "tags", "sentiment" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

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

      ```go Go
      feedbackText := "Great product, fast shipping!"

      schema := map[string]any{
      	"type": "object",
      	"properties": map[string]any{
      		"category":   map[string]any{"type": "string"},
      		"confidence": map[string]any{"type": "number"},
      		"tags":       map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
      		"sentiment":  map[string]any{"type": "string"},
      	},
      	"required":             []string{"category", "confidence", "tags", "sentiment"},
      	"additionalProperties": false,
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Classify this feedback: %s", feedbackText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		var result map[string]any
      		json.Unmarshal([]byte(variant.Text), &result)
      		fmt.Println(result)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class Classification {
          @JsonProperty("category")
          public String category;

          @JsonProperty("confidence")
          public double confidence;

          @JsonProperty("tags")
          public List<String> tags;

          @JsonProperty("sentiment")
          public String sentiment;
      }

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

          String feedbackText = "Great product, fast shipping!";

          StructuredMessageCreateParams<Classification> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(Classification.class)
              .addUserMessage("Classify this feedback: " + feedbackText)
              .build();

          StructuredMessage<Classification> response = client.messages().create(params);
          Classification result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.category + " (" + result.confidence + ")");
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Classification implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $category;
          public float $confidence;
          public array $tags;
          public string $sentiment;
      }

      $feedbackText = "Great product, fast shipping!";

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => "Classify this feedback: {$feedbackText}"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Classification::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof Classification) {
          echo "{$result->category} ({$result->confidence}): {$result->sentiment}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Classification < Anthropic::BaseModel
        required :category, String
        required :confidence, Float
        required :tags, Anthropic::ArrayOf[String]
        required :sentiment, String
      end

      feedback_text = "Great product, fast shipping!"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: Classification},
        messages: [
          {role: "user", content: "Classify this feedback: #{feedback_text}"}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="API 回應格式化">
    產生可直接用於 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-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Process this request: ..."
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "status": {"type": "string"},
                  "data": {"type": "object", "additionalProperties": false},
                  "errors": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "metadata": {"type": "object", "additionalProperties": false}
                },
                "required": ["status", "data", "metadata"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text' \
        --raw-output <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              status:
                type: string
              data:
                type: object
                additionalProperties: false
              errors:
                type: array
                items:
                  type: object
                  additionalProperties: false
              metadata:
                type: object
                additionalProperties: false
            required:
              - status
              - data
              - metadata
            additionalProperties: false
      messages:
        - role: user
          content: "Process this request: ..."
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class APIResponse(BaseModel):
          status: str
          data: dict
          errors: list[dict] | None
          metadata: dict


      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=APIResponse,
          messages=[{"role": "user", "content": "Process this request: ..."}],
      )

      print(response.parsed_output)
      ```

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

      const client = new Anthropic();

      const APIResponseSchema = z.object({
        status: z.string(),
        data: z.record(z.string(), z.any()),
        errors: z.array(z.record(z.string(), z.any())).optional(),
        metadata: z.record(z.string(), z.any())
      });

      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(APIResponseSchema) },
        messages: [{ role: "user", content: "Process this request..." }]
      });

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

      ```csharp C#
      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = "Process this request: ..." }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          status = new { type = "string" },
                          data = new { type = "object", additionalProperties = false },
                          errors = new
                          {
                              type = "array",
                              items = new { type = "object", additionalProperties = false },
                          },
                          metadata = new { type = "object", additionalProperties = false },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "status", "data", "metadata" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

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

      ```go Go
      client := anthropic.NewClient()

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"status": map[string]any{
      						"type": "string",
      					},
      					"data": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      					"errors": map[string]any{
      						"type": "array",
      						"items": map[string]any{
      							"type":                 "object",
      							"additionalProperties": false,
      						},
      					},
      					"metadata": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      				},
      				"required": []string{"status", "data", "metadata"},
      			},
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock("Process this request: ...")),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class APIData {
          @JsonProperty("message")
          public String message;

          @JsonProperty("resource_id")
          public String resourceId;
      }

      static class APIError {
          @JsonProperty("code")
          public String code;

          @JsonProperty("message")
          public String message;
      }

      static class APIMetadata {
          @JsonProperty("request_id")
          public String requestId;

          @JsonProperty("timestamp")
          public String timestamp;
      }

      static class APIResponse {
          @JsonProperty("status")
          public String status;

          @JsonProperty("data")
          public APIData data;

          @JsonProperty("errors")
          public List<APIError> errors;

          @JsonProperty("metadata")
          public APIMetadata metadata;
      }

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

          StructuredMessageCreateParams<APIResponse> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(APIResponse.class)
              .addUserMessage("Process this request: ...")
              .build();

          StructuredMessage<APIResponse> response = client.messages().create(params);
          APIResponse result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.status);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Payload implements StructuredOutputModel { use StructuredOutputModelTrait; public string $message; }

      class APIError implements StructuredOutputModel { use StructuredOutputModelTrait; public string $code; public string $detail; }

      class Metadata implements StructuredOutputModel { use StructuredOutputModelTrait; public string $request_id; }

      class APIResponse implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $status;
          public Payload $data;
          #[Constrained(itemClass: APIError::class)]
          public ?array $errors;
          public Metadata $metadata;
      }

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => 'Process this request: ...']
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => APIResponse::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof APIResponse) {
          echo "{$result->status}: {$result->data->message}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Payload < Anthropic::BaseModel
        required :message, String
      end

      class APIError < Anthropic::BaseModel
        required :code, String
        required :detail, String
      end

      class Metadata < Anthropic::BaseModel
        required :request_id, String
      end

      class APIResponse < Anthropic::BaseModel
        required :status, String
        required :data, Payload
        optional :errors, Anthropic::ArrayOf[APIError]
        required :metadata, Metadata
      end

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: APIResponse},
        messages: [
          {role: "user", content: "Process this request: ..."}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## 嚴格工具使用

若要透過文法受限取樣對工具輸入強制執行 JSON Schema 合規性，請參閱[嚴格工具使用](https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/strict-tool-use)。

### 同時使用兩項功能

JSON 輸出和嚴格工具使用解決不同的問題，並可協同運作：

* **JSON 輸出**控制 Claude 的回應格式（Claude 說什麼）
* **嚴格工具使用**驗證工具參數（Claude 如何呼叫您的函式）

結合使用時，Claude 可以使用保證有效的參數呼叫工具，並且回傳結構化的 JSON 回應。這對於同時需要可靠工具呼叫和結構化最終輸出的代理工作流程非常有用。

<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-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Help me plan a trip to Paris departing May 15, 2026"
        }
      ],
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": {
            "type": "object",
            "properties": {
              "summary": {"type": "string"},
              "next_steps": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["summary", "next_steps"],
            "additionalProperties": false
          }
        }
      },
      "tools": [
        {
          "name": "search_flights",
          "strict": true,
          "input_schema": {
            "type": "object",
            "properties": {
              "destination": {"type": "string"},
              "date": {"type": "string", "format": "date"}
            },
            "required": ["destination", "date"],
            "additionalProperties": false
          }
        }
      ]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: Help me plan a trip to Paris departing May 15, 2026
  # JSON 輸出：結構化回應格式
  output_config:
    format:
      type: json_schema
      schema:
        type: object
        properties:
          summary:
            type: string
          next_steps:
            type: array
            items:
              type: string
        required: [summary, next_steps]
        additionalProperties: false
  # 嚴格工具使用：保證符合規範的工具參數
  tools:
    - name: search_flights
      strict: true
      input_schema:
        type: object
        properties:
          destination:
            type: string
          date:
            type: string
            format: date
        required: [destination, date]
        additionalProperties: false
  YAML
  ```

  ```python Python
  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Help me plan a trip to Paris departing May 15, 2026",
          }
      ],
      # JSON 輸出：結構化回應格式
      output_config={
          "format": {
              "type": "json_schema",
              "schema": {
                  "type": "object",
                  "properties": {
                      "summary": {"type": "string"},
                      "next_steps": {"type": "array", "items": {"type": "string"}},
                  },
                  "required": ["summary", "next_steps"],
                  "additionalProperties": False,
              },
          }
      },
      # 嚴格工具使用：保證符合規格的工具參數
      tools=[
          {
              "name": "search_flights",
              "strict": True,
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "destination": {"type": "string"},
                      "date": {"type": "string", "format": "date"},
                  },
                  "required": ["destination", "date"],
                  "additionalProperties": False,
              },
          }
      ],
  )

  print(response)
  ```

  ```typescript TypeScript
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],
    // JSON 輸出：結構化回應格式
    output_config: {
      format: {
        type: "json_schema",
        schema: {
          type: "object",
          properties: {
            summary: { type: "string" },
            next_steps: { type: "array", items: { type: "string" } }
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    // 嚴格工具使用：保證工具參數符合結構
    tools: [
      {
        name: "search_flights",
        description: "Search for available flights to a destination on a specific date",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: { type: "string" },
            date: { type: "string", format: "date" }
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  });

  // Claude 可能先呼叫工具（tool_use），或直接以 JSON 回應（text）
  console.log("Stop reason:", response.stop_reason);
  for (const block of response.content) {
    switch (block.type) {
      case "tool_use":
        console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);
        break;
      case "text":
        console.log("Response:", block.text);
        break;
    }
  }
  ```

  ```csharp C#
  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Help me plan a trip to Paris departing May 15, 2026" }],
      // JSON 輸出：結構化回應格式
      OutputConfig = new OutputConfig
      {
          Format = new JsonOutputFormat
          {
              Schema = new Dictionary<string, JsonElement>
              {
                  ["type"] = JsonSerializer.SerializeToElement("object"),
                  ["properties"] = JsonSerializer.SerializeToElement(new
                  {
                      summary = new { type = "string" },
                      next_steps = new { type = "array", items = new { type = "string" } },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              },
          },
      },
      // 嚴格工具使用：保證工具參數符合規範
      Tools =
      [
          new Tool
          {
              Name = "search_flights",
              Strict = true,
              InputSchema = new InputSchema(new Dictionary<string, JsonElement>
              {
                  ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
                  {
                      ["destination"] = new { type = "string" },
                      ["date"] = new { type = "string", format = "date" },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              }),
          }
      ],
  };

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

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),
  	},
  	// JSON 輸出：結構化回應格式
  	OutputConfig: anthropic.OutputConfigParam{
  		Format: anthropic.JSONOutputFormatParam{
  			Schema: map[string]any{
  				"type":                 "object",
  				"additionalProperties": false,
  				"properties": map[string]any{
  					"summary":    map[string]any{"type": "string"},
  					"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
  				},
  				"required": []string{"summary", "next_steps"},
  			},
  		},
  	},
  	// 嚴格工具使用：保證符合規範的工具參數
  	Tools: []anthropic.ToolUnionParam{
  		{OfTool: &anthropic.ToolParam{
  			Name:   "search_flights",
  			Strict: anthropic.Bool(true),
  			InputSchema: anthropic.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"destination": map[string]any{"type": "string"},
  					"date":        map[string]any{"type": "string", "format": "date"},
  				},
  				Required: []string{"destination", "date"},
  				ExtraFields: map[string]any{
  					"additionalProperties": false,
  				},
  			}}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.Content)
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // JSON 輸出：結構化回應格式
  JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()
      .putAdditionalProperty("type", JsonValue.from("object"))
      .putAdditionalProperty("properties", JsonValue.from(Map.of(
          "summary", Map.of("type", "string"),
          "next_steps", Map.of("type", "array", "items", Map.of("type", "string"))
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  // 嚴格工具使用：保證工具參數符合結構
  InputSchema toolSchema = InputSchema.builder()
      .properties(JsonValue.from(Map.of(
          "destination", Map.of("type", "string"),
          "date", Map.of("type", "string", "format", "date")
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
      .outputConfig(OutputConfig.builder()
          .format(JsonOutputFormat.builder().schema(outputSchema).build())
          .build())
      .addTool(Tool.builder()
          .name("search_flights")
          .description("Search for available flights to a destination on a specific date")
          .strict(true)
          .inputSchema(toolSchema)
          .build())
      .build();

  Message response = client.messages().create(params);
  IO.println(response);
  ```

  ```php PHP
  use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
  use Anthropic\Lib\Contracts\StructuredOutputModel;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  class TripPlan implements StructuredOutputModel
  {
      use StructuredOutputModelTrait;

      public string $summary;
      public array $next_steps;
  }

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          ['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']
      ],
      model: 'claude-opus-5-5',
      // JSON 輸出：結構化回應格式
      outputConfig: ['format' => TripPlan::class],
      // 嚴格工具使用：保證工具參數符合結構
      tools: [
          [
              'name' => 'search_flights',
              'strict' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'destination' => ['type' => 'string'],
                      'date' => ['type' => 'string', 'format' => 'date']
                  ],
                  'required' => ['destination', 'date'],
                  'additionalProperties' => false
              ]
          ]
      ],
  );

  // Claude 可能先呼叫工具（tool_use），或直接以 JSON 回應（text）
  $plan = $message->parsedOutput();
  if ($plan instanceof TripPlan) {
      echo $plan->summary, "\n";
  } elseif ($toolUse = array_find($message->content, fn($block) => $block instanceof ToolUseBlock)) {
      echo "Tool call: {$toolUse->name}(", json_encode($toolUse->input), ")\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {role: "user", content: "Help me plan a trip to Paris departing May 15, 2026"}
    ],
    # JSON 輸出：結構化回應格式
    output_config: {
      format: {
        type: :json_schema,
        schema: {
          type: "object",
          properties: {
            summary: {type: "string"},
            next_steps: {type: "array", items: {type: "string"}}
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    # 嚴格工具使用：保證工具參數符合結構描述
    tools: [
      {
        name: "search_flights",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: {type: "string"},
            date: {type: "string", format: "date"}
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  )
  puts message
  ```
</CodeGroup>

## 重要注意事項

### 文法編譯與快取

結構化輸出使用搭配已編譯文法產物的受限取樣。這會帶來一些需要注意的效能特性：

* **首次請求延遲：** 第一次使用特定 schema 時，文法編譯會產生額外的延遲

* **自動快取：** 已編譯的文法會自上次使用起快取 24 小時，使後續請求快上許多

* **快取失效：** 若您變更以下項目，快取將會失效：

  * JSON schema 結構
  * 請求中的工具集合（同時使用結構化輸出和工具使用時）
  * 僅變更 `name` 或 `description` 欄位不會使快取失效

### 提示修改與 token 成本

使用結構化輸出時，Claude 會自動收到一段額外的系統提示，說明預期的輸出格式。這表示：

* 您的輸入 token 數量會略微增加
* 注入的提示會像任何其他系統提示一樣消耗您的 token
* 變更 `output_config.format` 參數會使該對話串的任何[提示快取](https://platform.claude.com/docs/zh-TW/build-with-claude/prompt-caching)失效

### JSON Schema 限制

結構化輸出支援標準 JSON Schema，但有一些限制。JSON 輸出和嚴格工具使用共用這些限制。

<Accordion title="支援的功能">
  * 所有基本型別：object、array、string、integer、number、boolean、null
  * `enum`（僅限字串、數字、布林值或 null，不支援複雜型別）。關於大小寫的注意事項，請參閱[無效輸出](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#invalid-outputs)。
  * `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）
</Accordion>

<Accordion title="不支援">
  * 遞迴 schema
  * 列舉中的複雜型別
  * 外部 `$ref`（例如 `'$ref': 'http://...'`）
  * 數值限制條件（例如 `minimum`、`maximum`、`multipleOf`）
  * 字串限制條件（`minLength`、`maxLength`）
  * 超出 `minItems` 為 0 或 1 以外的陣列限制條件
  * `additionalProperties` 設為 `false` 以外的任何值

  若您使用不支援的功能，將會收到含有詳細資訊的 400 錯誤。
</Accordion>

<Accordion title="Pattern 支援（正規表示式）">
  **支援的正規表示式功能：**

  * 完整比對（`^...$`）和部分比對
  * 量詞：`*`、`+`、`?`、簡單的 `{n,m}` 情況
  * 字元類別：`[]`、`.`、`\d`、`\w`、`\s`
  * 群組：`(...)`

  **不支援：**

  * 對群組的反向參照（例如 `\1`、`\2`）
  * 前瞻／後顧斷言（例如 `(?=...)`、`(?!...)`）
  * 單字邊界：`\b`、`\B`
  * 範圍很大的複雜 `{n,m}` 量詞

  簡單的正規表示式模式運作良好。複雜的模式可能導致 400 錯誤。
</Accordion>

<Tip>
  SDK 輔助工具可以轉換含有[API 不支援的限制條件](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#json-schema-limitations)的 schema。請參閱 [SDK 轉換的運作方式](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#how-sdk-transformation-works)。
</Tip>

### 屬性順序

使用結構化輸出時，物件中的屬性會維持您在 schema 中定義的順序，但有一個重要的注意事項：**必要屬性會先出現，接著才是選用屬性**。

例如，給定以下 schema：

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

輸出會將屬性排序為：

1. `name`（必要，依 schema 順序）
2. `email`（必要，依 schema 順序）
3. `notes`（選用，依 schema 順序）
4. `age`（選用，依 schema 順序）

這表示輸出可能如下所示：

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

若輸出中的屬性順序對您的應用程式很重要，請將所有屬性標記為必要，或在您的解析邏輯中考量此重新排序。

### 無效輸出

雖然結構化輸出在大多數情況下保證符合 schema，但在某些情境下輸出可能不符合您的 schema：

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

即使使用結構化輸出，Claude 仍會維持其安全性和有幫助的特性。若 Claude 因安全理由拒絕請求：

* 回應會有 `stop_reason: "refusal"`
* 您會收到 200 狀態碼
* 您會被收取所產生 token 的費用
* 輸出可能不符合您的 schema，因為拒絕訊息優先於 schema 限制

要求模型提供其思考過程或逐步推理的屬性，可能會導致 `reasoning_extraction` 拒絕。請改為要求簡短的說明。請參閱[將推理保留在思考區塊中](https://platform.claude.com/docs/zh-TW/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks)。

**達到 token 上限**（`stop_reason: "max_tokens"`）

若回應因達到 `max_tokens` 上限而被截斷：

* 回應會有 `stop_reason: "max_tokens"`
* 輸出可能不完整且不符合您的 schema
* 請以更高的 `max_tokens` 值重試，以取得完整的結構化輸出

**列舉值大小寫**

結構化輸出不保證字串 `enum` 和 `const` 值的大小寫：Claude 可能回傳一個僅在大小寫上與您的 schema 不同的值，通常是空格後單字的第一個字母。例如，給定以下 schema：

```json
{
  "type": "string",
  "enum": ["Conversation Topic 1", "Conversation Topic 2", "Conversation topic 3"]
}
```

輸出可能包含 `"Conversation Topic 3"`（大寫「T」），即使該確切值不在列舉中。回應會正常完成，沒有錯誤也沒有特殊的 `stop_reason`。這同時適用於 JSON 輸出和嚴格工具使用。請以不區分大小寫的方式比較列舉值，並避免使用僅在大小寫上不同的列舉值。

### Schema 複雜度限制

結構化輸出的運作方式是將您的 JSON schema 編譯為限制 Claude 輸出的文法。越複雜的 schema 會產生越大的文法，編譯時間也越長。為防止編譯時間過長，API 強制執行數項複雜度限制。

#### 明確限制

以下限制適用於所有帶有 `output_config.format` 或 `strict: true` 的請求：

| 限制         | 值  | 說明                                                                                            |
| ---------- | -- | --------------------------------------------------------------------------------------------- |
| 每個請求的嚴格工具數 | 20 | 帶有 `strict: true` 的工具數量上限。非嚴格工具不計入此限制。                                                        |
| 選用參數       | 24 | 所有嚴格工具 schema 和 JSON 輸出 schema 中的選用參數總數。每個未列於 `required` 中的參數都計入此限制。                          |
| 具有聯集型別的參數  | 16 | 所有嚴格 schema 中使用 `anyOf` 或型別陣列（例如 `"type": ["string", "null"]`）的參數總數。這些參數特別昂貴，因為它們會產生指數級的編譯成本。 |

<Note>
  這些限制適用於單一請求中所有嚴格 schema 的合計總數。例如，若您有 4 個嚴格工具，每個有 6 個選用參數，即使沒有任何單一工具看起來很複雜，您仍會達到 24 個參數的上限。
</Note>

#### 額外的內部限制

除了上表中的明確限制之外，已編譯文法的大小還有額外的內部限制。這些限制之所以存在，是因為 schema 複雜度無法簡化為單一維度：選用參數、聯集型別、巢狀物件和工具數量等功能會彼此交互作用，可能使已編譯的文法變得不成比例地龐大。

超出這些限制時，您會收到訊息為「Schema is too complex for compilation.」的 400 錯誤。這些錯誤表示您的 schema 合計複雜度超出了可有效率編譯的範圍，即使上表中的每項個別限制都已滿足。作為最後的防線，API 也強制執行 **180 秒的編譯逾時**。通過所有明確檢查但產生非常龐大已編譯文法的 schema 可能會觸及此逾時。

#### 降低 schema 複雜度的技巧

若您觸及複雜度限制，請依序嘗試以下策略：

1. **僅將關鍵工具標記為嚴格。** 若您有許多工具，請將嚴格模式保留給 schema 違規會造成實際問題的工具，而對較簡單的工具則仰賴 Claude 的自然遵循能力。

2. **減少選用參數。** 盡可能將參數設為 `required`。每個選用參數大約會使文法狀態空間的一部分加倍。若某個參數總是有合理的預設值，請考慮將其設為必要，並讓 Claude 明確提供該預設值。

3. **簡化巢狀結構。** 帶有選用欄位的深層巢狀物件會使複雜度加劇。盡可能將結構扁平化。

4. **拆分為多個請求。** 若您有許多嚴格工具，請考慮將它們拆分至不同的請求或子代理中。

若有效的 schema 持續發生問題，請附上您的 schema 定義[聯絡支援團隊](https://support.claude.com/en/articles/9015913-how-to-get-support)。

## 從 beta 版遷移

`output_format` 參數已移至 `output_config.format`，且不再需要 beta 標頭。`output_format` 參數已被棄用，並將在未來移除。若仍要使用它，請新增 `structured-outputs-2025-11-13` beta 標頭。若未新增，API 會傳回 400 錯誤。

Python SDK（v1.0 及更新版本）在 `client.beta.messages.create()` 或 `count_tokens()` 上不接受 `output_format={...}`，並會引發 `TypeError`。請改用 `output_config`。如需更新後的 API 格式，請參閱[使用原始 JSON schema](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#using-a-raw-json-schema)。

## 資料保留

使用結構化輸出時，提示和回應會以 ZDR 處理。然而，JSON schema 本身會基於最佳化目的，自上次使用起暫時快取最多 24 小時。除 API 回應之外，不會保留任何提示或回應資料。

結構化輸出符合 HIPAA 資格，但 **JSON schema 定義中不得包含 PHI**。API 會將 JSON schema 編譯為與訊息內容分開快取的文法，而這些快取的 schema 不會獲得與提示和回應相同的 PHI 保護。請勿在 schema 屬性名稱、`enum` 值、`const` 值或 `pattern` 正規表示式中包含 PHI。PHI 應僅出現在訊息內容（提示和回應）中，在那裡它受到 HIPAA 保護措施的保障。

關於所有功能的 ZDR 和 HIPAA 資格，請參閱 [API 與資料保留](https://platform.claude.com/docs/zh-TW/manage-claude/api-and-data-retention)。

## 功能相容性

**可搭配使用：**

* **[批次處理](https://platform.claude.com/docs/zh-TW/build-with-claude/batch-processing)：** 以 50% 折扣大規模處理結構化輸出
* **[Token 計數](https://platform.claude.com/docs/zh-TW/build-with-claude/token-counting)：** 無需編譯即可計算 token
* **[串流](https://platform.claude.com/docs/zh-TW/build-with-claude/streaming)：** 像一般回應一樣串流結構化輸出
* **[合併使用](https://platform.claude.com/docs/zh-TW/build-with-claude/structured-outputs#using-both-features-together)：** 在同一個請求中同時使用 JSON 輸出（`output_config.format`）和嚴格工具使用（`strict: true`）

**不相容：**

* **[引用](https://platform.claude.com/docs/zh-TW/build-with-claude/citations)：** 引用需要將引用區塊與文字交錯，這與嚴格的 JSON schema 限制相衝突。若在啟用引用的情況下使用 `output_config.format`，會回傳 400 錯誤。
* **訊息預填：** 與 JSON 輸出不相容

<Tip>
  **文法範圍：** 文法僅套用於 Claude 的直接輸出，不套用於工具使用呼叫、工具結果或思考標籤（使用[思考](https://platform.claude.com/docs/zh-TW/build-with-claude/thinking)時）。文法狀態會在各區段之間重設，讓 Claude 能夠自由思考，同時仍在最終回應中產生結構化輸出。
</Tip>

## 後續步驟

<CardGroup cols={2}>
  <Card title="引用" icon="book-bookmark" href="https://platform.claude.com/docs/zh-TW/build-with-claude/citations">
    讓 Claude 在回答有關所提供文件的問題時引用其來源。
  </Card>

  <Card title="嚴格工具使用" icon="check" href="https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/strict-tool-use">
    透過文法受限取樣對 Claude 的工具輸入強制執行 JSON Schema 合規性。
  </Card>

  <Card title="搭配 Claude 的工具使用" icon="wrench" href="https://platform.claude.com/docs/zh-TW/agents-and-tools/tool-use/overview">
    將 Claude 連接至外部工具和 API。了解工具在何處執行以及代理迴圈如何運作。
  </Card>

  <Card title="定價" icon="calculator" href="https://platform.claude.com/docs/zh-TW/about-claude/pricing">
    了解 Anthropic 針對模型和功能的定價結構。
  </Card>
</CardGroup>
