跳轉到主要內容
結構化輸出強制模型回傳符合您定義結構的 JSON 物件。這消除了解析、驗證或重試非結構化模型輸出的需要。 ARouter 支援兩種結構化輸出模式:
模式說明
json_object保證輸出有效 JSON;不強制執行結構
json_schema保證輸出與您的精確結構匹配的 JSON

使用結構化輸出

在請求體中傳入 response_format

JSON Object 模式

{
  "model": "openai/gpt-5.4",
  "messages": [
    {
      "role": "user",
      "content": "Return information about the planet Mars as JSON"
    }
  ],
  "response_format": { "type": "json_object" }
}
模型回傳有效 JSON,但不強制執行結構:
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"Mars\",\"diameter_km\":6779,\"moons\":2,\"habitable\":false}"
      }
    }
  ]
}

JSON Schema 模式

使用 json_schema 強制執行嚴格結構:
{
  "model": "openai/gpt-5.4",
  "messages": [
    {
      "role": "system",
      "content": "You are a data extraction assistant."
    },
    {
      "role": "user",
      "content": "Extract the planet information: Mars is a terrestrial planet with 2 moons and a diameter of 6779 km."
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "planet_info",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "diameter_km": { "type": "number" },
          "moons": { "type": "integer" },
          "habitable": { "type": "boolean" }
        },
        "required": ["name", "diameter_km", "moons", "habitable"],
        "additionalProperties": false
      }
    }
  }
}
回應:
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"Mars\",\"diameter_km\":6779,\"moons\":2,\"habitable\":false}"
      },
      "finish_reason": "stop"
    }
  ]
}

完整範例

import json
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
    base_url="https://api.arouter.ai/v1",
    api_key="lr_live_xxxx",
)

# 使用 Pydantic 定義結構
class PlanetInfo(BaseModel):
    name: str
    diameter_km: float
    moons: int
    habitable: bool

# 使用 OpenAI 的 parse() 方法(推薦)
response = client.beta.chat.completions.parse(
    model="openai/gpt-5.4",
    messages=[
        {"role": "system", "content": "You are a data extraction assistant."},
        {"role": "user", "content": "Tell me about Mars."},
    ],
    response_format=PlanetInfo,
)

planet = response.choices[0].message.parsed
print(planet.name)        # "Mars"
print(planet.moons)       # 2
print(planet.diameter_km) # 6779.0

# 或手動使用 response_format dict
response = client.chat.completions.create(
    model="openai/gpt-5.4",
    messages=[
        {"role": "user", "content": "Tell me about Mars as JSON."},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "planet_info",
            "strict": True,
            "schema": PlanetInfo.model_json_schema(),
        },
    },
)
data = json.loads(response.choices[0].message.content)
planet = PlanetInfo(**data)

模型支援

支援 strict: truejson_schema 模式的模型包括:
  • openai/gpt-5.4, openai/gpt-5.4-pro, openai/o3, openai/o4-mini
  • anthropic/claude-sonnet-4.6, anthropic/claude-opus-4.5
  • google/gemini-2.5-flash, google/gemini-2.5-pro
json_object 模式(無結構強制)的支援範圍更廣。請查看 GET /v1/models 取得最新的能力資訊。

串流結構化輸出

結構化輸出支援串流。JSON 內容增量傳遞,由客戶端進行組裝:
const stream = await client.chat.completions.create({
  model: "openai/gpt-5.4",
  messages: [{ role: "user", content: "Tell me about Mars." }],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "planet_info",
      strict: true,
      schema: { /* ... */ },
    },
  },
  stream: true,
});

let jsonBuffer = "";
for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) jsonBuffer += content;
}

const data = JSON.parse(jsonBuffer);

最佳實踐

  1. 使用 strict: true — 這可以保證結構遵從性。否則模型可能回傳不完全符合結構的有效 JSON。
  2. 設定 additionalProperties: false — 嚴格模式必須設定。防止模型新增額外的鍵。
  3. 明確列出所有必填欄位 — 在嚴格模式下,properties 中的每個欄位都應在 required 中。
  4. 包含系統提示 — 告知模型作為資料提取或結構化輸出助手的角色,可提高可靠性。

錯誤處理

如果模型無法生成與您的結構匹配的有效 JSON(如提示與結構根本衝突),回應的 finish_reason 將為 "length""content_filter"。解析內容前始終檢查 finish_reason
response = client.chat.completions.create(...)

choice = response.choices[0]
if choice.finish_reason != "stop":
    raise ValueError(f"Unexpected finish reason: {choice.finish_reason}")

data = json.loads(choice.message.content)