AI Agent

AI Agent 為什麼離不開 JSON?從 Tool Calling、Function Calling 到 MCP

筆記型電腦螢幕顯示程式碼編輯器與終端機日誌,雙手正在鍵盤上輸入
開發者在多螢幕工作區撰寫與除錯——Agent 的工具呼叫最終也要落到這樣的可執行環境。

症狀: Agent 能聊天,但一接工具就參數對不上、重試把訂單打兩遍,或者 MCP 服務明明在線卻「看不見工具」。
最快解法: 把 JSON 當成 Agent 與工具之間的唯一契約:用 JSON Schema 約束入參,用固定字段回傳結果,再用 MCP 把這份契約從單應用搬到可發現、可鑑權的協議層。

這篇文章面向正在把大模型接到真實系統的開發者、平臺工程師和架構師。你要搞清楚的不是又一個名詞,而是同一份 JSON 如何在 Function Calling、Tool Calling 和 MCP 之間流轉,以及哪一層失敗會讓整條鏈路停住。

如果你正在評估框架而不是協議本身,可以先讀 AI Agent 開發框架怎麼選;工具真正執行時還涉及隔離邊界,可對照 AI Sandbox、Docker 與虛擬文件系統的差異。

AI Agent 爲什麼離不開 JSON

模型擅長生成自然語言,系統只認結構化指令。Agent 一旦要查庫存、改工單、跑測試或打開瀏覽器,就必須把「意圖」編成程序能校驗、能拒絕、能審計的數據。JSON 正好卡在這個縫裡:人能讀,機器能解析,HTTP、日誌、隊列和數據庫都喫得下。

更關鍵的是可驗證。一段散文無法告訴網關「order_id 必須是字符串、amount 必須是正數、currency 只能是枚舉」。JSON Schema 可以。官方 Function Calling 文檔把函數參數定義成 JSON Schema,正是爲了讓模型在採樣時就往合法結構上靠,而不是事後再猜。查看 OpenAI Function Calling 指南

JSON Schema 本身也規定了類型、必填、枚舉和 additionalProperties 等約束;生產環境裡這些不是學術細節,而是防止模型發明字段、把字符串塞進數字、或漏掉冪等鍵的第一道閘。查看 JSON Schema 說明

你可以把它想成三層:

  • 對話層:用戶說「把上海倉那單改成加急」。
  • 契約層:模型輸出 {"name":"update_order","arguments":{"order_id":"A938","priority":"expedited"}}。
  • 執行層:你的代碼校驗 schema、查權限、調用業務 API、把結果再寫成 JSON 餵回模型。

沒有契約層,Agent 只是會說話的聊天窗口。有了契約層,它才可能成爲可治理的執行器。

⚠️ 經驗判斷:如果工具入參還在用「自由文本 + 正則提取」,說明你還沒有 Agent 協議,只有提示詞運氣。先把 schema 寫死,再談多工具編排。

Function Calling:模型第一次學會填表

Function Calling 的本質,是讓模型在回復用戶之前,先決定要不要調用你聲明過的函數,並給出符合 schema 的參數對象。典型數據流只有五步,但每一步都可能丟字段:

  1. 註冊:請求裡帶上 tools,每個 function 含 name、description、parameters(JSON Schema)。
  2. 決策:模型根據用戶話術與 description 選擇函數,或選擇直接回答。
  3. 填表:輸出 tool_calls,arguments 通常是 JSON 字符串或結構化對象。
  4. 執行:應用側 parse、校驗、鑑權、調用真實系統。
  5. 回灌:把工具結果作爲 tool 消息再發給模型,模型才生成對用戶的最終答覆。

description 不是文案,是路由信號。寫「查詢天氣」會和「查詢物流」搶流量;寫「僅在用戶給出城市名時查詢當前氣溫,不要用於歷史氣候」才能減少誤觸發。parameters 裏每個字段的 description 同樣會被模型當提示詞用,所以「order_id:業務訂單號,不是支付流水號」這種約束應該寫進 schema,而不是只寫在系統提示裏。

嚴格模式(strict / Structured Outputs)的意義,是把「儘量像 JSON」升級成「必須符合 schema」。JSON mode 只保證花括號能 parse,不保證缺字段或類型錯了會被擋住。生產裏你仍要在應用層做二次校驗:schema 合法不等於業務合法,例如金額爲負數、倉庫代碼不存在、用戶無權操作該訂單。

一份最小可執行契約

下面這份 schema 故意把枚舉和冪等鍵寫死。模型可以決定要不要調用,但不能發明 warehouse 之外的倉位字段,也不能省略 idempotency_key。

function schema(節選)
{
  "type": "function",
  "name": "update_shipment",
  "description": "僅在用戶明確要求改配送優先級時調用。",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {"type": "string", "description": "業務訂單號"},
      "priority": {"type": "string", "enum": ["standard", "expedited"]},
      "idempotency_key": {"type": "string"}
    },
    "required": ["order_id", "priority", "idempotency_key"],
    "additionalProperties": false
  }
}

執行側拿到 arguments 後,正確順序是:JSON parse → schema 校驗 → 權限檢查 → 查冪等表 → 調業務 API → 把 {"ok":true,"shipment_id":"..."} 或 {"ok":false,"error":"not_found"} 回給模型。永遠不要把堆棧或密鑰原文塞進 tool result。

Tool Calling:從「函數」變成「工具平面」

業界很快發現:模型要碰的不只是你自己寫的函數。搜索、代碼解釋器、瀏覽器、計算機操作、文件檢索,都是「工具」。於是 API 從早期的 function_call 演進到統一的 tools / Tool Calling。名字變了,JSON 契約沒變:仍然是 name + schema + 調用記錄 + 結果回灌。

Anthropic 把這一能力叫 Tool use,並用 JSON Schema 描述 input_schema;模型返回的 tool_use 塊同樣是結構化參數,應用執行後再以 tool_result 回傳。查看 Anthropic Tool use 文檔

對工程團隊來說,Tool Calling 帶來三個新問題,都仍然靠 JSON 解決:

  • 並行調用:一次回復裏多個 tool_calls。你要決定哪些可並行(讀操作),哪些必須串行(先鎖庫存再扣款)。
  • 工具審批:高風險工具先返回「待批准」的 JSON 狀態,而不是直接執行。
  • 可觀測性:trace 裏要能還原 tool 名、參數摘要、耗時、錯誤碼,而不是只存一段模型散文。

Tool Calling 還把失敗類型從「模型胡說」擴展成「模型選對了工具但參數越權」。例如工具叫 run_sql,schema 只允許 SELECT,模型卻在某個字符串字段裏塞進 DDL。JSON Schema 攔得住類型,攔不住語義攻擊,所以危險工具必須在執行層再做允許列表、超時和沙箱。

MCP:把 JSON 契約做成可插拔協議

Function / Tool Calling 解決的是某一次 API 請求裏模型和本應用工具怎麼說話。MCP(Model Context Protocol)解決的是工具如何被發現、被多個客戶端復用、被獨立進程託管。規範裏,客戶端與服務器用 JSON-RPC 2.0 交換消息:初始化、列出 tools/resources/prompts、調用 tool、流式通知。查看 MCP 規範

對開發者最直觀的數據流是:

  1. 宿主(IDE、Agent 運行時、聊天應用)作爲 MCP Client 啓動。
  2. 通過 stdio、SSE 或 Streamable HTTP 連上 MCP Server。
  3. initialize 協商協議版本與能力。
  4. tools/list 拿到工具清單:每個工具仍帶 JSON Schema 形式的 inputSchema。
  5. 模型側還是走 Tool Calling:選中某個 MCP 工具後,Client 發 tools/call,參數是 JSON。
  6. Server 執行後返回 content(文本、圖片或結構化內容),Client 再把它編成 tool result 餵給模型。

所以 MCP 並沒有取代 JSON,而是把原來寫死在應用代碼裏的 tools 數組,變成一份運行時可發現的工具目錄。同一套 Git 服務器、同一套瀏覽器自動化、同一套內部工單 API,可以被 Cursor、Claude Desktop 或你自建的 Agent 共用,只要它們都講 MCP。

這也解釋了爲什麼「裝了 MCP 仍調不到工具」幾乎總是 JSON 契約問題:initialize 失敗、list 返回空、schema 與模型提供商的 strict 模式不兼容、call 的 arguments 缺 required 字段、或 Server 用自然語言錯誤代替 JSON-RPC error。

完整數據流:從用戶一句話到 MCP 執行

把三層疊在一起,一次真實任務大致長這樣:

  1. 用戶輸入自然語言。
  2. Agent 運行時組裝 messages + 本地 tools + 從 MCP tools/list 拉來的遠程 tools。
  3. 模型做 Tool Calling,輸出 JSON 參數。
  4. 運行時按 tool 來源分流:本地函數直接執行;MCP 工具走 JSON-RPC tools/call。
  5. 執行結果寫成 JSON(或 MCP content 數組)回到對話。
  6. 模型決定繼續調用、詢問用戶,還是給出最終答案。

這裡有一個容易忽略的邊界:模型從未直接持有你的數據庫連接。它只產出 JSON。誰解析這份 JSON、在哪臺機器上執行、有沒有 VNC/SSH 會話、有沒有網絡出站,全是你的運行時策略。雲端 Mac 上跑 Agent 或 MCP Server,和在筆記本上跑,協議相同,權限平面不同。

層JSON 扮演的角色你該卡住的點
Function Calling單次請求的函數參數schema、strict、二次業務校驗
Tool Calling統一工具平面上的調用記錄並行、審批、trace
MCP跨進程的工具發現與調用傳輸、鑑權、list/call 錯誤碼

選型時不要被產品名帶走。先問:工具是只給這一個應用用,還是要給多個 Agent 宿主復用?前者 Function/Tool Calling 足夠;後者才值得上 MCP Server。反過來,沒有 schema 和校驗,上了 MCP 也只是把混亂從一個進程搬到另一個進程。

JSON 一壞,整條 Agent 鏈路都會壞

生產裏最常見的不是模型「不夠聰明」,而是契約在某一跳變形了。按跳排查會快很多:

  • 模型輸出不是合法 JSON:未開 strict、prompt 裏要求「先解釋再調用」、或把 arguments 包進 Markdown 代碼塊。處理:強制 structured tool 通道,禁止從 assistant 散文裏二次提取。
  • JSON 合法但 schema 不通過:缺 required、enum 寫錯、多了 additionalProperties。處理:把校驗錯誤作爲 tool result 回給模型,限制重試次數。
  • schema 通過但業務失敗:訂單不存在、庫存不足、權限拒絕。處理:返回穩定 error code,而不是 500 HTML。
  • 重試造成雙寫:超時後模型再調一次。處理:arguments 裏強制 idempotency_key,執行層去重。
  • MCP list 與 call 不一致:list 裏有工具,call 時 name 對不上,或 schema 在熱更新後變了。處理:對工具名和 schema 做版本哈希,變更即重新 initialize。
  • 敏感數據進了 JSON 日誌:完整參數寫進可被多人讀取的 trace。處理:只記字段名與哈希,對 token、郵箱、身份證脫敏。

把這些失敗寫成同一個驗收場景:故意讓工具超時、故意讓 schema 失敗、故意拒絕權限,看 Agent 是循環重試、胡亂改參數,還是停下來請求人工。過不了這一關的系統,不該接到支付、發貨或生產數據庫。

上線前用這份 JSON 契約清單驗收

  • [ ] 每個工具都有 name、description、JSON Schema,且 additionalProperties 爲 false。
  • [ ] 高風險字段使用 enum / format / 最大長度,而不是自由字符串。
  • [ ] 寫操作帶冪等鍵,超時與重試策略寫進運行時而不是提示詞。
  • [ ] tool result 使用穩定結構:ok、error、data,禁止把異常堆棧原文回灌。
  • [ ] 並行 tool_calls 已按副作用分組;付款、發郵件、刪數據默認串行。
  • [ ] MCP Server 的 initialize / tools/list / tools/call 都有超時、鑑權和錯誤碼。
  • [ ] 工具清單變更後,宿主會重新拉取 schema,而不是緩存一份過期 JSON。
  • [ ] trace 能還原調用鏈,但不能還原密鑰和完整個人信息。
  • [ ] 用固定輸入回歸:同一句話必須打到同一工具名和同一必填字段集合。

清單通過之後,再考慮多 Agent、記憶圖譜或更重的工作流。協議不穩,上層編排只會把壞 JSON 複製得更快。

獨立 FAQ

爲什麼不直接讓模型輸出自然語言給程序解析?

自然語言沒有必填字段和類型。程序無法穩定判斷「改成加急」對應哪個枚舉,也無法在網關層拒絕多餘字段。JSON Schema 把解析變成校驗,失敗可以結構化回傳,而不是靠正則碰運氣。

Function Calling 和 Tool Calling 有實質差別嗎?

對應用開發者而言,核心都是「模型填 JSON 表單,你的代碼執行」。Tool Calling 是更寬的平面:除了自定義函數,還包括搜索、代碼執行等宿主工具,並更強調並行調用與統一的 tool 結果通道。遷移時優先對齊 schema 與消息角色,而不是糾結舊字段名。

什麼時候才需要 MCP,而不是在應用裏寫死 tools 數組?

當同一組工具要被多個宿主復用、需要獨立進程隔離、或工具由另一個團隊發布時,MCP 才划算。如果工具只服務一個內部服務,且生命周期與應用綁定,Function/Tool Calling 更簡單,少一層傳輸和鑑權。

模型偶爾輸出壞 JSON,應該重試還是換協議?

先開嚴格結構化輸出,並禁止從散文裏抽參數。若仍高頻失敗,檢查 schema 是否過深、description 是否互相衝突、工具數量是否過多。這些問題換 MCP 不會消失,因爲 MCP 調用的入參同樣是 JSON。

如果你現在用共享筆記本跑 Agent,環境一變 schema 回歸就失效,工具進程也容易和日常開發搶端口。租用 Kvmjet 的裸金屬 Mac 雲主機,可以固定一份 macOS 鏡像,用 SSH 或 VNC 復現同一條 Tool Calling / MCP 失敗;公開說明提供按天到按季的周期,適合先把 JSON 契約跑穩再決定是否長期佔用機器。

建議帶着上面的清單繼續查看 Kvmjet 幫助中心,核對持續運行、權限隔離和遠程接入方式,再決定 MCP Server 放在本地、機房還是雲端 Mac。

把 Tool Calling 和 MCP 跑在固定的 Mac 環境裡

JSON Schema、冪等鍵和 MCP Server 都依賴可重現的執行環境。共用筆電一換目錄、一搶連接埠,迴歸立刻失效。

Kvmjet 的裸金屬 M4 Mac 提供真實 macOS、獨享頻寬和 SSH/VNC,適合把同一條失敗的 tools/call 反覆打通後再決定長期占用。

了解套餐方案 →

限時優惠