博客 • AI / Agent
2026 AI Agent 技術棧:LLM、MCP、Function Calling、JSON Schema 如何組合?
你在搭 AI Agent,文檔裏同時出現 LLM、MCP、Function Calling 和 JSON Schema。四套名詞看起來都能「讓模型調工具」,於是選型變成四選一。
它們不是四個替代方案。LLM 負責推理,Function Calling 負責把決策序列化成可執行的 JSON,MCP 負責連上外部服務並回傳結構化結果,JSON Schema 是貫穿三層的契約語言。
本文將說明:
- 四層各自擁有什麼、不該擁有什麼
- 一次工具調用的數據怎麼從模型流到 MCP 再流回來
- Function Calling 的 parameters 和 MCP 的 inputSchema 如何對齊
- 一份可落地的參考架構,以及放錯層會怎樣
- 用 JSONNote 本地調試這條鏈路上的 JSON
先記住這一點:2026 年的 Agent 棧不再爭論「要不要 JSON」,而是爭論「決策、執行、契約分別放在哪一層」。模型思考,Function Calling 序列化,MCP 執行,JSON Schema 校驗每一跳。下面按職責 → 數據流 → 各層 → 組合 → 錯位拆開。
四層分別幹什麼
先把職責畫清楚。混在一起的時候,調試會變成「模型不行」或「MCP 壞了」——往往兩邊都沒壞,是邊界沒劃開。
| 層 | 職責 | 輸入 | 輸出 |
|---|---|---|---|
| LLM | 推理、規劃、決定下一步 | 對話、工具結果、系統提示 | 繼續對話,或「調用某工具」 |
| Function Calling | 把「調哪個工具、帶什麼參數」變成 API 對象 | tools 數組 + 對話 | tool_calls[].arguments JSON |
| MCP | 發現工具、執行調用、回傳結構化結果 | tools/call + arguments | structuredContent JSON |
| JSON Schema | 描述並校驗每一跳的 JSON | schema 文檔 | 通過 / 失敗原因 |
一句話對照:LLM 想,Function Calling 說,MCP 做,JSON Schema 驗。
JSON Schema 怎麼寫、Tool Calling 和 Structured Output 有何差異,見AI Agent 爲什麼需要 JSON Schema。本文只講四層怎麼接在一起。
一次工具調用的數據流
以「按單號查工單」爲例。用戶說「查一下 T-1042」,Runtime 不會把這句話直接丟給工單系統,而是走完下面六步。
-
1
Runtime 組裝請求
把對話、系統提示和 tools 數組發給 LLM API。每個 tool 的 parameters 是一份 JSON Schema。
-
2
模型做 Function Calling
返回 name=get_ticket、arguments 爲 JSON 字符串,而不是自然語言「請幫我查一下」。
-
3
Runtime 校驗入參
json.loads 之後按同一份 schema validate。缺字段或類型錯就重試或拒絕,不要把髒參數打到後端。
-
4
MCP 執行
Runtime 把已校驗的 arguments 放進 MCP tools/call。Server 查庫,返回 structuredContent。
-
5
Runtime 校驗出參
structuredContent 必須符合 MCP outputSchema。通過後纔回填給模型。
-
6
模型給出最終回答
可以是自然語言,也可以再走 Structured Output,用另一份 schema 約束最終 JSON。
注意中間有兩次 validate:一次攔模型亂參,一次攔 Server 亂返回。少任何一次,下一層都會把髒數據當成事實。
LLM:只負責推理和決策
LLM 是棧裏唯一「會想」的層。它讀上下文,決定是回答、追問,還是調用工具。它不該直接碰數據庫、支付接口或文件系統。
2026 年各廠商的模型 API 已經對齊到同一套語義:你提供 tools,模型返回 tool_calls 或最終文本。
OpenAI 走Function Calling/ Responses API,Anthropic 走Tool Use,DeepSeek 兼容 OpenAI 字段名。差別在字段名和 Strict 能力,不在「要不要結構化」。
把 LLM 當執行器會出兩件事:密鑰進 prompt,副作用無法審計。執行留給 MCP,模型只輸出「我想調誰、參數是什麼」。
Function Calling:模型 API 層的工具選擇
Function Calling 不是協議,是模型 API 的響應形狀。它解決的問題是:模型的下一步必須是機器能讀的「函數名 + 參數」,而不是一段散文。
{
"model": "gpt-5",
"messages": [
{"role": "user", "content": "Look up ticket T-1042"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_ticket",
"description": "Fetch a support ticket by id",
"parameters": {
"type": "object",
"properties": {
"ticketId": {
"type": "string",
"pattern": "^T-[0-9]+$"
}
},
"required": ["ticketId"],
"additionalProperties": false
}
}
}
]
}
模型返回的 arguments 是字符串,不是已經 parse 好的對象。Runtime 必須自己 loads + validate。開了 Strict Schema 也一樣——API 層 conform 不能代替業務層校驗。這一點和2026 Structured Output裏的 response_format 是同一類問題,只是約束對象從「最終回答」換成了「工具參數」。
Function Calling 到此結束:它不管工具怎麼實現、結果長什麼樣。把 arguments 交給誰,是 Runtime 的事。
MCP:Agent 與外部世界的協議
MCP(Model Context Protocol)管的是 Agent Runtime 和外部服務之間的標準通話:列出工具、調用工具、拿回結構化結果。2026-07-28 規範已去掉 session 握手,每個請求自描述,Server 可以跟普通 HTTP 服務一樣做負載均衡。背景見AI Conference 2026 熱點。
{
"name": "get_ticket",
"description": "Fetch a support ticket by id",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ticketId": {
"type": "string",
"pattern": "^T-[0-9]+$"
}
},
"required": ["ticketId"]
},
"outputSchema": {
"type": "object",
"properties": {
"ticketId": { "type": "string" },
"status": { "type": "string", "enum": ["open", "pending", "closed"] },
"assignee": { "type": ["string", "null"] }
},
"required": ["ticketId", "status"]
}
}
Runtime 把 Function Calling 裏校驗過的 arguments 原樣放進 tools/call。Server 返回的 structuredContent 必須能通過 outputSchema。Client 側再 validate 一次——數據來源從模型換成了 Server,工程問題相同。
官方工具規範:MCP Tools。inputSchema 根節點仍是 object;outputSchema 在 2026-07-28 後可以是任意 JSON 值。
JSON Schema:貫穿三層的契約
JSON Schema 不是第四個「功能模塊」,是另外三層共用的描述語言。當前主流是2020-12。
| 出現位置 | 字段 | 約束什麼 | 誰校驗 |
|---|---|---|---|
| Function Calling | tools[].function.parameters | 模型生成的 arguments | Runtime(調 MCP 之前) |
| Structured Output | response_format.json_schema | 模型最終回答 | Runtime(交給業務之前) |
| MCP 入參 | inputSchema | tools/call 的 arguments | Runtime + Server |
| MCP 出參 | outputSchema | structuredContent | Runtime(回填模型之前) |
組合時最容易踩的坑:Function Calling 的 parameters 和 MCP 的 inputSchema 各寫一份,字段名或 enum 對不上。模型按 A 生成,Server 按 B 校驗,表現爲「模型總會調失敗」。
正確做法是一份契約、兩處掛載:用同一份 schema 對象生成 parameters 和 inputSchema。最終回答如果也要進下游系統,再單獨準備 Structured Output 的 schema,不要和工具入參混用。
參考架構:四層怎麼拼
生產環境裏四層的默認接法如下。本地函數或自家 HTTP API 可以先當 MCP 的替身,但邊界不要變。
| 組件 | 做什麼 | 不做什麼 |
|---|---|---|
| LLM API | 推理;返回 tool_calls 或最終 JSON | 不持有密鑰,不直接訪問內部系統 |
| Agent Runtime | 組裝 tools、校驗、路由到 MCP、回填、循環 | 不把自然語言當參數,不跳過 validate |
| MCP Server | 執行副作用,按 outputSchema 返回 JSON | 不解析 prompt,不替模型做規劃 |
| Schema 倉庫 | 單一來源;同時供給 API tools 和 MCP | 不在兩處手寫兩份「差不多」的 JSON |
schema = catalog.get("get_ticket") # single source
req.tools = [{ "type": "function",
"function": { "name": "get_ticket",
"parameters": schema.input } }]
resp = llm.chat(req)
args = json.loads(resp.tool_calls[0].arguments)
validate(args, schema.input) # stop bad model args
content = mcp.call("get_ticket", args) # MCP executes
validate(content.structuredContent, schema.output)
req.messages.append(tool_result(content)) # feed back, next turn
循環可以多輪:模型看到 structuredContent 後再決定下一個 tool 或給出最終回答。Schema 倉庫不變,變的是 messages 裏越積越多的工具結果。
放錯層會怎樣
四層能組合,是因爲每層只做一件事。職責滑到隔壁,故障會看起來像模型能力問題。
| 錯法 | 表面上像 | 實際後果 |
|---|---|---|
| 讓 LLM 直接打 HTTP | 少一個框架,出活快 | 密鑰進上下文;無法審計;換模型就要重寫調用 |
| 把 MCP 當 Function Calling 用 | 協議裏也有 name / arguments | 模型看不到 tools 列表,不會穩定選工具 |
| 只用 prompt 要 JSON,不寫 schema | demo 能跑 | 無法迴歸;字段漂移;下游 parse 天天修 |
| parameters 和 inputSchema 各寫一份 | 兩邊都能「跑起來」 | enum / 必填不一致,表現爲隨機 tool 失敗 |
| 相信模型輸出,跳過 validate | 開了 Strict / JSON Mode | 缺字段、截斷、類型錯誤直接打進業務 |
判斷口訣:模型只輸出決策 JSON;執行只發生在 MCP(或你顯式聲明的後端);進出都要 schema。
用 JSONNote 調試這條鏈路
這條鏈路上至少有三份 JSON:模型 arguments、MCP structuredContent、最終 Structured Output。它們都該能在本地 format / validate / diff,不必把 Key 和樣本上傳到任何服務。
-
1
格式化 arguments
把 tool_calls.arguments 貼進JSON 格式化,先排除語法損壞和截斷。
-
2
用同一份 schema 校驗
把 parameters / inputSchema 和 arguments 貼進JSON Schema。失敗點就是 Runtime 該拒絕的位置。
-
3
對比兩次調用
prompt 或 schema 改了一版,用JSON Diff看 arguments 或 structuredContent 到底變了哪幾個字段。
-
4
分享調試現場
用URL Hash 分享把某次 tool call 寫進鏈接,同事打開即可復現——數據不經過服務器。
常見問題
Function Calling 和 MCP 必須一起用嗎?
不是必須。本地函數、HTTP API 都可以當工具後端。MCP 的價值是把工具發現、調用和 structuredContent 標準化;Function Calling 只負責讓模型選出 name 和 arguments。兩者常組合,但可以單獨存在。
JSON Schema 應該寫幾份?
至少兩份:工具入參一份(Function Calling 的 parameters 與 MCP inputSchema 應對齊),工具出參或最終回答一份(MCP outputSchema 或 Structured Output 的 response_format)。不要讓模型側和 MCP 側各寫一份互不同步的契約。
沒有 MCP,只用 Function Calling 夠不夠?
做單機 demo 夠。一旦工具要給多個 Agent、多個運行時複用,或要水平擴展,缺了 MCP 你就要自己發明 tools/list、鑑權和結構化回傳。2026 年的默認組合是 Function Calling 選工具,MCP 執行工具。
模型已經返回 JSON 了,還要在 Runtime 裏校驗嗎?
要。Function Calling 的 arguments 和 MCP 的 structuredContent 都可能缺字段、類型錯或語法損壞。API 層 conform 不等於業務層正確。校驗是 Runtime 的責任,不是模型或 Server 的保證。
總結
2026 年的 Agent 技術棧可以收成三句:
LLM 決策,Function Calling 序列化,MCP 執行;JSON Schema 是三層共用的契約,不是第四個可選項。
先固定一份入參 schema、一份出參 schema,讓模型 API 和 MCP Server 掛同一份。Runtime 在兩跳都 validate。模型和協議還會變,契約和校驗層是 Agent 從 demo 到生產的護城河——調試這些 JSON 時,JSONNote 的本地 format / schema / diff 隨時可用。