Kvmzen 部落格
← 返回技術實踐

OpenAI Structured Outputs 完整指南:如何讓 GPT 穩定輸出符合 JSON Schema 的 JSON 資料?

技術實踐 ·約 12 分鐘閱讀

在程式碼編輯器中撰寫 JSON Schema 與結構化資料管線

只要你把模型輸出接到資料庫、表單或下游 Agent,就不能再靠「提示詞裡寫請回傳 JSON」碰運氣。OpenAI Structured Outputs 用受限解碼把輸出釘在你提供的 JSON Schema 上:欄位不會丟、列舉不會亂編、型別不會在字串和數字之間漂移。本文面向要做抽取、工單分類、評測打分和多步工作流程的工程師,走完從 JSON Mode 升級到 strict: true 的完整路徑。

100%
受支援模型上的 Schema 符合率
2
接入入口:Chat Completions / Responses
10
物件最大巢狀層數

JSON Mode 為什麼不夠穩

JSON Mode(type: "json_object")只保證「能被 JSON 解析器讀進去」。它不保證鍵名、必填欄位、列舉集合或數字型別。生產裡最常見的三種翻車是:漏掉 severity、把 42 寫成 "42"、在 enum 外發明一個 urgent-plus。下游一旦用強型別解析,整條管線就會在隨機樣本上失敗。

能力 JSON Mode Structured Outputs
輸出合法 JSON
遵守你給的 JSON Schema 否(靠提示詞) 是(受限解碼)
開啟方式 json_object json_schema + strict: true
典型模型 較早期 GPT-4o / GPT-3.5 等 gpt-4o-2024-08-06、gpt-4o-mini 及更新快照
拒答處理 仍可能吐出「看起來像 JSON」的拒絕話術 走獨立的 refusal 欄位

官方把 Structured Outputs 稱為 JSON Mode 的演進:新專案應預設走 Schema,而不是在解析失敗後再重試三次。若你同時在比較不同模型的 Token 帳單,可以對照 Kimi K3 與 GPT-5.5 的 API 成本對比——結構化輸出本身幾乎不增加輸出體積,真正拉開帳單的是重試和過長推理。

兩個入口,同一套 Schema 規則
Chat Completions 把 Schema 放在 response_format;Responses API 放在 text.format。欄位路徑不同,strictadditionalProperties: false 和「全部 required」這些約束完全一樣。

最小可運行請求

下面這個工單抽取 Schema 涵蓋了生產裡 80% 的需求:字串、列舉、整數,以及用 null 聯合型別模擬可選欄位。模型必須回傳所有鍵;沒有帳號 ID 時明確給 null,而不是省略鍵。

Chat Completions · response_format
{
  "model": "gpt-4o-2024-08-06",
  "messages": [
    {"role": "system", "content": "把使用者描述抽取成工單物件。"},
    {"role": "user", "content": "結帳頁用已儲存卡會 500。今天開始。帳號 acct_8842。"}
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "support_ticket",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "summary": { "type": "string" },
          "category": {
            "type": "string",
            "enum": ["billing", "bug", "account", "other"]
          },
          "severity": { "type": "integer" },
          "account_id": { "type": ["string", "null"] }
        },
        "required": ["summary", "category", "severity", "account_id"],
        "additionalProperties": false
      }
    }
  }
}

Responses API 的等價寫法是把同一份 Schema 放進 text.formattypenameschemastrict 作為兄弟欄位。首次用某個 Schema 時,伺服器會編譯約束,延遲可能略高;之後同一 Schema 會被快取,後續請求回到正常水準。

筆電上的程式碼與結構化 API 除錯環境
把 Schema 當成介面契約:先在本機用同一份定義跑單元測試,再交給模型產生

strict Schema 必須滿足的規則

開啟 strict: true 之後,你提交的不再是「寬鬆 JSON Schema」,而是官方支援的子集。不合規會直接 400,而不是「盡量遵守」。

  • 根必須是 object:根節點不能是 anyOf 或陣列。Zod 的 discriminatedUnion 若產生頂層 anyOf,需要包一層物件,例如 { "result": ... }
  • 每個 object 都要 additionalProperties: false:包含巢狀物件。漏一個就會被拒絕。
  • properties 裡出現的鍵必須全部進入 required:可選語意用 "type": ["string", "null"]anyOfnull
  • 支援的型別:string、number、integer、boolean、object、array、enum、anyOf
  • 字串約束patternformatemaildate-timeuuidipv4 等)。
  • 數字與陣列minimum / maximum / multipleOfminItems / maxItems
  • 明確不支援allOfnotif/then/elsedependentRequired 等組合關鍵字。
  • 規模上限:合計最多約 5000 個物件屬性、10 層巢狀;所有屬性名、定義名、列舉值字串總長不超過 12 萬字元;列舉值總數最多 1000。
微調模型更嚴
在微調模型上,patternformatminLengthminimumminItems 等約束目前仍可能不受支援。先在基礎快照上驗證 Schema,再決定是否微調。

工具呼叫 vs 回覆正文

同一套 strict 也能用在函式 / 工具參數上。差別是意圖:工具 Schema 描述「模型要呼叫什麼」;response_format / text.format 描述「模型要回覆使用者什麼形狀」。抽取、評分、產生 UI 狀態用後者;查天氣、寫檔案、跑指令用前者。若你在對比終端 Agent 與 GUI 操控能力,可參考 Claude Code 與 OpenAI Computer Use 的關係——那是另一層「怎麼行動」,本文解決的是「行動前後的資料契約」。

用 SDK 直接解析成物件

手寫 JSON Schema 容易漏 required。官方 SDK 提供 Pydantic / Zod 助手:從型別產生 Schema,並在回應側直接給出解析後的物件。下面是 Python 的典型寫法。

Python · Pydantic
from typing import Literal, Optional
from pydantic import BaseModel
from openai import OpenAI

class SupportTicket(BaseModel):
    summary: str
    category: Literal["billing", "bug", "account", "other"]
    severity: int
    account_id: Optional[str]

client = OpenAI()
completion = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "抽取工單。"},
        {"role": "user", "content": "結帳頁 500,帳號 acct_8842。"},
    ],
    response_format=SupportTicket,
)
ticket = completion.choices[0].message.parsed
if completion.choices[0].message.refusal:
    raise RuntimeError(completion.choices[0].message.refusal)
print(ticket.category, ticket.severity)
把 Schema 當成單一事實來源
同一份 Pydantic / Zod 模型同時服務 API 請求、單元測試和下游校驗。不要在提示詞裡再抄一遍欄位清單,否則文件會先於程式碼腐爛。

報錯、拒答與工程清單

上線前按這張清單過一遍,能消掉絕大多數「本機 curl 能跑、管線卻 400」的問題。

  • 400 Unsupported schema:缺 additionalProperties: false、有未 required 欄位、根是 anyOf,或用了 allOf
  • 模型拒答:安全策略觸發時,內容不會塞進 Schema;讀取 refusal,在 UI 裡展示,不要當 JSON 解析失敗重試。
  • 可選欄位變成空字串:契約裡寫了 null 就要接受 null;下游入庫前把 null 對應成 SQL NULL,不要再二次猜測。
  • 列舉過寬:把業務狀態收成 5–8 個值;上百個列舉既燒上下文,也逼近 1000 條上限。
  • 鍵順序:輸出鍵順序與 Schema 中宣告順序一致,串流消費時可以按欄位增量解析。
  • 提示詞仍有用:Schema 管形狀,提示詞管語意。例如「severity 1–5,5 為全站故障」要寫在 system 裡,整數範圍也可以用 minimum / maximum 再鎖一層。

落地時建議固定三件事:Schema 進 Git;對拒答、400、逾時分別打點;用真實工單做一組回歸樣本,而不是只用「Hello 回傳 JSON」這種玩具輸入。結構化輸出讓解析層穩定之後,你才能把精力放到分類準確率和延遲上,而不是每週修一次正規表示式。

在 Mac mini 上跑抽取管線,環境更省心

Structured Outputs 的除錯循環很短:改 Schema、跑一小批樣本、看解析物件。macOS 上 Python、Node.js、Docker 和 Homebrew 開箱即用,不必先搭 WSL。Apple Silicon 統一記憶體讓你同時開編輯器、本機校驗腳本和長上下文評測,不至於在型別檢查時把機器拖進 swap。

Mac mini M4 待機大約 4W,適合把回歸樣本集整晚掛著跑;macOS 崩潰率低,Gatekeeper 與 SIP 也降低了相依腳本被污染的風險。和同價位 Windows 盒子比,長期無人值守跑 Agent 評測時,穩定性和電費都更友善。

若你希望有一台一直在線、專門跑 JSON 回歸與模型對比的節點,了解 Kvmzen 的套餐方案,把 Schema 契約從筆電搬到固定規格的雲端 Mac 上。

限時特惠

不只是一台 Mac,是你在雲端的開發基地

獨享算力 · 全球節點 · 按月訂閱 · 無需購置硬體

返回首頁
限時優惠 點擊查看套餐