博客 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 是貫穿三層的契約語言。

本文將說明:

先記住這一點: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. 1
    Runtime 組裝請求

    把對話、系統提示和 tools 數組發給 LLM API。每個 tool 的 parameters 是一份 JSON Schema。

  2. 2
    模型做 Function Calling

    返回 name=get_ticket、arguments 爲 JSON 字符串,而不是自然語言「請幫我查一下」。

  3. 3
    Runtime 校驗入參

    json.loads 之後按同一份 schema validate。缺字段或類型錯就重試或拒絕,不要把髒參數打到後端。

  4. 4
    MCP 執行

    Runtime 把已校驗的 arguments 放進 MCP tools/call。Server 查庫,返回 structuredContent。

  5. 5
    Runtime 校驗出參

    structuredContent 必須符合 MCP outputSchema。通過後纔回填給模型。

  6. 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 的響應形狀。它解決的問題是:模型的下一步必須是機器能讀的「函數名 + 參數」,而不是一段散文。

發給模型的 tools 定義(parameters 就是 JSON Schema)
{
  "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 熱點

MCP 工具定義:inputSchema / outputSchema 用 JSON Schema 2020-12
{
  "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
Runtime 把 Function Calling 接到 MCP(僞代碼)
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. 1
    格式化 arguments

    把 tool_calls.arguments 貼進JSON 格式化,先排除語法損壞和截斷。

  2. 2
    用同一份 schema 校驗

    把 parameters / inputSchema 和 arguments 貼進JSON Schema。失敗點就是 Runtime 該拒絕的位置。

  3. 3
    對比兩次調用

    prompt 或 schema 改了一版,用JSON Diff看 arguments 或 structuredContent 到底變了哪幾個字段。

  4. 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 隨時可用。

← 返回博客