2026 年大半的 production LLM 應用要模型吐特定形狀的 JSON,不是自由文字。從履歷抽欄位、把客戶意圖分到 12 類之一、生成有命名步驟的行動計畫。三年前你只能對自由文字跑 regex 然後祈禱,現在有三個真實選項,各有取捨。
三條路
- JSON mode。 跟模型說「輸出有效 JSON」,讓它自己想形狀。便宜、低額外負擔、沒 schema 強制。
- Tool use / function calling。 定義一個工具,給它的參數 JSON schema。即使工具是虛構的,模型被強制吐出符合 schema 的參數(配合 strict mode)。
- 約束解碼 / strict 結構化輸出。 廠商在 sampling 時強制 schema 合規 —— 不合 schema 的 token 直接 mask 掉,模型字面意義不能吐出無效 JSON。
2026 年三種在主要廠商都有,但功能名跟限制不太一樣。
JSON mode:最簡單的工具
response = client.chat.completions.create(
model="gpt-5",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "You output a JSON object with fields name, email, role."},
{"role": "user", "content": "..."},
],
)
模型吐某個 JSON 物件,你 parse 它。system prompt 把 schema 講清楚通常會中。
強項: 最便宜、普遍支援、好設定。
弱項:
- Schema 強制是 best-effort,模型可能漏欄位或加多餘的。
- 沒型別保證。「age」可能回
"forty"不是40。 - 你還是要在下游寫 Pydantic / zod 驗證。
Schema 簡單、成本要省、下游能驗證 —— 用 JSON mode。
Tool use:schema 強制 JSON
定義一個其實不是工具的工具,只是用來約束輸出:
tools = [{
"type": "function",
"function": {
"name": "submit_extraction",
"description": "提交抽取出的欄位。",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0, "maximum": 120},
"role": {"type": "string", "enum": ["engineer", "designer", "manager", "other"]}
},
"required": ["name", "email", "role"],
"additionalProperties": False
}
}
}]
response = client.chat.completions.create(
model="gpt-5",
tools=tools,
tool_choice={"type": "function", "function": {"name": "submit_extraction"}},
messages=[...],
)
args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
強項:
- 開
strict: True,OpenAI 保證輸出符合 schema。沒無效 JSON、沒缺欄位、沒型別錯。Anthropic 跟 Gemini 有類似 strict mode。 - enum 限制特定值。
role永遠不會回"chief vibemaster"。 - Schema 是模型輸入的一部分 —— 比「system prompt 說輸出 X」遵守得更好。
弱項:
- Strict 加延遲(廠商每次 request 把 schema 編譯成約束解碼狀態機),通常慢 10-20%。
- Strict 有微妙限制:root 不能
oneOf、所有欄位必須required、遞迴 schema 受限。讀廠商文件。 - 強迫 tool-call 回應形狀,你要去 args 挖出來。
2026 年 production 抽取的對的預設值。
約束解碼(strict 結構化輸出)
OpenAI 的 response_format={"type": "json_schema", "strict": true, ...} 跟 Anthropic / Gemini 對應的版本,是 tool-use-as-extraction 的乾淨版:
response = client.chat.completions.create(
model="gpt-5",
response_format={
"type": "json_schema",
"json_schema": {
"name": "extraction",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"role": {"type": "string", "enum": ["engineer", "designer", "manager"]}
},
"required": ["name", "role"],
"additionalProperties": False
}
}
},
messages=[...],
)
result = json.loads(response.choices[0].message.content)
跟 strict tool use 一樣的保證,但 response 是普通 content(不是 tool call)。概念上不需要「函式」包裝就用這個更乾淨。
自架(vLLM、llama.cpp)透過 Outlines、llguidance、XGrammar 等函式庫實作,在 sampling 時 mask 無效 token。一樣保證,不打廠商 API。
開 strict 之後實際發生什麼
模型字面意義不能吐無效 JSON。decoder 每個 token step,看所有 20 萬個 vocabulary token,檢查哪些能讓輸出在已 emit 的 token 跟 schema 下保持有效,只允許從有效 token 採樣。
這代表:
- schema 寫 integer,你永遠不會拿到
"age": "forty"。 - 永遠不會缺 required 欄位。
- 永遠不會 JSON 語法錯。
- 還是可能拿到語意錯的資料 ——
"age": 999是有效 integer,只是錯。
驗證是結構性的,不是事實性的。你還是要 sanity check 範圍、商業規則、內容品質。
production 上的五個驚喜
- Strict 對複雜巢狀 schema 失敗。 有些 schema 廠商編譯不了(深巢狀、遞迴、太多 enum 值)。你會看到 400 錯誤。要嘛簡化,要嘛 fall back 到沒 strict 的 tool use。
- 延遲負擔是真的。 簡單抽取從 200ms 變 280ms。批次抽取就重要了。
- 模型偶爾會拒答。 「我無法從輸入判斷 X」—— 它必須符合 schema,但 schema 要求模型推不出的欄位,可能吐 placeholder 或呼叫失敗。加
"unknown"enum 值或讓選填欄位真的選填。 - 跨廠商 schema 漂移。 OpenAI、Anthropic、Gemini 的 schema 方言略不同。OpenAI 過的 schema Anthropic 可能失敗。換廠商要測。
- 長 enum 的成本。 200 個 enum 值的
role欄位讓 schema 膨脹加延遲。選項多就考慮自由 string + 下游驗證,或拆成 category + subcategory。
怎麼挑
- 一次性原型、schema 簡單、會下游驗證: JSON mode。出貨最快。
- Production 抽取、不能壞: strict tool use 或 strict
response_formatjson_schema。schema 是你的合約。 - Schema 巨大或複雜(很多 enum、巢狀、遞迴): 沒 strict 的 tool use,下游用 Pydantic / zod 驗。99% 遵守而且避免廠商編譯錯。
- 自架想要保證: Outlines 或 XGrammar 加 vLLM。Strict 沒每呼叫成本。
- 階層任務(計畫 + 步驟 + 每步驟參數): Tool use,每個「工具」代表一個步驟類型。模型吐一連串 tool call。
函式庫輔助
2026 年熱門 wrapper:
- Pydantic + Instructor(Python)。
instructor.from_openai(client)給你client.chat.completions.create(response_model=MyModel, ...),回 typed Pydantic 物件。藏掉 strict mode 接線。 - zod-to-json-schema(TypeScript)。 zod schema 轉 JSON schema,送 API,response 過 zod 解。雙向型別安全。
- Outlines(Python)。 本地模型或想完全控制約束演算法時。
大部分團隊:Instructor(Python)或 zod(TypeScript)疊在 strict API 上。除非你在做奇怪的事,別自己寫驗證層。
什麼時候不要用結構化輸出
結構化輸出對抽取、分類、計畫生成很棒,對這些不好:
- 長篇自然語言輸出。 別硬塞文章到
{"text": "..."}schema,直接用純文字。 - 創意寫作。 Schema 約束降低創意,可量測微小;在乎品質就跳過。
- 使用者可見 UI 的 streaming。 Strict 結構化輸出 streaming 不順。聊天 UI 純文字比較友善。
下一步
- OpenAI 結構化輸出文件。
- Anthropic tool use 文件。
- Outlines 函式庫(開源結構化生成)。
- Efficient Guided Generation for Large Language Models(Willard & Louf, 2023)。
- 查這些詞:JSON Schema、Pydantic、Instructor library、XGrammar。