AI Agent 为什么开始需要 JSON Schema?从 Tool Calling 到结构化 Agent 工作流
2024 年以前,多数「Agent」是一段 prompt 加两三个函数。2026 年不行了:Function Calling 要描述参数,MCP 要注册工具,Structured Outputs 要约束返回值,A2A 要协商能力,Agents SDK 要用 handoff 对象做路由。这些层各自独立演进,却都落回同一份契约语言——JSON Schema。本文讲清它为什么从「文档里的校验语法」变成了 Agent 的运行时,以及从单次 Tool Calling 走到多步工作流时,该把 Schema 写在哪里。
阅读前速览
| 阶段 | 没有 Schema 时 | 有 Schema 时 |
|---|---|---|
| 单次 Tool Calling | 参数是字符串,类型靠运气,多一个字段也能跑 | parameters / inputSchema 在调用前拦截 |
| 多步 Agent | 下游靠解析散文,handoff 靠「提到了 billing」 | handoff 对象带 output_type,路由可测试 |
| 跨协议复用 | Function Calling、MCP、A2A 各写一份,越改越偏 | Git 里一份 Schema,导出为三种适配器 |
结论先说:JSON Schema 不是「给模型看的文档」,而是 Agent 主机在每一步执行前、交接时、评测时都要跑的校验器。
为什么是「开始」需要,而不是一直需要?
早期 Tool Calling 能活下来,是因为链路短:模型吐一个函数名和一段参数,宿主执行,结果塞回对话。错了就重试,用户能看见。Schema 最多写在 README,属于「最好有」。
2025–2026 年链路变长了。一个工单 Agent 可能先分类,再查订单,再决定退款或转人工,再把对象交给另一个 Agent。中间任何一跳如果只是「看起来像 JSON」,后面的步骤会把错误当成事实。这时 Schema 从文档变成了运行时门禁:过不了就不往下走。
另一条压力来自协议层。我们在 MCP Tool Schema vs Function Calling 里写过:厂商 API 用 parameters,MCP 用 inputSchema。A2A Extensions 又把能力写进 Agent Card。三套协议不共享运行时,但都认 JSON Schema 子集。你不在一开始选定 Schema,就会在三个地方各维护一份「几乎一样」的定义,然后在第三周发现 order_id 有的是 string、有的是 number。
DevDay 2026 预测 把同一件事说得更直:Structured Outputs、Function Calling、Agent 运行时正在收成同一根 JSON 管。Schema 是这根管的口径。口径不定,后面接什么协议都漏。
Tool Calling 看起来已经有「结构」,缺口在哪?
Function Calling 的 tools[] 里本来就有一份类似 Schema 的 parameters。很多团队因此觉得「我们已经结构化了」。生产里真正出事的,往往是下面四类。
第一,arguments 仍是字符串。模型返回的是:
{
"tool_calls": [{
"id": "call_8f2a",
"type": "function",
"function": {
"name": "create_refund",
"arguments": "{\"order_id\":\"A-1042\",\"amount\":\"99.00\",\"reason\":\"late\"}"
}
}]
}
宿主必须再 JSON.parse 一次。amount 是字符串还是数字,reason 是否在枚举里,多出来的 note 能不能进账务系统——parameters 若不在主机侧强制执行,这些都靠「模型今天心情好」。这不是 Schema 写了就自动生效;strict: true 只约束模型侧,宿主仍要自己校验。
第二,工具结果没有 Schema。role: tool 大多是自由字符串。下一步模型会把这段文字再「理解」一遍。查询成功、库存为 0、权限不足,三种情况如果外形不同,多步工作流会在第三跳分叉到错误分支。
第三,类型宽到失去筛选力。只写 type: string 的 priority,模型可以输出 "urgent!!!"。没有 enum、required、additionalProperties: false 的 Schema,只是一份注释。
第四,单次成功掩盖了工作流失败。第一步 lookup_order 多返回一个 status: "ok",第二步退款 Agent 把 status 当成订单状态。本地试一条快乐路径看不出来,回归集里才会炸。Schema 的价值在这里:它让「这一跳能交给下一跳的对象」变成可断言的测试,而不是对话记录。
调试这类嵌套 JSON 时,先在本地 格式化并做语法检查,再和上周能跑的定义做 Diff。不要把带用户数据或密钥的 tool 响应贴到公开 JSON 网站。
JSON Schema 在 Agent 里其实有四层角色
把 Schema 只当成「工具参数说明书」,会漏掉 2026 年真正在长的三层。完整图是:
- 工具入参。 Function Calling 的
parameters、MCP 的inputSchema。决定模型「能请求什么」。 - 工具出参。 查询、写入、外部 API 的返回对象。决定下一跳「能相信什么」。这一层现在最常缺。
- Agent 交接(handoff)。 分类结果、意图、置信度、要交给谁。Agents SDK 里这是路由依据,不是聊天摘要。
- 工作流状态与评测。 检查点、trace、eval fixture。长任务要能从 JSON 恢复,而不是从对话记录里猜进度。
入参 Schema 示例(退款工具):
{
"$id": "https://example.com/schemas/tools/create_refund.json",
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
"amount": { "type": "number", "exclusiveMinimum": 0 },
"reason": { "type": "string", "enum": ["late", "damaged", "duplicate", "other"] }
},
"required": ["order_id", "amount", "reason"],
"additionalProperties": false
}
handoff Schema 示例(分诊 → 账务):
{
"$id": "https://example.com/schemas/handoffs/triage_to_billing.json",
"type": "object",
"properties": {
"intent": { "type": "string", "enum": ["refund", "exchange", "human"] },
"order_id": { "type": "string" },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"from": { "const": "triage" },
"to": { "const": "billing_agent" }
},
"required": ["intent", "order_id", "confidence", "from", "to"],
"additionalProperties": false
}
两份文件的共同点比差异更重要:都是对象、都有枚举、都禁止多余字段、都能进 Git、都能被 ajv(或等价库)在 CI 里跑。模型换快照、协议换 MCP 还是 Function Calling,校验器不用重写。关键字怎么写,见 JSON Schema 教程。
从单次调用到结构化工作流,Schema 卡在哪几道门
单次 Tool Calling 只有一扇门:参数合不合法。结构化 Agent 工作流至少有四扇。
门 1:选择工具之前。 目录超过十个工具后,把整份 Schema 塞进一次请求又贵又容易选错。先检索再调用时,检索结果本身也应是带 Schema 的短名单,而不是一段模型生成的工具介绍。
门 2:执行之前。 parse → validate → execute。validate 失败就返回结构化错误(缺字段、类型错、枚举外),不要把原始字符串丢给下一跳。这一步同时挡住 prompt 注入式的额外键。
门 3:交接之前。 triage 必须交出符合 handoff Schema 的对象,billing 才启动。路由条件写成 intent === "refund" && confidence >= 0.8,而不是「回复里出现了退款两个字」。
门 4:恢复与评测。 长任务把检查点写成合法 JSON:
{
"run_id": "run_20260903_014",
"step": "await_refund_ack",
"state": {
"order_id": "A-1042",
"intent": "refund",
"amount": 99.0
},
"schema": "https://example.com/schemas/state/refund_flow.json"
}
评测集也是同一类对象:给定输入,断言输出通过 Schema,并且关键字段相等。Agent「变笨了」时,先 Diff 本周与上周的 Schema 和 fixture,再怀疑模型。很多事故是多了一个 required,或少了一个 enum 值。
一份 Schema,如何喂给 Function Calling、MCP 和 A2A
协议层不要各写各的。推荐的方向是一份规范文件,三种投影:
| 协议 | Schema 落点 | 从同一文件怎么导出 |
|---|---|---|
| Function Calling | function.parameters |
拷贝 type/properties/required,打开 strict |
| MCP | inputSchema |
同一对象作为 tools/list 的一部分 |
| Structured Outputs | text.format.json_schema |
handoff / 最终答复用同一套对象定义 |
| A2A | Agent Card / Extension metadata | 对外能力声明引用同一 $id |
投影时注意子集差异:厂商 strict 模式往往要求所有 property 都在 required 里,且禁用部分 oneOf / $ref。规范文件可以更完整,导出器按目标裁剪,并在 CI 里对「规范 vs 投影」做一次结构 Diff,避免手改某一侧。
MCP 负责工具供给,Function Calling 负责模型如何表达调用意图,A2A 负责Agent 之间如何发现和协商。Schema 不替代这三层,它是三层共用的字段字典。这一点和前两篇的结论一致,只是把「字典」从概念落成仓库里的文件。
落地:把 JSON Schema 当成单一事实来源
可以立刻做、也不依赖任何一次发布会的几件事:
- 在仓库建
schemas/tools/、schemas/handoffs/、schemas/state/。每个文件有稳定$id和版本。 - 用脚本导出 Function Calling / MCP / Structured Outputs 三种适配,禁止在适配文件里手改字段。
- CI 用 ajv(或等价库)校验:示例 payload、历史 fixture、线上抽样(脱敏后)必须通过当前 Schema。
- 新工具默认
additionalProperties: false和显式enum。需要开放扩展时,单独开metadata对象,不要把根对象放宽。 - 工具超过十个,加路由或检索,不要把目录整包塞进一次请求。
- 宿主同时接受
arguments为字符串或对象,避免厂商把类型收紧后全站告警。 - 含密钥或用户数据的 payload 只在浏览器本地看:格式化、Diff,零上传。
目录可以很薄。重要的是先有文件,再写 prompt。prompt 里的「请返回 JSON」无法在 CI 里失败;Schema 可以。
常见误区
- 把 Schema 写进系统提示就算完成。 模型侧约束和主机侧校验是两件事。没有主机校验,
strict关掉或换模型快照后,契约就消失。 - 只校验入参,不校验出参和 handoff。 多步 Agent 的事故更多发生在「上一步返回了意料外的形状」。
- 为每个协议手写一份「差不多」的 Schema。 第三周一定分叉。导出,不要复制。
- 用超宽
type: object逃避设计。 开放对象等于没有门。真正要扩展,用版本号或显式metadata。 - 把 Schema 当成模型训练语料。 它首先是给校验器和测试跑的。描述写清楚有助于选工具,但不能代替
enum和required。 - 在可视化编排里改字段,却不回写 Git。 画布上的节点不是事实来源。先导出 Schema,再让画布读文件。
FAQ
没有 Schema,Tool Calling 能不能上线?
原型可以。工具固定、步骤少于三、失败可由人看见时,字符串参数往往够用。一旦出现分支、自动重试或第二个 Agent,没有 Schema 的错误会静默传播。
JSON Schema 会不会被 Structured Outputs 或 MCP 取代?
不会。它们是消费 Schema 的层,不是另一套类型系统。2026 年的趋势是更多协议引用同一份 Schema,而不是发明新的字段语言。
该用 draft-07 还是 2020-12?
跟你的校验库和厂商子集走。Agent 场景真正用到的是 type、properties、required、enum、additionalProperties。选一个能在 CI 和模型 API 之间对齐的子集,比追最新 draft 更重要。
私有内部 API,还要 MCP 和 A2A 吗?
不一定。内部闭环用 Function Calling + Schema 即可。预留 MCP / A2A 的价值在于:当你把同一份 Schema 写进仓库后,以后要插协议只是加导出器,不用重新发明字段。
怎么调试失败的 tool / handoff JSON?
在浏览器本地格式化、对照 Schema 看缺了哪个 required,再和上一版 Diff。JSONSort 不上传数据。不要把含密钥的响应贴到公开站点。
结论
AI Agent「开始」需要 JSON Schema,不是因为模型突然读不懂自然语言,而是因为工作流变长、协议变多、错误必须在主机侧被拦住。Tool Calling 给出调用意图;Structured Outputs 给出最终对象;MCP 和 A2A 给出供给与协商。把这几层粘在一起的,是仓库里那一组可以校验、可以 Diff、可以导出的 Schema 文件。
从今天起与其先写一段更长的 prompt,不如先写 schemas/tools/create_refund.json。模型会换,协议会改,口径应先稳定。
相关文章
- JSON Schema 是什么?用法与教程 关键字、required 与 additionalProperties
- MCP Tool Schema vs Function Calling 工具 JSON 的协议边界
- A2A Extensions 与 JSON 协议 多 Agent 互操作的第三层
- OpenAI DevDay 2026 重点预测 三层能力如何收成一根 JSON 管
- JSONSort 本地 JSON 工具箱 格式化、语法校验、Diff
- 博客全部文章 更多 JSON 与 AI 开发笔记
更新日志: 初稿发布