> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aihubmix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 結構化輸出 Structured Outputs

> 透過 JSON Schema 約束模型輸出格式（Structured Outputs），支援 OpenAI、Anthropic Claude、Gemini 等模型的 response_format 與 output_config.format，含自動降級與跨協議轉換。

## 功能概述

結構化輸出（Structured Outputs）讓模型的回覆嚴格遵循你定義的 JSON Schema，確保回傳值可以直接被程式解析，無需正則或後處理。

與在 prompt 中要求模型「請回傳 JSON」不同，結構化輸出基於**受約束解碼（Constrained Decoding）**：上游將 JSON Schema 編譯為語法規則，在推理過程中逐 token 約束生成，模型**不可能**產出違反 Schema 的內容。

典型場景：

* 從非結構化文字中擷取實體和欄位
* 分類 / 打標籤 / 情感分析
* 多步推理中間結果的標準化傳遞
* Agent 工具呼叫參數的強型別約束

## 各協議參數對照

<Note>
  三種協議的參數名不同，但底層機制一致：模型輸出嚴格匹配你提供的 JSON Schema。
</Note>

| 協議                                 | 參數                                         | 支援模型                                       |
| ---------------------------------- | ------------------------------------------ | ------------------------------------------ |
| OpenAI Chat `/v1/chat/completions` | `response_format.type: "json_schema"`      | Claude 4.5+、GPT-4o / GPT-5 系列、Gemini 系列    |
| Anthropic Messages `/v1/messages`  | `output_config.format.type: "json_schema"` | Claude 4.5+（直連 / Vertex）；Bedrock 僅 4.5–4.6 |
| OpenAI Responses `/v1/responses`   | `text.format.type: "json_schema"`          | 隨上游模型能力                                    |

### AWS Bedrock 限制

AWS Bedrock 上 Claude 4.7 及以上版本使用 Mantle 推理路徑，該路徑目前不支援 `output_config.format`。閘道會自動為這些模型剝離 `format` 欄位並在回應標頭中標記降級（見下方[降級機制](#降級機制)），請求不會因此報錯。

***

## 快速開始

### OpenAI 協議（推薦）

適用於所有支援結構化輸出的模型，跨廠商通用。

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      api_key="<AIHUBMIX_API_KEY>",
      base_url="https://aihubmix.com/v1",
  )

  response = client.chat.completions.create(
      model="claude-sonnet-5",
      messages=[
          {"role": "user", "content": "提取以下資訊：張三，28歲，台北市信義區"}
      ],
      response_format={
          "type": "json_schema",
          "json_schema": {
              "name": "person_info",
              "strict": True,
              "schema": {
                  "type": "object",
                  "properties": {
                      "name": {"type": "string"},
                      "age": {"type": "integer"},
                      "city": {"type": "string"}
                  },
                  "required": ["name", "age", "city"],
                  "additionalProperties": False
              }
          }
      }
  )

  import json
  data = json.loads(response.choices[0].message.content)
  print(data)
  # {"name": "張三", "age": 28, "city": "台北市信義區"}
  ```

  ```typescript Node.js theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: "<AIHUBMIX_API_KEY>",
    baseURL: "https://aihubmix.com/v1",
  });

  const response = await client.chat.completions.create({
    model: "claude-sonnet-5",
    messages: [
      { role: "user", content: "提取以下資訊：張三，28歲，台北市信義區" },
    ],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "person_info",
        strict: true,
        schema: {
          type: "object",
          properties: {
            name: { type: "string" },
            age: { type: "integer" },
            city: { type: "string" },
          },
          required: ["name", "age", "city"],
          additionalProperties: false,
        },
      },
    },
  });

  const data = JSON.parse(response.choices[0].message.content);
  console.log(data);
  ```

  ```bash cURL theme={null}
  curl https://aihubmix.com/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <AIHUBMIX_API_KEY>" \
    -d '{
      "model": "claude-sonnet-5",
      "messages": [
        {"role": "user", "content": "提取以下資訊：張三，28歲，台北市信義區"}
      ],
      "response_format": {
        "type": "json_schema",
        "json_schema": {
          "name": "person_info",
          "strict": true,
          "schema": {
            "type": "object",
            "properties": {
              "name": {"type": "string"},
              "age": {"type": "integer"},
              "city": {"type": "string"}
            },
            "required": ["name", "age", "city"],
            "additionalProperties": false
          }
        }
      }
    }'
  ```
</CodeGroup>

### 使用其他模型（GLM-5.2 示例）

同一套 OpenAI 協議參數適用於所有支援結構化輸出的模型，切換 `model` 即可。

```python Python theme={null}
from openai import OpenAI

client = OpenAI(
    api_key="<AIHUBMIX_API_KEY>",
    base_url="https://aihubmix.com/v1",
)

response = client.chat.completions.create(
    model="glm-5.2",
    messages=[
        {"role": "user", "content": "提取以下資訊：張三，28歲，台北市信義區"}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person_info",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "city": {"type": "string"}
                },
                "required": ["name", "age", "city"],
                "additionalProperties": False
            }
        }
    }
)

import json
data = json.loads(response.choices[0].message.content)
print(data)
# {"name": "張三", "age": 28, "city": "台北市信義區"}
```

### Claude 原生協議

使用 Anthropic SDK 直接呼叫，參數為 `output_config.format`。

<CodeGroup>
  ```python Python theme={null}
  import anthropic

  client = anthropic.Anthropic(
      api_key="<AIHUBMIX_API_KEY>",
      base_url="https://aihubmix.com",
  )

  response = client.messages.create(
      model="claude-sonnet-5",
      max_tokens=1024,
      messages=[
          {"role": "user", "content": "提取以下資訊：張三，28歲，台北市信義區"}
      ],
      output_config={
          "format": {
              "type": "json_schema",
              "schema": {
                  "type": "object",
                  "properties": {
                      "name": {"type": "string"},
                      "age": {"type": "integer"},
                      "city": {"type": "string"}
                  },
                  "required": ["name", "age", "city"],
                  "additionalProperties": False
              }
          }
      }
  )

  import json
  data = json.loads(response.content[0].text)
  print(data)
  ```

  ```bash cURL theme={null}
  curl https://aihubmix.com/v1/messages \
    -H "Content-Type: application/json" \
    -H "x-api-key: <AIHUBMIX_API_KEY>" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-sonnet-5",
      "max_tokens": 1024,
      "messages": [
        {"role": "user", "content": "提取以下資訊：張三，28歲，台北市信義區"}
      ],
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": {
            "type": "object",
            "properties": {
              "name": {"type": "string"},
              "age": {"type": "integer"},
              "city": {"type": "string"}
            },
            "required": ["name", "age", "city"],
            "additionalProperties": false
          }
        }
      }
    }'
  ```
</CodeGroup>

***

## Schema 編寫要點

### 必需欄位

所有 `object` 類型必須顯式宣告 `additionalProperties: false`，否則部分上游會拒絕請求。

```json theme={null}
{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "score": { "type": "number" }
  },
  "required": ["name", "score"],
  "additionalProperties": false
}
```

### 巢狀物件

巢狀的 `object` 同樣需要 `additionalProperties: false`：

```json theme={null}
{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "email": { "type": "string" }
      },
      "required": ["name", "email"],
      "additionalProperties": false
    }
  },
  "required": ["user"],
  "additionalProperties": false
}
```

### 跨協議 Schema 差異

| 特性                              | OpenAI 協議    | Anthropic 協議      |
| ------------------------------- | ------------ | ----------------- |
| `name` 欄位                       | 必需           | 不支援（閘道跨協議呼叫時自動處理） |
| `strict` 欄位                     | 可選，推薦 `true` | 不支援               |
| 數值約束 (`minimum`, `maximum` 等)   | 支援           | 不支援（閘道自動清理，不影響請求） |
| 字串約束 (`minLength`, `maxLength`) | 支援           | 不支援（閘道自動清理）       |

<Tip>
  使用 OpenAI 協議呼叫 Claude 模型時，閘道會自動轉換 Schema 格式並清理不相容的關鍵字，無需手動適配。
</Tip>

***

## 自動降級機制

閘道預設為所有請求開啟結構化輸出的自動降級保護。當模型或平台不支援時，閘道**不會回傳錯誤**，而是自動剝離 Schema 約束並在回應標頭中標記降級原因。你的請求仍然會得到正常的模型回覆，只是輸出不受 Schema 強制約束。

這意味著你可以放心地在用戶端統一啟用結構化輸出，而無需針對每個模型做相容判斷：

* **多模型切換無憂**：同一套程式碼在 Claude、GPT、Gemini、GLM 之間切換模型時，即使目標模型不支援結構化輸出，請求也不會報錯
* **Fallback 路由透明**：當主渠道不可用觸發 Fallback 路由到備用渠道時，即使備用渠道的模型版本不支援結構化輸出，請求仍然正常完成
* **用戶端邏輯簡化**：不需要維護一份「哪些模型支援結構化輸出」的清單，閘道已自動處理；用戶端只需檢查回應標頭決定是否需要額外解析

### 回應標頭

```
X-Structured-Output-Degraded: <reason>
```

| reason                                 | 含義                                           |
| -------------------------------------- | -------------------------------------------- |
| `model_unsupported`                    | 該模型（或該模型在當前平台上）不支援結構化輸出                      |
| `json_object_unsupported_on_anthropic` | `json_object` 模式無法轉換為 Anthropic 格式           |
| `json_schema_missing_schema`           | 指定了 `json_schema` 類型但缺少 `schema` 欄位          |
| `schema_keywords_stripped`             | Schema 中的部分約束關鍵字被清理（如 `minimum`、`maxLength`） |

### 偵測範例

```python Python theme={null}
import httpx

response = httpx.post(
    "https://aihubmix.com/v1/chat/completions",
    headers={
        "Authorization": "Bearer <AIHUBMIX_API_KEY>",
        "Content-Type": "application/json",
    },
    json={
        "model": "claude-sonnet-5",
        "messages": [{"role": "user", "content": "提取資訊：張三，28歲"}],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "person",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
                    "required": ["name", "age"],
                    "additionalProperties": False,
                }
            }
        }
    }
)

# 檢查是否發生降級（例如請求被路由到不支援的 Bedrock 渠道時）
degraded = response.headers.get("X-Structured-Output-Degraded")
if degraded:
    print(f"結構化輸出已降級：{degraded}")
    # 此時模型回覆仍然正常，但輸出不受 Schema 強制約束
else:
    import json
    data = json.loads(response.json()["choices"][0]["message"]["content"])
    print(data)  # {"name": "張三", "age": 28}
```

***

## 與 `json_object` 模式的區別

|           | `json_schema`（結構化輸出）           | `json_object` |
| --------- | ------------------------------ | ------------- |
| 輸出保證      | 嚴格匹配指定 Schema                  | 僅保證是合法 JSON   |
| 欄位控制      | 欄位名、類型、是否必需均受約束                | 無約束           |
| 適用協議      | OpenAI / Anthropic / Responses | 僅 OpenAI 相容協議 |
| Claude 支援 | 透過 `output_config.format`      | 不支援           |

<Warning>
  `json_object` 模式不支援轉換到 Claude 原生協議。如果你透過 OpenAI 協議向 Claude 傳送 `response_format: {"type": "json_object"}`，回應標頭會標記 `json_object_unsupported_on_anthropic` 降級。建議直接使用 `json_schema` 類型。
</Warning>

***

## 常見問題

<AccordionGroup>
  <Accordion title="哪些模型支援 Structured Outputs？">
    **Claude 系列**（透過 Anthropic API 的 `output_config.format`）：

    * Opus / Sonnet / Haiku 4.5 及以上版本
    * Fable / Mythos 5 及以上版本
    * Bedrock 平台僅 4.5–4.6；Vertex AI 與直連一致

    **OpenAI 系列**（透過 `response_format`）：

    * GPT-4o 及以上、GPT-5 系列

    **Gemini 系列**（透過 `responseSchema`）：

    * Gemini 2.5 及以上

    可透過[模型列表頁](https://aihubmix.com/models)查看各模型的能力標籤。
  </Accordion>

  <Accordion title="為什麼 Bedrock 上部分 Claude 模型不支援結構化輸出？">
    AWS Bedrock 上 Claude 4.7+ 使用了新的 Mantle 推理路徑，該路徑目前不接受 `output_config.format` 參數。閘道會自動處理：剝離 format 並正常回傳回應，同時標記降級標頭 `X-Structured-Output-Degraded: model_unsupported`。Claude 4.5–4.6 在 Bedrock 上完全支援。
  </Accordion>

  <Accordion title="跨協議呼叫時 JSON Schema 會被修改嗎？">
    會。當透過 OpenAI 協議呼叫 Claude 模型時，閘道自動：

    1. 將 `response_format` 轉換為 `output_config.format`
    2. 移除 Anthropic 不支援的 Schema 關鍵字（`minimum`、`maxLength` 等）
    3. 如果有關鍵字被清理，回應標頭標記 `schema_keywords_stripped`

    反向（Claude 協議呼叫 OpenAI 模型）同樣自動轉換。
  </Accordion>

  <Accordion title="Structured Outputs 可以和 Extended Thinking 同時使用嗎？">
    可以。`output_config` 中的 `format`（結構化輸出）和 `reasoning` 中的 `effort`（思考強度）是獨立參數，可以同時設定：

    ```json theme={null}
    {
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": { ... }
        }
      },
      "reasoning": {
        "effort": "high"
      }
    }
    ```
  </Accordion>

  <Accordion title="與 OpenRouter 等聚合平台相比，降級機制有什麼不同？">
    大多數 API 聚合平台在模型不支援結構化輸出時會直接回傳錯誤。AIHubMix 採用**優雅降級**策略：自動剝離不相容的參數，正常回傳模型回應，並透過 `X-Structured-Output-Degraded` 回應標頭告知用戶端降級原因。你的應用不會因此中斷。
  </Accordion>
</AccordionGroup>
