OpenAI 相容 API 的結構化輸出:JSON Schema 指南

說明 Responses 與 Chat Completions 如何使用嚴格 JSON Schema,並涵蓋應用驗證、拒絕、截斷與路由相容測試。

OpenAI 相容 API 的結構化輸出: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。

無論使用哪個端點:

  1. 只保留一份版本化 JSON Schema 真相來源;
  2. 轉成端點外層封裝時不改變語意;
  3. 完成後再次驗證回應;
  4. 結構驗證後才套用確定性的商業規則;
  5. 分開監控解析、拒絕、截斷與相容性失敗。

初次遷移可先閱讀 OpenAI 相容 API 指南,串流與不完整事件可參考 AI API 串流指南,並在模型與價格確認目前模型範圍,再針對 API 金鑰實際使用的路由驗證 Structured Outputs。

Structured Outputs 的價值,是把模型回應收斂成可靠的應用介面;它不會取代事實查核、授權與計費邏輯。「OpenAI 相容」仍是必須逐項測試的主張,而不是從基礎 URL 就能推導的保證。