AI 開發

AI 為什麼需要 JSON Schema?Structured Output、Function Calling 與 JSON Schema 完整解析

閱讀約 14 分鐘

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」,職責就丟了。拆開之後:

  1. JSON Schema 是共用口徑:欄位詞典、必填、列舉。它不呼叫、不生成,只描述「合法長什麼樣」。
  2. Function Calling 是向外伸手。模型請求一次副作用,參數必須過 Schema,結果最好也過 Schema。
  3. 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 各手寫一份「差不多」的定義。推薦形狀是一份原始檔,兩套匯出:

  1. 倉庫裡放 schemas/order.json,帶穩定 $id 和版本。
  2. 匯出 Function Calling:裁成 function.parameters,開啟 strict。查單工具往往只需要 order_id,不要把整份訂單物件塞進參數。
  3. 匯出 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。模型會換,協議會變,直徑應該先穩定。

延伸閱讀

更新記錄: 首次釋出