AI Agent Tool Calling 為什麼依賴 JSON Schema?參數錯誤、類型錯誤與驗證方法

從 Tool Calling 參數契約出發,說明 JSON Schema 為何是 Agent 工具調用的核心,常見參數/類型錯誤分類,以及 ajv、strict mode、二次校驗與錯誤回灌的完整驗證流水線。

本系列前幾篇已經鋪好背景:JSON Schema、Function Calling 與 MCP 的技術演進講「為什麼會出現」;Tool Calling → MCP 資料流講「位元組怎麼流」;JSON Schema 是否成為標準 Contract講「生態是否收斂」。本文聚焦一個更落地的問題:Tool Calling 為什麼幾乎必然依賴 JSON Schema,以及引數錯誤、型別錯誤該如何分類與驗證。

模型選工具、填引數時,宿主程式不能「相信運氣」——必須在執行前用同一份 Schema 做 fail-fast 校驗。否則一次幻覺引數就可能刪庫、發錯郵件、或把髒資料寫回下一輪對話。結論先行:JSON Schema 是 Tool Calling 裡唯一同時被模型 API、MCP 與宿主執行時共同理解的引數契約;驗證要做在「parse 之後、execute 之前」,並把結構化錯誤回灌給模型重試。

為什麼 Tool Calling 依賴 JSON Schema

Tool Calling(與 Function Calling 同義的資料流)本質是:模型從工具列表裡選一個,並輸出符合約定的 JSON 引數。這裡有三方要握手:

  • 模型 API:OpenAI、Gemini、Anthropic 的 Tools API 均用 JSON Schema 描述 parameters(或等價欄位),部分廠商還在解碼階段用 Schema 約束輸出。
  • MCP:每個 Tool 的 inputSchema 型別即 JSON Schema;Host 對映到模型 API 時常原樣透傳或做子集裁剪。
  • 宿主程式:需要機器可讀、可版本化、可 CI 校驗的契約——JSON Schema 有 ajv、jsonschema(Python)等成熟實現,比「Prompt 裡寫 JSON 格式」可靠幾個數量級。

沒有 Schema 時,宿主只能正則或 Prompt 約定解析 arguments,在規模化 Agent 裡會迅速失控。Schema 提供三件事:形狀(有哪些欄位)、型別(各欄位是什麼型別)、約束(enum、minimum、pattern 等)——這正是執行工具前必須知道的全部語法層資訊。業務語義(「priority=high 是否符合 SLA」)仍要程式碼校驗,但語法層已足夠擋住大部分模型幻覺。

使用者意圖 → 模型讀 tools[] 裡的 JSON Schema
         → 輸出 tool_calls[].function.arguments(JSON 字串)
         → 宿主 JSON.parse + Schema 校驗
         → 透過才呼叫 MCP / HTTP / DB

Schema 在呼叫鏈上的三個位置

位置Schema 作用典型失敗
工具註冊(tools / MCP list)告訴模型「有哪些工具、各要什麼引數」Schema 本身非法、draft 不相容、description 誤導模型
模型輸出(tool_calls.arguments)約束模型生成的引數 JSON缺 required、型別錯、編造假欄位
工具返回(寫回 messages)可選:約束 result 形狀,防髒資料進上下文Server 返回非 JSON、欄位漂移

與Structured Output的區別:Structured Output 約束終端使用者可見的 JSON 回覆;Tool Calling 的 Schema 約束中間步驟的執行引數。兩者可共用同一套 Schema 定義工具(Pydantic / Zod 生成),但校驗時機不同——Tool arguments 必須在每次 tool_calls 後、執行前校驗。

引數錯誤:缺欄位、多欄位、名錯、語法錯

引數錯誤指 JSON 能 parse,或 parse 前就壞了,但不符合 Schema 對「有哪些鍵、是否必填」的約定:

錯誤型別示例Schema 關鍵字處理建議
缺 required 欄位Schema 要求 title,arguments 只有 priorityrequired回灌錯誤,讓模型補全;檢查 description 是否寫清必填
多餘欄位模型編造 urgent: true,Schema 未定義additionalProperties: falseOpenAI strict mode 常強制;非 strict 時宿主應 strip 或 reject
欄位名拼寫錯誤titel 而非 titleproperties 鍵名加強 description;enum 工具名與引數名保持一致
JSON 語法錯誤尾隨逗號、單引號、未閉合括號(parse 層)先 JSON.parse,失敗則整段回灌;考慮 Structured Output 降低語法錯
空 arguments{} 但 Schema 有 requiredrequired + minProperties無參工具應顯式 properties: {} 且 required 為空
// Schema 片段
{
  "type": "object",
  "properties": {
    "ticket_id": { "type": "string", "description": "工單 ID" },
    "note": { "type": "string" }
  },
  "required": ["ticket_id"],
  "additionalProperties": false
}

// 模型輸出(缺 ticket_id)→ 校驗失敗
{ "note": "請儘快處理" }

型別錯誤:型別不匹配、enum、巢狀與 coercion

型別錯誤指欄位存在,但值的 JSON 型別或格式不符合 Schema:

錯誤型別示例常見原因
基本型別錯limit: "10" 應為 number模型習慣把數字當字串輸出
enum 違規priority: "urgent",enum 只有 low/medium/highdescription 未列舉合法值
陣列/物件巢狀錯應為 tags: [] 卻給了字串複雜 Schema 超出模型子集能力
格式 string 違規email 不符合 format: email幻覺郵箱、日期格式各國混用
oneOf/anyOf 不滿足多型引數哪個分支都對不上Schema 過複雜,目標 API 子集不支援

關於 coercion(型別強制):部分校驗庫允許 "10" 自動轉為數字 10。Agent 生產環境建議預設關閉 coercion——模型應學會輸出正確型別;否則 silent coercion 會掩蓋系統性型別漂移,並在業務層引發更難查的 bug。若必須相容,在 Schema 旁檔案化並在 CI 用樣例鎖定行為。

// 型別錯誤示例
Schema: { "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }
模型:   { "limit": " fifty " }  // string,且非數字 → 校驗失敗

驗證方法:語法、arguments、strict mode

1. 校驗 Schema 本身是否合法

在註冊工具前,用元 Schema(draft 2020-12 等)驗證你的 parameters / inputSchema 檔案無語法錯誤。可在 JSON 工具箱瀏覽器端本機完成,避免把非法 Schema 發給模型 API。

2. 校驗 arguments 是否符合 Schema

宿主在 JSON.parse(arguments) 成功後,用與註冊時同一份 Schema 校驗物件。常見庫:

  • JavaScript / TypeScript:ajv(注意 draft 與 strict 選項)
  • Python:jsonschema、Pydantic(model_validate 前先 parse JSON)
  • 從程式碼生成 Schema:Zod / Pydantic → JSON Schema,單一源避免漂移

3. 廠商 strict mode 與 API 級約束

OpenAI strict: true 要求 Schema 滿足更嚴子集(如所有 object 顯式 additionalProperties: false、required 覆蓋全部 properties)。這能在模型生成階段減少引數錯誤,但不能替代宿主側二次校驗——API 子集各廠商不同,詳見Contract 一文的方言說明。

4. 樣例驅動與 CI

為每個工具維護「合法 arguments 樣例 + 故意錯誤樣例」,在 CI 跑 Schema 校驗斷言。Schema 變更等同 API 破壞性變更,應版本化。

端到端驗證流水線與錯誤回灌

推薦最小流水線(與資料流一文的「校驗與迴流」銜接):

1. tools/list 或靜態註冊 → 驗證每個 inputSchema 語法
2. 收到 tool_calls → JSON.parse(arguments)
   ├─ parse 失敗 → role: tool 訊息寫「JSON 語法錯誤: …」→ 讓模型重試
   └─ parse 成功 → ajv/jsonschema 校驗
        ├─ 失敗 → 結構化錯誤列表(缺欄位、型別、enum)
        │         → 作為 tool result 或 user 訊息回灌
        └─ 成功 → 執行業務 + 可選業務規則校驗
3. 工具返回 → 可選對 result Schema 校驗後再 append 到 messages
4. 日誌:保留 schema 版本、arguments 原文、校驗錯誤碼(勿記錄金鑰)

錯誤回灌要點:給模型的反饋要機器可讀且具體——「ticket_id is required」比「引數不對請重試」有效得多。部分框架把校驗錯誤格式化成 JSON 再塞回 tool role,模型下一輪更容易 Self-correction。

執行層仍要做鑑權與冪等——Schema 只保證形狀,不保證「這個 ticket_id 是否屬於當前使用者」。

落地建議

  • 單一 Schema 源:Pydantic / Zod 定義 → 生成 MCP inputSchema 與 OpenAI tools,避免三份定義。
  • description 當 Prompt 寫:description 直接影響模型是否填對 enum 與 required;Code Review Schema 與 Review API 同等重要。
  • 簡 Schema、嚴校驗:複雜 oneOf/$ref 深度按目標 API 子集裁剪;校驗失敗快速 fail,不要 silent fix。
  • 兩處必驗:tool_calls 後、execute 前;MCP 返回後、寫回模型前(若 result 會進上下文)。
  • 本機先驗:上線前在 JSON 工具箱貼上 Schema + 樣例 arguments,確認錯誤資訊可讀。
  • 與 Structured Output 分工:使用者-facing 回覆用 response Schema;工具引數用 tools Schema——勿混為一份。

常見問題 FAQ

Tool Calling 可以不用 JSON Schema,只用自然語言描述引數嗎?

原型可以,生產不建議。自然語言無法 fail-fast、無法 CI 版本化,模型易漏欄位或型別漂移。主流 API 與 MCP 已預設 Schema 化。

arguments 是字串還是物件?

多數 Chat Completions 風格 API 把 arguments 做成 JSON 字串,宿主需 JSON.parse 後再校驗。部分新介面直接返回物件;無論哪種,落地前用同一份 Schema 驗證。

校驗失敗應該重試幾次?

常見做法 1–3 次帶錯誤回灌的重試,仍失敗則降級為澄清問題或人工介入。無限重試會燒 token 且可能迴圈幻覺。

ajv 與 Pydantic 選哪個?

Node/TS 宿主用 ajv 直接驗 JSON Schema;Python 業務若已是 Pydantic 模型,可 Schema 生成 + 執行時 model_validate。關鍵是與發給模型的 Schema 同源。

strict mode 開啟後還要宿主校驗嗎?

要。strict 減少模型側錯誤,不防 MCP 返回髒資料、不防 Schema 與程式碼實現漂移、不防業務規則違規。

如何本機驗證 Schema 與 arguments?

在 JSON 工具箱瀏覽器端貼上 Schema 與樣例 JSON,本機校驗語法與結構匹配,資料不上傳伺服器。

總結與下一步

Tool Calling 依賴 JSON Schema,因為它是模型、MCP 與宿主唯一能共享、可校驗的引數契約。引數錯誤(缺、多、錯名、語法)與型別錯誤(型別、enum、巢狀)應在 execute 前被 Schema 攔截,並透過結構化錯誤回灌讓模型 Self-correction。

建議下一步:選一條真實工具鏈(如工單建立),寫清 Schema + 合法/非法樣例,在 JSON 工具箱本機驗通後接入 Agent;系列閱讀順序:演進 → 資料流 → Contract → 本文(驗證)。