博客 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 随时可用。

← 返回博客