上一篇《AI Agent 为什么开始使用 JSON Schema、Function Calling 与 MCP》讲的是为什么会出现这三层。本文换一个视角:盯住一次真实调用里流动的字节——几乎全是 JSON。
用户看到的是自然语言;Agent 真正「办事」靠的是:把意图编码成 JSON 参数、把工具结果编码成 JSON 消息、把跨进程协议也编码成 JSON-RPC。JSON 不是点缀,而是模型、宿主程序、MCP Server 之间唯一能互相校验的公共语言。
三个名字,同一份 JSON
文档里常把三个词混用,数据流上它们处在不同层,但载荷形状高度同构:
| 名称 | 发生在哪两端 | JSON 扮演的角色 |
|---|---|---|
| Function Calling | 模型 API ↔ 宿主 | tools 定义 + tool_calls.arguments |
| Tool Calling | 同上(更通用的叫法) | 同一套 messages / tools JSON |
| MCP | 宿主 ↔ 工具进程 | JSON-RPC 方法 + inputSchema |
可以记成一句话:模型侧用 JSON 选工具、填参数;MCP 侧用 JSON 发现工具、执行工具。 宿主是翻译器:把 MCP 的 tools/list 映射成模型的 tools 数组,把模型的 tool_calls 映射成 tools/call。
为什么必须是 JSON
Agent 要同时满足三方:
- 模型:训练语料里 JSON 极多,生成合法对象比生成 protobuf 二进制容易得多
- 程序:有成熟的解析、Schema 校验、Diff 与 JSONPath 生态
- 协议:OpenAPI、JSON-RPC、MCP inputSchema 已经绑在同一套类型描述上
纯自然语言无法 fail-fast:括号、引号、多语言混写都会让正则解析崩溃。YAML 对缩进敏感,模型更容易写坏。二进制协议对人类与 LLM 都不友好。于是 JSON 成为「可审计、可校验、可版本化」的默认导线格式——这也是本站工具全部围绕 JSON 的原因:你调试的就是这条导线。
第一跳:工具定义里的 Schema
数据流从「告诉模型有哪些工具」开始。无论走 OpenAI 风格的 tools,还是 MCP 的 tools/list,核心都是一份 JSON Schema(或其子集):
{
"name": "get_weather",
"description": "查询指定城市当前天气,只读",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名,如上海" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
MCP 里同一份约束写在 inputSchema 字段。Schema 同时进两路:校验器拦截非法参数;模型上下文靠 description 决定何时调用。字段说明写得越像业务文档,误调用越少。
第二跳:Function Calling / Tool Calling
宿主把工具列表随 messages 发给模型后,模型不执行代码,只返回结构化调用。典型形态(各厂商字段名略有差异):
{
"role": "assistant",
"tool_calls": [
{
"id": "call_01",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"上海\",\"unit\":\"celsius\"}"
}
}
]
}
注意 arguments 常常是字符串化的 JSON:要先 JSON.parse,再按 Schema 校验,最后才执行。执行结果再以 tool 角色消息回流:
{
"role": "tool",
"tool_call_id": "call_01",
"content": "{\"city\":\"上海\",\"temp_c\":31,\"condition\":\"晴\"}"
}
这一跳解决的是「模型如何伸手」。并行多工具时,数组里会出现多个 tool_calls,宿主可并发执行,再按 id 把结果对号入座。
第三跳:MCP JSON-RPC
若工具不在宿主进程内,而是独立 MCP Server(文件系统、GitHub、内部订单服务),宿主与 Server 之间走 JSON-RPC 2.0。一次只读查询大致三步:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"host","version":"1.0"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"上海"}}}
Server 的成功响应同样是 JSON:content 数组里常见 type: "text",文本内容又是一段 JSON 字符串。于是出现「JSON 套 JSON」——外层是协议信封,内层是业务载荷。调试 MCP 时先分清这两层,再用 Schema 校验内层。
传输可以是 stdio 或 Streamable HTTP,但载荷仍是 JSON 行或 JSON 体。关于 2026 传输与 SDK 是否要改代码,见《MCP 2026 迁移指南》。
一次完整调用的数据流追踪
用户说:「上海今天多少度?」端到端如下:
- Host → MCP Server:
tools/list得到带inputSchema的工具清单(JSON) - Host → 模型 API:映射为
tools[].parameters(仍是 JSON Schema) - 模型 → Host:
tool_calls,arguments为{"city":"上海"} - Host 校验:对照 Schema,缺字段或类型错误则拒绝执行,把错误 JSON 回喂模型
- Host → MCP:
tools/call,params.arguments为对象(不是字符串) - MCP → Host:天气结果 JSON
- Host → 模型:
role: tool的 content 字符串 - 模型 → 用户:自然语言;若下游系统只要结构,再用输出 Schema 约束最终 JSON
用户自然语言
│
▼
Host 编排 ──JSON Schema──► LLM Tool Calling
│ │
│ ▼
│ arguments JSON
│ │
▼ ▼
MCP JSON-RPC ◄──────── 校验通过才执行
│
▼
结果 JSON ──► tool message ──► 模型最终答复
小型脚本可能跳过 MCP,直接在宿主里调本地函数;企业 Agent 则几乎总是「Tool Calling + MCP」叠在一起。选型与生态见《2026 MCP Server 排名》。
校验失败时数据如何回流
JSON 能成为 Agent 的「类型系统」,是因为失败也可以结构化。建议至少两道闸:
| 闸门 | 校验对象 | 失败后怎么回流 |
|---|---|---|
| 执行前 | 模型 arguments | 不调用真实工具;把 Schema 错误写成 tool 结果或系统提示,让模型重填 |
| 回写前 | MCP / 函数返回值 | 截断、脱敏或标错;避免把堆栈原文灌进下一轮上下文 |
开发阶段把 Schema 与 2~3 组正/反例 payload 存进仓库,用 JSON 工具箱本地校验——这和测 REST 契约同一思路,只是消费者换成了模型。
常见问题 FAQ
Tool Calling 和 Function Calling 是一回事吗?
对开发者几乎是同一套数据流:宿主把工具 Schema 发给模型,模型返回带 JSON 参数的调用,宿主执行后再把 JSON 结果塞回对话。Function Calling 是早期 OpenAI 命名;Tool Calling / Tools API 是后续更通用的叫法。
MCP 报文为什么也是 JSON?
MCP 基于 JSON-RPC 2.0:initialize、tools/list、tools/call 的请求与响应都是 JSON 对象。工具的 inputSchema 本身又是 JSON Schema,因此 Host 可以把 MCP 工具一对一映射成模型 API 的 tools 数组。
模型输出的 arguments 是字符串还是对象?
多数 Chat Completions 风格的 API 把 arguments 做成 JSON 字符串,需要宿主 JSON.parse 后再按 Schema 校验。部分新接口直接给对象。无论哪种,落地前都应用同一份 Schema 校验。
为什么不能用 YAML 或 protobuf 替代 JSON?
可以在工具实现内部用任意格式,但模型上下文与跨厂商协议层已把 JSON 当作事实标准。YAML 缩进易错,protobuf 对模型不友好。常见做法是边界用 JSON,内部再转换。
数据流里哪一层最该做 Schema 校验?
至少两处:模型返回 tool_calls 之后、真正执行工具之前;以及 MCP Server 返回结果之后、写回模型之前。前者防幻觉参数,后者防脏数据污染下一轮推理。
如何本地验证这条链路上的 JSON?
把 inputSchema、样例 arguments、工具返回样例保存成 JSON 文件,用 JSON 工具箱在浏览器本地校验 Schema 与数据是否匹配,数据不上传服务器。
总结
AI Agent 离不开 JSON,是因为每一跳都要机器可读:Schema 描述工具,Tool Calling 传递调用,MCP 用 JSON-RPC 把调用送出进程。自然语言只出现在两端的用户界面;中间全是可校验的对象。
建议你从一次真实工具开始:写出 Schema → 打印模型返回的 arguments 字符串并解析 → 若工具在 MCP Server 上,再抓一条 tools/call。三份 JSON 对得上,这条 Agent 才算真正跑通。演进背景见《技术演进全解析》;Schema 样例可在 JSON 工具箱本地校验后再上线。