AI Agent 為什麼開始需要 JSON Schema?從 Tool Calling 到結構化 Agent 工作流
2024 年以前,多數「Agent」是一段 prompt 加兩三個函式。2026 年不行了:Function Calling 要描述參數,MCP 要註冊工具,Structured Outputs 要約束回傳值,A2A 要協商能力,Agents SDK 要用 handoff 物件做路由。這些層各自獨立演進,卻都落回同一份契約語言——JSON Schema。本文講清它為什麼從「文件裡的校驗語法」變成了 Agent 的執行時,以及從單次 Tool Calling 走到多步工作流時,該把 Schema 寫在哪裡。
閱讀前速覽
| 階段 | 沒有 Schema 時 | 有 Schema 時 |
|---|---|---|
| 單次 Tool Calling | 參數是字串,型別靠運氣,多一個欄位也能跑 | parameters / inputSchema 在呼叫前攔截 |
| 多步 Agent | 下游靠解析散文,handoff 靠「提到了 billing」 | handoff 物件帶 output_type,路由可測試 |
| 跨協議複用 | Function Calling、MCP、A2A 各寫一份,越改越偏 | Git 裡一份 Schema,匯出為三種適配器 |
結論先說:JSON Schema 不是「給模型看的文件」,而是 Agent 宿主在每一步執行前、交接時、評測時都要跑的校驗器。
為什麼是「開始」需要,而不是一直需要?
早期 Tool Calling 能活下來,是因為鏈路短:模型吐一個函式名和一段參數,宿主執行,結果塞回對話。錯了就重試,使用者能看見。Schema 最多寫在 README,屬於「最好有」。
2025–2026 年鏈路變長了。一個工單 Agent 可能先分類,再查訂單,再決定退款或轉人工,再把物件交給另一個 Agent。中間任何一跳如果只是「看起來像 JSON」,後面的步驟會把錯誤當成事實。這時 Schema 從文件變成了執行時門禁:過不了就不往下走。
另一條壓力來自協議層。我們在 MCP Tool Schema vs Function Calling 裡寫過:廠商 API 用 parameters,MCP 用 inputSchema。A2A Extensions 又把能力寫進 Agent Card。三套協議不共享執行時,但都認 JSON Schema 子集。你不在一開始選定 Schema,就會在三個地方各維護一份「幾乎一樣」的定義,然後在第三週發現 order_id 有的是 string、有的是 number。
DevDay 2026 預測 把同一件事說得更直:Structured Outputs、Function Calling、Agent 執行時正在收成同一根 JSON 管。Schema 是這根管的口徑。口徑不定,後面接什麼協議都漏。
Tool Calling 看起來已經有「結構」,缺口在哪?
Function Calling 的 tools[] 裡本來就有一份類似 Schema 的 parameters。很多團隊因此覺得「我們已經結構化了」。生產裡真正出事的,往往是下面四類。
第一,arguments 仍是字串。模型回傳的是:
{
"tool_calls": [{
"id": "call_8f2a",
"type": "function",
"function": {
"name": "create_refund",
"arguments": "{\"order_id\":\"A-1042\",\"amount\":\"99.00\",\"reason\":\"late\"}"
}
}]
}
宿主必須再 JSON.parse 一次。amount 是字串還是數字,reason 是否在列舉裡,多出來的 note 能不能進帳務系統——parameters 若不在宿主側強制執行,這些都靠「模型今天心情好」。這不是 Schema 寫了就自動生效;strict: true 只約束模型側,宿主仍要自己校驗。
第二,工具結果沒有 Schema。role: tool 大多是自由字串。下一步模型會把這段文字再「理解」一遍。查詢成功、庫存為 0、權限不足,三種情況如果外形不同,多步工作流會在第三跳分叉到錯誤分支。
第三,型別寬到失去篩選力。只寫 type: string 的 priority,模型可以輸出 "urgent!!!"。沒有 enum、required、additionalProperties: false 的 Schema,只是一份註解。
第四,單次成功掩蓋了工作流失敗。第一步 lookup_order 多回傳一個 status: "ok",第二步退款 Agent 把 status 當成訂單狀態。本地試一條快樂路徑看不出來,迴歸集裡才會炸。Schema 的價值在這裡:它讓「這一跳能交給下一跳的物件」變成可斷言的測試,而不是對話紀錄。
除錯這類巢狀 JSON 時,先在本地 格式化並做語法檢查,再和上週能跑的定義做 Diff。不要把帶使用者資料或金鑰的 tool 回應貼到公開 JSON 網站。
JSON Schema 在 Agent 裡其實有四層角色
把 Schema 只當成「工具參數說明書」,會漏掉 2026 年真正在長的三層。完整圖是:
- 工具入參。 Function Calling 的
parameters、MCP 的inputSchema。決定模型「能請求什麼」。 - 工具出參。 查詢、寫入、外部 API 的回傳物件。決定下一跳「能相信什麼」。這一層現在最常缺。
- Agent 交接(handoff)。 分類結果、意圖、信心分數、要交給誰。Agents SDK 裡這是路由依據,不是聊天摘要。
- 工作流狀態與評測。 檢查點、trace、eval fixture。長任務要能從 JSON 恢復,而不是從對話紀錄裡猜進度。
入參 Schema 範例(退款工具):
{
"$id": "https://example.com/schemas/tools/create_refund.json",
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
"amount": { "type": "number", "exclusiveMinimum": 0 },
"reason": { "type": "string", "enum": ["late", "damaged", "duplicate", "other"] }
},
"required": ["order_id", "amount", "reason"],
"additionalProperties": false
}
handoff Schema 範例(分診 → 帳務):
{
"$id": "https://example.com/schemas/handoffs/triage_to_billing.json",
"type": "object",
"properties": {
"intent": { "type": "string", "enum": ["refund", "exchange", "human"] },
"order_id": { "type": "string" },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"from": { "const": "triage" },
"to": { "const": "billing_agent" }
},
"required": ["intent", "order_id", "confidence", "from", "to"],
"additionalProperties": false
}
兩份檔案的共同點比差異更重要:都是物件、都有列舉、都禁止多餘欄位、都能進 Git、都能被 ajv(或等價函式庫)在 CI 裡跑。模型換快照、協議換 MCP 還是 Function Calling,校驗器不用重寫。關鍵字怎麼寫,見 JSON Schema 教程。
從單次呼叫到結構化工作流,Schema 卡在哪幾道門
單次 Tool Calling 只有一扇門:參數合不合法。結構化 Agent 工作流至少有四扇。
門 1:選擇工具之前。 目錄超過十個工具後,把整份 Schema 塞進一次請求又貴又容易選錯。先檢索再呼叫時,檢索結果本身也應是帶 Schema 的短名單,而不是一段模型生成的工具介紹。
門 2:執行之前。 parse → validate → execute。validate 失敗就回傳結構化錯誤(缺欄位、型別錯、列舉外),不要把原始字串丟給下一跳。這一步同時擋住 prompt 注入式的額外鍵。
門 3:交接之前。 triage 必須交出符合 handoff Schema 的物件,billing 才啟動。路由條件寫成 intent === "refund" && confidence >= 0.8,而不是「回覆裡出現了退款兩個字」。
門 4:恢復與評測。 長任務把檢查點寫成合法 JSON:
{
"run_id": "run_20260903_014",
"step": "await_refund_ack",
"state": {
"order_id": "A-1042",
"intent": "refund",
"amount": 99.0
},
"schema": "https://example.com/schemas/state/refund_flow.json"
}
評測集也是同一類物件:給定輸入,斷言輸出通過 Schema,並且關鍵欄位相等。Agent「變笨了」時,先 Diff 本週與上週的 Schema 和 fixture,再懷疑模型。很多事故是多了一個 required,或少了一個 enum 值。
一份 Schema,如何餵給 Function Calling、MCP 和 A2A
協議層不要各寫各的。推薦的方向是一份規範檔案,三種投影:
| 協議 | Schema 落點 | 從同一檔案怎麼匯出 |
|---|---|---|
| Function Calling | function.parameters |
複製 type/properties/required,開啟 strict |
| MCP | inputSchema |
同一物件作為 tools/list 的一部分 |
| Structured Outputs | text.format.json_schema |
handoff / 最終回覆用同一套物件定義 |
| A2A | Agent Card / Extension metadata | 對外能力聲明引用同一 $id |
投影時注意子集差異:廠商 strict 模式往往要求所有 property 都在 required 裡,且停用部分 oneOf / $ref。規範檔案可以更完整,匯出器按目標裁剪,並在 CI 裡對「規範 vs 投影」做一次結構 Diff,避免手改某一側。
MCP 負責工具供給,Function Calling 負責模型如何表達呼叫意圖,A2A 負責Agent 之間如何發現和協商。Schema 不替代這三層,它是三層共用的欄位字典。這一點和前兩篇的結論一致,只是把「字典」從概念落成倉庫裡的檔案。
落地:把 JSON Schema 當成單一事實來源
可以立刻做、也不依賴任何一次發表會的幾件事:
- 在倉庫建
schemas/tools/、schemas/handoffs/、schemas/state/。每個檔案有穩定$id和版本。 - 用腳本匯出 Function Calling / MCP / Structured Outputs 三種適配,禁止在適配檔案裡手改欄位。
- CI 用 ajv(或等價函式庫)校驗:範例 payload、歷史 fixture、線上抽樣(去識別化後)必須通過目前 Schema。
- 新工具預設
additionalProperties: false和明確enum。需要開放擴充時,單獨開metadata物件,不要把根物件放寬。 - 工具超過十個,加路由或檢索,不要把目錄整包塞進一次請求。
- 宿主同時接受
arguments為字串或物件,避免廠商把型別收緊後全站告警。 - 含金鑰或使用者資料的 payload 只在瀏覽器本地看:格式化、Diff,零上傳。
目錄可以很薄。重要的是先有檔案,再寫 prompt。prompt 裡的「請回傳 JSON」無法在 CI 裡失敗;Schema 可以。
常見誤區
- 把 Schema 寫進系統提示就算完成。 模型側約束和宿主側校驗是兩件事。沒有宿主校驗,
strict關掉或換模型快照後,契約就消失。 - 只校驗入參,不校驗出參和 handoff。 多步 Agent 的事故更多發生在「上一步回傳了意料外的形狀」。
- 為每個協議手寫一份「差不多」的 Schema。 第三週一定分叉。匯出,不要複製。
- 用超寬
type: object逃避設計。 開放物件等於沒有門。真正要擴充,用版本號或明確metadata。 - 把 Schema 當成模型訓練語料。 它首先是給校驗器和測試跑的。描述寫清楚有助於選工具,但不能代替
enum和required。 - 在視覺化編排裡改欄位,卻不回寫 Git。 畫布上的節點不是事實來源。先匯出 Schema,再讓畫布讀檔案。
FAQ
沒有 Schema,Tool Calling 能不能上線?
原型可以。工具固定、步驟少於三、失敗可由人看見時,字串參數往往夠用。一旦出現分支、自動重試或第二個 Agent,沒有 Schema 的錯誤會靜默傳播。
JSON Schema 會不會被 Structured Outputs 或 MCP 取代?
不會。它們是消費 Schema 的層,不是另一套型別系統。2026 年的趨勢是更多協議引用同一份 Schema,而不是發明新的欄位語言。
該用 draft-07 還是 2020-12?
跟你的校驗函式庫和廠商子集走。Agent 場景真正用到的是 type、properties、required、enum、additionalProperties。選一個能在 CI 和模型 API 之間對齊的子集,比追最新 draft 更重要。
私有內部 API,還要 MCP 和 A2A 嗎?
不一定。內部閉環用 Function Calling + Schema 即可。預留 MCP / A2A 的價值在於:當你把同一份 Schema 寫進倉庫後,以後要插協議只是加匯出器,不用重新發明欄位。
怎麼除錯失敗的 tool / handoff JSON?
在瀏覽器本地格式化、對照 Schema 看缺了哪個 required,再和上一版 Diff。JSONSort 不上傳資料。不要把含金鑰的回應貼到公開站點。
結論
AI Agent「開始」需要 JSON Schema,不是因為模型突然讀不懂自然語言,而是因為工作流變長、協議變多、錯誤必須在宿主側被攔住。Tool Calling 給出呼叫意圖;Structured Outputs 給出最終物件;MCP 和 A2A 給出供給與協商。把這幾層黏在一起的,是倉庫裡那一組可以校驗、可以 Diff、可以匯出的 Schema 檔案。
從今天起與其先寫一段更長的 prompt,不如先寫 schemas/tools/create_refund.json。模型會換,協議會改,口徑應先穩定。
相關文章
- JSON Schema 是什麼?用法與教程 關鍵字、required 與 additionalProperties
- MCP Tool Schema vs Function Calling 工具 JSON 的協議邊界
- A2A Extensions 與 JSON 協議 多 Agent 互操作的第三層
- OpenAI DevDay 2026 重點預測 三層能力如何收成一根 JSON 管
- JSONSort 本地 JSON 工具箱 格式化、語法校驗、Diff
- 部落格全部文章 更多 JSON 與 AI 開發筆記
更新日誌: 初稿發布