OpenAI 相容 API 的結構化輸出:JSON Schema 指南
說明 Responses 與 Chat Completions 如何使用嚴格 JSON Schema,並涵蓋應用驗證、拒絕、截斷與路由相容測試。

Structured Outputs 讓應用程式要求模型回傳符合既定結構描述的 JSON,而不只是用提示詞要求「請輸出 JSON」。在 OpenAI 相容 API 中,同一份 JSON Schema 可用於 Responses 或 Chat Completions,但外層欄位不同,而且必須針對實際模型與路由驗證支援程度。
安全的生產模式有三層:模型支援時使用 strict 結構描述、應用端再次解析與驗證 JSON,最後再套用商業規則。符合結構描述能消除大量格式錯誤,但不能證明內容在事實或語意上正確。
Structured Outputs 不等於 JSON 模式
| 方法 | 產生有效 JSON | 強制符合結構描述 | 適用情境 |
|---|---|---|---|
| 只靠提示詞 | 不保證 | 否 | 可以接受解析失敗的原型 |
| JSON 模式 | 支援且完整生成時是 | 否 | 結構彈性、由應用程式驗證的 JSON |
strict: true Structured Outputs |
須處理完成狀態與拒絕 | 在支援的 JSON Schema 子集中是 | 型別化擷取與應用流程 |
OpenAI Structured Outputs 指南建議在模型與端點支援時優先使用 Structured Outputs。若模型應回覆可預測的資料物件,使用結構化回應格式;若模型要請求應用程式執行動作,則使用 Function Calling。兩者都可使用嚴格結構描述,但呼叫識別碼與結果訊息屬於不同協定。
先定義資料結構,再選擇端點
假設客服分流需要 category、priority、requires_human 與 summary:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "account"]
},
"priority": {
"type": "integer",
"minimum": 1,
"maximum": 3
},
"requires_human": {
"type": "boolean"
},
"summary": {
"type": "string"
}
},
"required": ["category", "priority", "requires_human", "summary"],
"additionalProperties": false
}
將所有欄位列為必填,並設定 additionalProperties: false,能讓下游程式取得穩定物件。但這不代表所有 JSON Schema 關鍵字都可攜。OpenAI 支援文件列出的子集;其他 OpenAI 相容供應商可能支援不同子集,甚至完全沒有嚴格模式。
使用 Responses API 傳送結構描述
Responses API 將結構描述放在 text.format:
export MODELFLARE_API_KEY="<YOUR_API_KEY>"
export MODEL_ID="<MODEL_WITH_VERIFIED_STRUCTURED_OUTPUT_SUPPORT>"
curl -sS https://modelflare.dev/v1/responses \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"$MODEL_ID\",
\"input\": \"客戶被重複扣款,並要求退款。\",
\"text\": {
\"format\": {
\"type\": \"json_schema\",
\"name\": \"support_ticket\",
\"schema\": {
\"type\": \"object\",
\"properties\": {
\"category\": {
\"type\": \"string\",
\"enum\": [\"billing\", \"technical\", \"account\"]
},
\"priority\": {
\"type\": \"integer\",
\"minimum\": 1,
\"maximum\": 3
},
\"requires_human\": { \"type\": \"boolean\" },
\"summary\": { \"type\": \"string\" }
},
\"required\": [\"category\", \"priority\", \"requires_human\", \"summary\"],
\"additionalProperties\": false
},
\"strict\": true
}
}
}"
呼叫 POST https://modelflare.dev/v1/responses 時,使用 Authorization: Bearer <YOUR_API_KEY> 與 Content-Type: application/json。Responses 的生成內容位於帶型別的輸出項目中。SDK 可能提供聚合文字或已解析輸出的輔助函式,但直接使用 HTTP 時,不應假設結構化物件位於最上層;要先讀取完整回應、找到文字輸出,再執行 JSON 解析。
使用 Chat Completions 傳送同一份結構描述
Chat Completions 將相同結構描述放在 response_format.json_schema:
curl -sS https://modelflare.dev/v1/chat/completions \
-H "Authorization: Bearer $MODELFLARE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"model\": \"$MODEL_ID\",
\"messages\": [
{
\"role\": \"user\",
\"content\": \"客戶被重複扣款,並要求退款。\"
}
],
\"response_format\": {
\"type\": \"json_schema\",
\"json_schema\": {
\"name\": \"support_ticket\",
\"schema\": {
\"type\": \"object\",
\"properties\": {
\"category\": {
\"type\": \"string\",
\"enum\": [\"billing\", \"technical\", \"account\"]
},
\"priority\": {
\"type\": \"integer\",
\"minimum\": 1,
\"maximum\": 3
},
\"requires_human\": { \"type\": \"boolean\" },
\"summary\": { \"type\": \"string\" }
},
\"required\": [\"category\", \"priority\", \"requires_human\", \"summary\"],
\"additionalProperties\": false
},
\"strict\": true
}
}
}"
Chat Completions 通常在 choices[0].message.content 回傳 JSON 文字;它仍是字串,必須解析。
| 用途 | Responses API | Chat Completions |
|---|---|---|
| 結構描述容器 | text.format |
response_format.json_schema |
| 格式類型 | text.format.type |
response_format.type |
| 結構描述名稱 | text.format.name |
response_format.json_schema.name |
| 結構描述本文 | text.format.schema |
response_format.json_schema.schema |
| 嚴格模式旗標 | text.format.strict |
response_format.json_schema.strict |
| 結果位置 | 帶型別的輸出項目/輔助函式 | choices[0].message.content |
Modelflare 的 OpenAI 相容層會在支援的 OpenAI/Codex 路由中轉換兩種外層封裝,但轉換不能為本身不支援 Structured Outputs 的上游模型創造能力。其他以 Chat Completions 原樣透傳的模型家族,仍以上游供應商的精確協定為準。
分開驗證結構與語意
模型可能回傳:
{
"category": "billing",
"priority": 2,
"requires_human": true,
"summary": "客戶表示遭到重複扣款,並要求退款。"
}
結構驗證
使用 JSON Schema 驗證器或型別化 SDK 輔助函式檢查必填屬性、額外欄位、型別、列舉值與數值範圍。即使供應商承諾嚴格符合,應用程式端驗證仍能防範不支援的路由、整合錯誤、截斷與未來協定變更。
語意與商業規則驗證
結構描述無法判斷客戶是否真的重複扣款、priority: 2 是否正確,或退款是否必須人工核准。高影響流程應把模型物件視為建議分類,與權威資料比對,並在確定性程式碼中執行權限、財務限制與安全規則。結構正確的 JSON 絕不能繞過授權或計費邊界。
處理不完整與例外輸出
拒絕
模型可能因安全原因拒絕請求。應先檢查回應狀態與拒絕表示,再尋找 JSON;不要把拒絕誤報為解析錯誤。
截斷與輸出限制
若生成在物件完成前停止,結構描述無法補回缺少的結尾。檢查完成狀態與停止原因,並為最大合法物件設定足夠但有界的輸出限制。
不支援的結構描述關鍵字
嚴格模式通常只支援 JSON Schema 子集。先使用物件、陣列、基本型別、列舉值、必填欄位與明確的額外屬性規則;導入參照、遞迴結構、複雜聯集或進階驗證前,查閱目前供應商文件。
首次使用結構描述的延遲
部分供應商會預處理並快取新結構描述,第一次請求可能較慢。重複使用穩定且版本化的結構描述,不要每次動態產生唯一結構描述;效能比較也要分開首次與穩態資料。
模型或路由不相容
端點可能接受一般 Chat Completions,卻拒絕 json_schema、忽略 strict,或把欄位轉送給不支援的模型。200 加上看似 JSON 的內容仍不是嚴格符合的證明;必須使用邊界與無效輸入驗證。
生產前執行相容性測試
| 測試 | 應保存的證據 |
|---|---|
| 最小必填物件 | 狀態、模型、路由、已解析物件與驗證結果 |
| 每個列舉值 | 能否產生與解析每個允許值 |
| 資訊不足 | 使用有界替代值,或要求澄清 |
| 觸發安全政策 | 拒絕格式與應用處理 |
| 低輸出限制 | 完成狀態與截斷處理 |
| 不支援的結構描述功能 | 明確錯誤或靜默忽略 |
| Responses 與 Chat 外層封裝 | 是否產生等價應用物件 |
| 重複穩定結構描述 | 首次與穩態時間 |
比較模型時必須固定結構描述、提示詞、區域、串流模式與輸出限制,並記錄測試日期,因為模型可用性與供應商行為會改變。
測試工作流程後再選端點
應用程式已採用帶型別的輸出項目、Responses 串流事件或更完整的 Responses 工具流程時,選擇 Responses;既有訊息式整合穩定且路由支援 response_format 時,可選 Chat Completions。更完整的取捨見 Responses API 與 Chat Completions。
無論使用哪個端點:
- 只保留一份版本化 JSON Schema 真相來源;
- 轉成端點外層封裝時不改變語意;
- 完成後再次驗證回應;
- 結構驗證後才套用確定性的商業規則;
- 分開監控解析、拒絕、截斷與相容性失敗。
初次遷移可先閱讀 OpenAI 相容 API 指南,串流與不完整事件可參考 AI API 串流指南,並在模型與價格確認目前模型範圍,再針對 API 金鑰實際使用的路由驗證 Structured Outputs。
Structured Outputs 的價值,是把模型回應收斂成可靠的應用介面;它不會取代事實查核、授權與計費邏輯。「OpenAI 相容」仍是必須逐項測試的主張,而不是從基礎 URL 就能推導的保證。