AI 為什麼需要 JSON Schema?Structured Output、Function Calling 與 JSON Schema 完整解析
2024 年起,產品頁把 JSON Schema、Function Calling、Structured Output 寫成同一件事。它們不是。Schema 是契約語言,Function Calling 是「請宿主執行」,Structured Output 是「模型必須吐出這個形狀」。本文把三個詞拆開,再說明為什麼機率模型必須接到一份可校驗的 Schema 上,而不是一句「請返回 JSON」。
閱讀前速覽
| 概念 | 一句話 | 落在哪 |
|---|---|---|
| JSON Schema | 欄位、型別、必填、列舉的契約,不是模型功能 | git 裡的 .json,CI 裡的 validator |
| Function Calling | 模型提出呼叫,宿主執行,常有副作用 | tools[] / arguments |
| Structured Output | 模型最終回覆必須符合這份形狀,通常無副作用 | json_schema / output_type |
先記住分工:Schema 是口徑,Function Calling 是伸手,Structured Output 是交回物件。把三者寫成「讓模型輸出 JSON」,下一跳就會在 parse 散文。
三個詞為什麼總被混用?
文件、SDK 示例和推文喜歡把它們疊成一句話:給模型一份 Schema,它就會輸出合法 JSON。演示裡確實經常成功。線上事故通常發生在你以為「已經結構化」之後。
混用會掩蓋三層完全不同的工作:
- Function Calling 的
arguments仍常是字串。 宿主必須再JSON.parse,型別、列舉、多餘欄位都還沒被攔住。 - Structured Output 只管最終回覆。 它不替你查訂單、不打款、不寫資料庫。有副作用的動作仍走 Function Calling。
- Schema 如果只寫在 prompt 裡,CI 測不了。 「請返回 JSON」不能失敗;一份進 git 的 Schema 可以。
所以問題不是「AI 會不會 JSON」,而是:誰在什麼時刻,用哪一份契約,攔住非法物件。三個詞回答的是三個不同的誰。
JSON Schema 是什麼(先分清:它不是模型功能)
JSON Schema 是一份描述 JSON 形狀的文件:有哪些鍵、各是什麼型別、哪些必填、列舉取哪些值、能不能多出欄位。它比「看起來像 JSON」嚴得多,也比 TypeScript 型別或 protobuf 更常被模型 API 直接引用。
Agent / Structured Output 真正用到的,通常就這幾個關鍵字:
type/properties:物件長什麼樣。required:缺了哪個鍵就不算過。enum/pattern/ 數值上下限:把「差不多」收成有限集合。additionalProperties: false:多餘鍵直接失敗,擋住 prompt 注入帶進來的額外欄位。
語法細節見 JSON Schema 教程。這裡只要先記住:Schema 先給 validator 和測試看。清晰的 description 能幫模型選對工具,但不能代替 enum 和 required。
{
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
"amount": { "type": "number", "exclusiveMinimum": 0 },
"status": { "type": "string", "enum": ["paid", "refunded", "pending"] }
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
上面這份訂單契約可以同時餵給 Function Calling 的 parameters、Structured Output 的 json_schema,以及宿主裡的 ajv。改欄位只改這一份。
Function Calling 是什麼?
Function Calling(以及更泛的 Tool Calling)不是「模型去執行函式」。模型只做一件事:發出函式名和參數。宿主 parse、校驗、執行,再把結果塞回對話。查庫存、退款、寫工單——副作用在這一層。
廠商把工具定義寫成 tools[],其中的 parameters 就是一份 Schema 子集:
{
"type": "function",
"function": {
"name": "lookup_order",
"description": "Look up an order by ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^A-[0-9]{4,}$"
}
},
"required": ["order_id"],
"additionalProperties": false
},
"strict": true
}
}
AI Agent 完整指南 把這一跳畫進完整迴圈;MCP Tool Schema vs Function Calling 說明 MCP 的 inputSchema 和廠商 parameters 是同一本字典的兩種寫法。
看起來已經「結構化」了。缺口在於:arguments 經常仍是字串;工具返回值往往沒有 Schema;strict 只約束模型側。沒有宿主校驗,amount 是數字還是字串,仍然看今天的快照心情。
Structured Output 是什麼?
Structured Output(Structured Outputs / JSON mode 的嚴格形態)約束的是模型自己的最終訊息。分類、抽取、摘要物件、Agent 的 handoff——這些不需要外部副作用,但下游必須拿到固定形狀。
各家欄位名不一樣:response_format、text.format.json_schema、output_type。核心相同:把一份 Schema 交給解碼器,非法 token 路徑直接剪掉。
{
"type": "json_schema",
"json_schema": {
"name": "order",
"strict": true,
"schema": {
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" },
"status": {
"type": "string",
"enum": ["paid", "refunded", "pending"]
}
},
"required": ["order_id", "amount", "status"],
"additionalProperties": false
}
}
}
DevDay 2026 預測 把這一層和 Function Calling、Agent 執行時寫成同一根 JSON 管。口徑仍是 Schema:管子的直徑先定,協議才能接上去。
開啟 strict: true 之後,漏欄位、列舉寫錯、多出鍵,常常在模型側就被攔住。它替代的是「請用 JSON 回答」那句 prompt,不替代宿主再驗一次,更不替代 Function Calling。
三者怎麼疊在一起?
把三個詞壓成「讓模型輸出 JSON」,職責就丟了。拆開之後:
- JSON Schema 是共用口徑:欄位詞典、必填、列舉。它不呼叫、不生成,只描述「合法長什麼樣」。
- Function Calling 是向外伸手。模型請求一次副作用,參數必須過 Schema,結果最好也過 Schema。
- Structured Output 是向回交物件。模型結束這一跳時,回覆本身必須是那份形狀。
一張對照表,避免下次再把它們寫成同義詞:
| 層 | 模型在做什麼 | 沒有 Schema 會怎樣 |
|---|---|---|
| Function Calling | 提議呼叫,等待宿主 | 參數是散文;額外來鍵可能打進帳單系統 |
| Structured Output | 直接生成最終物件 | 下游用正則從段落裡摳欄位 |
| JSON Schema | 什麼都不做,只當尺子 | 尺子若只存在於 prompt,CI 量不到 |
多步 Agent 把兩層都用上:先 Structured Output 做分診物件,再 Function Calling 查單、退款。AI Agent 與 JSON Schema 工作流 寫的是門禁怎麼排;本文只把三塊積木先認清。
AI 為什麼離不開 JSON Schema?
因為模型和程式不是同一種東西。模型按機率續寫 token;程式按確定規則讀物件。中間如果只靠「它今天寫得像 JSON」,漏欄位、數字變字串、列舉寫成 "Paid"、note 多出來——每一種都可能被下一跳當成事實。
2024 年以前,鏈路短:一次聊天、一次函式、失敗了人能看見。2026 年的鏈路更長:分類 → 查單 → 決定退款或轉人工 → 把物件交給另一個 Agent。任何一跳只是「像 JSON」,後面的步驟都會把錯誤固化。
同一份訂單,模型隨時可能吐出這種「看起來合法」的物件:
{
"order_id": "A-1042",
"amount": "99.00",
"status": "Paid",
"note": "please hurry"
}
amount 是字串,status 大小寫不對,還多了 note。沒有 Schema,下游可能把 99.00 當字串拼進 SQL,或把 Paid 當成未知狀態重試。除錯時先在本地 格式化 看清結構,再和上週能過的物件 JSON Diff;不要把帶使用者資料的響應貼到公共 JSON 站點。
Schema 的價值不在「教模型說話」,而在把非法物件擋在宿主門內。呼叫前攔一次,回覆後攔一次,評測再斷言一次。視窗越大、步驟越多,散文錯誤越容易變成「系統裡的事實」。
同一份 Schema,兩種投影
不要為 Function Calling 和 Structured Output 各手寫一份「差不多」的定義。推薦形狀是一份原始檔,兩套匯出:
- 倉庫裡放
schemas/order.json,帶穩定$id和版本。 - 匯出 Function Calling:裁成
function.parameters,開啟strict。查單工具往往只需要order_id,不要把整份訂單物件塞進參數。 - 匯出 Structured Output:同一份物件作為
json_schema/output_type,給抽取、分診、最終回覆用。
投影時注意子集:廠商 strict 常常要求每個 property 都在 required 裡,並禁用部分 oneOf / $ref。原始檔可以更完整,匯出器按目標裁剪;CI 對「源 vs 投影」做結構 Diff,避免有人手改其中一側。
宿主側校驗仍然必要
strict: true 只約束模型解碼。換快照、廠商關掉 strict、arguments 二次 parse、工具自己返回了開放物件——都可能繞過模型側。宿主仍要在三處跑同一份 Schema:
- 執行工具前。 parse → validate → execute。失敗就返回結構化錯誤,不要把原始字串傳給下一跳。
- 收下模型最終物件後。 Structured Output 成功也不等於你的業務規則過了(金額上限、貨幣、許可權)。
- 寫入狀態 / 評測夾具時。 checkpoint 和 eval 必須是合法 JSON,不能從聊天記錄猜進度。
模型側約束和宿主側校驗是兩份工作。關掉 strict 或換一家 API 之後,只留 prompt 裡的 Schema,契約就消失了。
2026 年怎麼落地(先寫檔案,再寫 prompt)
- 在倉庫建
schemas/tools/、schemas/extract/、schemas/state/。每個檔案有$id和版本。 - 先寫 Schema,再寫「請返回 JSON」。prompt 裡的句子過不了 CI。
- 有副作用(查庫、打款、發信)走 Function Calling;分類、抽取、handoff 走 Structured Output。
- 新物件預設
additionalProperties: false加明確enum。要開放擴充套件,單獨加metadata,不要鬆開根物件。 - 工具超過十個就先檢索再呼叫。不要把整本目錄塞進一次請求。
- 金鑰和使用者欄位只在瀏覽器裡看:格式化、JSON Diff,零上傳。
目錄可以很薄。要緊的是檔案先於 prompt。模型還會換,協議還會加;口徑應該先穩定。
常見誤區
- 把 Structured Output 當成 Function Calling。 前者不執行程式碼。要查單、退款,仍需工具層。
- 只在 prompt 寫「請返回 JSON」。 沒有進 git 的 Schema,就沒有可失敗的測試。
- 只校驗工具入參,不校驗模型最終物件。 抽取和分診的事故,往往發生在「看起來已經是 JSON」的回覆上。
- 為每種協議手寫一份差不多的 Schema。 第三週就會分叉。匯出,不要複製。
- 用寬
type: object逃避設計。 開放物件不是門禁。真要擴充套件,用版本號或顯式metadata。
FAQ
Structured Output 能替代 Function Calling 嗎?
不能。Structured Output 約束最終回覆的形狀;Function Calling 請求宿主執行有副作用的動作。抽取訂單欄位用前者,查庫存、退款用後者。兩者應引用同一份 Schema,而不是互相替代。
Function Calling 已經有 parameters,還要不要單獨的 Schema 檔案?
要。parameters 是投影,不是源。單獨檔案才能同時餵給 Structured Output、MCP、評測和宿主 ajv。只改 tools[] 裡的一份,三週後就會和抽取 Schema 對不上。
沒有 Agent,只做一次資訊抽取,需要 Schema 嗎?
更需要。一次抽取的結果如果直接進資料庫或表格,非法物件沒有下一跳可以「再理解一次」。Structured Output + 宿主校驗是最小完整集。
開了 strict: true,還要宿主校驗嗎?
要。strict 管模型解碼;宿主管業務規則、二次 parse、以及廠商關掉 strict 之後的退化路徑。兩道門成本很低,少一道就會在換模型時露出來。
被拒的 JSON 怎麼在本地查?
先格式化看結構,對照 Schema 找缺的 required 或錯的 enum,再和上一版能過的物件 Diff。JSONSort 在瀏覽器本地做這些事,不上傳資料。不要把帶金鑰的響應貼到公共站點。
結論
AI 需要 JSON Schema,不是因為模型突然讀不懂自然語言,而是因為機率輸出必須接到確定的門禁上。Function Calling 表達「去做」;Structured Output 表達「交回這個物件」;Schema 是兩者共用的口徑。
從今天起,先寫 schemas/extract_order.json,再決定呼叫哪一家的 API。模型會換,協議會變,直徑應該先穩定。
延伸閱讀
- AI Agent 為什麼開始需要 JSON Schema 從 Tool Calling 到結構化工作流的門禁
- AI Agent 是什麼?Tool Calling 與 JSON 執行時、工具呼叫與 JSON 契約
- MCP Tool Schema vs Function Calling 工具 JSON 的協議邊界
- JSON Schema 是什麼 required、enum 與 additionalProperties
- JSONSort 本地工具箱 格式化、校驗、Diff
- 全部文章 更多 JSON 與 AI 開發筆記
更新記錄: 首次釋出