AI 開發

AI Agent 為什麼開始需要 JSON Schema?從 Tool Calling 到結構化 Agent 工作流

閱讀約 15 分鐘

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 年真正在長的三層。完整圖是:

  1. 工具入參。 Function Calling 的 parameters、MCP 的 inputSchema。決定模型「能請求什麼」。
  2. 工具出參。 查詢、寫入、外部 API 的回傳物件。決定下一跳「能相信什麼」。這一層現在最常缺。
  3. Agent 交接(handoff)。 分類結果、意圖、信心分數、要交給誰。Agents SDK 裡這是路由依據,不是聊天摘要。
  4. 工作流狀態與評測。 檢查點、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。模型會換,協議會改,口徑應先穩定。

相關文章

更新日誌: 初稿發布