AI Agent 為什麼離不開 JSON?從 Tool Calling、Function Calling 到 MCP 的完整資料流解析

拆解一次 Agent 呼叫裡每一跳的 JSON:工具定義 Schema、Function Calling / Tool Calling 訊息、MCP JSON-RPC,以及校驗失敗時資料如何回流。

上一篇《AI Agent 為什麼開始使用 JSON Schema、Function Calling 與 MCP》講的是為什麼會出現這三層。本文換一個視角:盯住一次真實呼叫裡流動的位元組——幾乎全是 JSON。

使用者看到的是自然語言;Agent 真正「辦事」靠的是:把意圖編碼成 JSON 引數、把工具結果編碼成 JSON 訊息、把跨程式協議也編碼成 JSON-RPC。JSON 不是點綴,而是模型、宿主程式、MCP Server 之間唯一能互相校驗的公共語言。

三個名字,同一份 JSON

檔案裡常把三個詞混用,資料流上它們處在不同層,但載荷形狀高度同構:

名稱發生在哪兩端JSON 扮演的角色
Function Calling模型 API ↔ 宿主tools 定義 + tool_calls.arguments
Tool Calling同上(更通用的叫法)同一套 messages / tools JSON
MCP宿主 ↔ 工具程式JSON-RPC 方法 + inputSchema

可以記成一句話:模型側用 JSON 選工具、填引數;MCP 側用 JSON 發現工具、執行工具。 宿主是翻譯器:把 MCP 的 tools/list 對映成模型的 tools 陣列,把模型的 tool_calls 對映成 tools/call。

為什麼必須是 JSON

Agent 要同時滿足三方:

  • 模型:訓練語料裡 JSON 極多,生成合法物件比生成 protobuf 二進位制容易得多
  • 程式:有成熟的解析、Schema 校驗、Diff 與 JSONPath 生態
  • 協議:OpenAPI、JSON-RPC、MCP inputSchema 已經綁在同一套型別描述上

純自然語言無法 fail-fast:括號、引號、多語言混寫都會讓正則解析崩潰。YAML 對縮排敏感,模型更容易寫壞。二進位制協議對人類與 LLM 都不友好。於是 JSON 成為「可審計、可校驗、可版本化」的預設導線格式——這也是本站工具全部圍繞 JSON 的原因:你除錯的就是這條導線。

第一跳:工具定義裡的 Schema

資料流從「告訴模型有哪些工具」開始。無論走 OpenAI 風格的 tools,還是 MCP 的 tools/list,核心都是一份 JSON Schema(或其子集):

{
  "name": "get_weather",
  "description": "查詢指定城市當前天氣,只讀",
  "parameters": {
    "type": "object",
    "properties": {
      "city": { "type": "string", "description": "城市名,如上海" },
      "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
    },
    "required": ["city"]
  }
}

MCP 裡同一份約束寫在 inputSchema 欄位。Schema 同時進兩路:校驗器攔截非法引數;模型上下文靠 description 決定何時呼叫。欄位說明寫得越像業務檔案,誤呼叫越少。

第二跳:Function Calling / Tool Calling

宿主把工具列表隨 messages 發給模型後,模型不執行程式碼,只返回結構化呼叫。典型形態(各廠商欄位名略有差異):

{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_01",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\":\"上海\",\"unit\":\"celsius\"}"
      }
    }
  ]
}

注意 arguments 常常是字串化的 JSON:要先 JSON.parse,再按 Schema 校驗,最後才執行。執行結果再以 tool 角色訊息迴流:

{
  "role": "tool",
  "tool_call_id": "call_01",
  "content": "{\"city\":\"上海\",\"temp_c\":31,\"condition\":\"晴\"}"
}

這一跳解決的是「模型如何伸手」。並行多工具時,陣列裡會出現多個 tool_calls,宿主可併發執行,再按 id 把結果對號入座。

第三跳:MCP JSON-RPC

若工具不在宿主程式內,而是獨立 MCP Server(檔案系統、GitHub、內部訂單服務),宿主與 Server 之間走 JSON-RPC 2.0。一次只讀查詢大致三步:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"host","version":"1.0"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"上海"}}}

Server 的成功響應同樣是 JSON:content 陣列裡常見 type: "text",文字內容又是一段 JSON 字串。於是出現「JSON 套 JSON」——外層是協議信封,內層是業務載荷。除錯 MCP 時先分清這兩層,再用 Schema 校驗內層。

傳輸可以是 stdio 或 Streamable HTTP,但載荷仍是 JSON 行或 JSON 體。關於 2026 傳輸與 SDK 是否要改程式碼,見《MCP 2026 遷移指南》。

一次完整呼叫的資料流追蹤

使用者說:「上海今天多少度?」端到端如下:

  1. Host → MCP Server:tools/list 得到帶 inputSchema 的工具清單(JSON)
  2. Host → 模型 API:對映為 tools[].parameters(仍是 JSON Schema)
  3. 模型 → Host:tool_calls,arguments 為 {"city":"上海"}
  4. Host 校驗:對照 Schema,缺欄位或型別錯誤則拒絕執行,把錯誤 JSON 回喂模型
  5. Host → MCP:tools/call,params.arguments 為物件(不是字串)
  6. MCP → Host:天氣結果 JSON
  7. Host → 模型:role: tool 的 content 字串
  8. 模型 → 使用者:自然語言;若下游系統只要結構,再用輸出 Schema 約束最終 JSON
使用者自然語言
    │
    ▼
Host 編排 ──JSON Schema──► LLM Tool Calling
    │                         │
    │                         ▼
    │                    arguments JSON
    │                         │
    ▼                         ▼
MCP JSON-RPC ◄──────── 校驗透過才執行
    │
    ▼
結果 JSON ──► tool message ──► 模型最終答覆

小型指令碼可能跳過 MCP,直接在宿主裡調本機函式;企業 Agent 則幾乎總是「Tool Calling + MCP」疊在一起。選型與生態見《2026 MCP Server 排名》。

校驗失敗時資料如何迴流

JSON 能成為 Agent 的「型別系統」,是因為失敗也可以結構化。建議至少兩道閘:

閘門校驗物件失敗後怎麼迴流
執行前模型 arguments不呼叫真實工具;把 Schema 錯誤寫成 tool 結果或系統提示,讓模型重填
回寫前MCP / 函式返回值截斷、脫敏或標錯;避免把堆疊原文灌進下一輪上下文

開發階段把 Schema 與 2~3 組正/反例 payload 存進倉庫,用 JSON 工具箱本機校驗——這和測 REST 契約同一思路,只是消費者換成了模型。

常見問題 FAQ

Tool Calling 和 Function Calling 是一回事嗎?

對開發者幾乎是同一套資料流:宿主把工具 Schema 發給模型,模型返回帶 JSON 引數的呼叫,宿主執行後再把 JSON 結果塞回對話。Function Calling 是早期 OpenAI 命名;Tool Calling / Tools API 是後續更通用的叫法。

MCP 報文為什麼也是 JSON?

MCP 基於 JSON-RPC 2.0:initialize、tools/list、tools/call 的請求與響應都是 JSON 物件。工具的 inputSchema 本身又是 JSON Schema,因此 Host 可以把 MCP 工具一對一對映成模型 API 的 tools 陣列。

模型輸出的 arguments 是字串還是物件?

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

為什麼不能用 YAML 或 protobuf 替代 JSON?

可以在工具實現內部用任意格式,但模型上下文與跨廠商協議層已把 JSON 當作事實標準。YAML 縮排易錯,protobuf 對模型不友好。常見做法是邊界用 JSON,內部再轉換。

資料流裡哪一層最該做 Schema 校驗?

至少兩處:模型返回 tool_calls 之後、真正執行工具之前;以及 MCP Server 返回結果之後、寫回模型之前。前者防幻覺引數,後者防髒資料汙染下一輪推理。

如何本機驗證這條鏈路上的 JSON?

把 inputSchema、樣例 arguments、工具返回樣例儲存成 JSON 檔案,用 JSON 工具箱在瀏覽器本機校驗 Schema 與資料是否匹配,資料不上傳伺服器。

總結

AI Agent 離不開 JSON,是因為每一跳都要機器可讀:Schema 描述工具,Tool Calling 傳遞呼叫,MCP 用 JSON-RPC 把呼叫送出程式。自然語言只出現在兩端的使用者介面;中間全是可校驗的物件。

建議你從一次真實工具開始:寫出 Schema → 列印模型返回的 arguments 字串並解析 → 若工具在 MCP Server 上,再抓一條 tools/call。三份 JSON 對得上,這條 Agent 才算真正跑通。演進背景見《技術演進全解析》;Schema 樣例可在 JSON 工具箱本機校驗後再上線。