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