本系列前几篇已经铺好背景:JSON Schema、Function Calling 与 MCP 的技术演进讲「为什么会出现」;Tool Calling → MCP 数据流讲「字节怎么流」;JSON Schema 是否成为标准 Contract讲「生态是否收敛」。本文聚焦一个更落地的问题:Tool Calling 为什么几乎必然依赖 JSON Schema,以及参数错误、类型错误该如何分类与验证。
模型选工具、填参数时,宿主程序不能「相信运气」——必须在执行前用同一份 Schema 做 fail-fast 校验。否则一次幻觉参数就可能删库、发错邮件、或把脏数据写回下一轮对话。结论先行:JSON Schema 是 Tool Calling 里唯一同时被模型 API、MCP 与宿主运行时共同理解的参数契约;验证要做在「parse 之后、execute 之前」,并把结构化错误回灌给模型重试。
为什么 Tool Calling 依赖 JSON Schema
Tool Calling(与 Function Calling 同义的数据流)本质是:模型从工具列表里选一个,并输出符合约定的 JSON 参数。这里有三方要握手:
- 模型 API:OpenAI、Gemini、Anthropic 的 Tools API 均用 JSON Schema 描述
parameters(或等价字段),部分厂商还在解码阶段用 Schema 约束输出。 - MCP:每个 Tool 的
inputSchema类型即 JSON Schema;Host 映射到模型 API 时常原样透传或做子集裁剪。 - 宿主程序:需要机器可读、可版本化、可 CI 校验的契约——JSON Schema 有 ajv、jsonschema(Python)等成熟实现,比「Prompt 里写 JSON 格式」可靠几个数量级。
没有 Schema 时,宿主只能正则或 Prompt 约定解析 arguments,在规模化 Agent 里会迅速失控。Schema 提供三件事:形状(有哪些字段)、类型(各字段是什么类型)、约束(enum、minimum、pattern 等)——这正是执行工具前必须知道的全部语法层信息。业务语义(「priority=high 是否符合 SLA」)仍要代码校验,但语法层已足够挡住大部分模型幻觉。
用户意图 → 模型读 tools[] 里的 JSON Schema
→ 输出 tool_calls[].function.arguments(JSON 字符串)
→ 宿主 JSON.parse + Schema 校验
→ 通过才调用 MCP / HTTP / DB
Schema 在调用链上的三个位置
| 位置 | Schema 作用 | 典型失败 |
|---|---|---|
| 工具注册(tools / MCP list) | 告诉模型「有哪些工具、各要什么参数」 | Schema 本身非法、draft 不兼容、description 误导模型 |
| 模型输出(tool_calls.arguments) | 约束模型生成的参数 JSON | 缺 required、类型错、编造假字段 |
| 工具返回(写回 messages) | 可选:约束 result 形状,防脏数据进上下文 | Server 返回非 JSON、字段漂移 |
与Structured Output的区别:Structured Output 约束最终用户可见的 JSON 回复;Tool Calling 的 Schema 约束中间步骤的执行参数。两者可共用同一套 Schema 定义工具(Pydantic / Zod 生成),但校验时机不同——Tool arguments 必须在每次 tool_calls 后、执行前校验。
参数错误:缺字段、多字段、名错、语法错
参数错误指 JSON 能 parse,或 parse 前就坏了,但不符合 Schema 对「有哪些键、是否必填」的约定:
| 错误类型 | 示例 | Schema 关键字 | 处理建议 |
|---|---|---|---|
| 缺 required 字段 | Schema 要求 title,arguments 只有 priority | required | 回灌错误,让模型补全;检查 description 是否写清必填 |
| 多余字段 | 模型编造 urgent: true,Schema 未定义 | additionalProperties: false | OpenAI strict mode 常强制;非 strict 时宿主应 strip 或 reject |
| 字段名拼写错误 | titel 而非 title | properties 键名 | 加强 description;enum 工具名与参数名保持一致 |
| JSON 语法错误 | 尾随逗号、单引号、未闭合括号 | (parse 层) | 先 JSON.parse,失败则整段回灌;考虑 Structured Output 降低语法错 |
| 空 arguments | {} 但 Schema 有 required | required + minProperties | 无参工具应显式 properties: {} 且 required 为空 |
// Schema 片段
{
"type": "object",
"properties": {
"ticket_id": { "type": "string", "description": "工单 ID" },
"note": { "type": "string" }
},
"required": ["ticket_id"],
"additionalProperties": false
}
// 模型输出(缺 ticket_id)→ 校验失败
{ "note": "请尽快处理" }
类型错误:类型不匹配、enum、嵌套与 coercion
类型错误指字段存在,但值的 JSON 类型或格式不符合 Schema:
| 错误类型 | 示例 | 常见原因 |
|---|---|---|
| 基本类型错 | limit: "10" 应为 number | 模型习惯把数字当字符串输出 |
| enum 违规 | priority: "urgent",enum 只有 low/medium/high | description 未列举合法值 |
| 数组/对象嵌套错 | 应为 tags: [] 却给了字符串 | 复杂 Schema 超出模型子集能力 |
| 格式 string 违规 | email 不符合 format: email | 幻觉邮箱、日期格式各国混用 |
| oneOf/anyOf 不满足 | 多态参数哪个分支都对不上 | Schema 过复杂,目标 API 子集不支持 |
关于 coercion(类型强制):部分校验库允许 "10" 自动转为数字 10。Agent 生产环境建议默认关闭 coercion——模型应学会输出正确类型;否则 silent coercion 会掩盖系统性类型漂移,并在业务层引发更难查的 bug。若必须兼容,在 Schema 旁文档化并在 CI 用样例锁定行为。
// 类型错误示例
Schema: { "limit": { "type": "integer", "minimum": 1, "maximum": 100 } }
模型: { "limit": " fifty " } // string,且非数字 → 校验失败
验证方法:语法、arguments、strict mode
1. 校验 Schema 本身是否合法
在注册工具前,用元 Schema(draft 2020-12 等)验证你的 parameters / inputSchema 文档无语法错误。可在 JSON 工具箱浏览器端本地完成,避免把非法 Schema 发给模型 API。
2. 校验 arguments 是否符合 Schema
宿主在 JSON.parse(arguments) 成功后,用与注册时同一份 Schema 校验对象。常见库:
- JavaScript / TypeScript:ajv(注意 draft 与
strict选项) - Python:jsonschema、Pydantic(
model_validate前先 parse JSON) - 从代码生成 Schema:Zod / Pydantic → JSON Schema,单一源避免漂移
3. 厂商 strict mode 与 API 级约束
OpenAI strict: true 要求 Schema 满足更严子集(如所有 object 显式 additionalProperties: false、required 覆盖全部 properties)。这能在模型生成阶段减少参数错误,但不能替代宿主侧二次校验——API 子集各厂商不同,详见Contract 一文的方言说明。
4. 样例驱动与 CI
为每个工具维护「合法 arguments 样例 + 故意错误样例」,在 CI 跑 Schema 校验断言。Schema 变更等同 API 破坏性变更,应版本化。
端到端验证流水线与错误回灌
推荐最小流水线(与数据流一文的「校验与回流」衔接):
1. tools/list 或静态注册 → 验证每个 inputSchema 语法
2. 收到 tool_calls → JSON.parse(arguments)
├─ parse 失败 → role: tool 消息写「JSON 语法错误: …」→ 让模型重试
└─ parse 成功 → ajv/jsonschema 校验
├─ 失败 → 结构化错误列表(缺字段、类型、enum)
│ → 作为 tool result 或 user 消息回灌
└─ 成功 → 执行业务 + 可选业务规则校验
3. 工具返回 → 可选对 result Schema 校验后再 append 到 messages
4. 日志:保留 schema 版本、arguments 原文、校验错误码(勿记录密钥)
错误回灌要点:给模型的反馈要机器可读且具体——「ticket_id is required」比「参数不对请重试」有效得多。部分框架把校验错误格式化成 JSON 再塞回 tool role,模型下一轮更容易 Self-correction。
执行层仍要做鉴权与幂等——Schema 只保证形状,不保证「这个 ticket_id 是否属于当前用户」。
落地建议
- 单一 Schema 源:Pydantic / Zod 定义 → 生成 MCP inputSchema 与 OpenAI tools,避免三份定义。
- description 当 Prompt 写:
description直接影响模型是否填对 enum 与 required;Code Review Schema 与 Review API 同等重要。 - 简 Schema、严校验:复杂 oneOf/$ref 深度按目标 API 子集裁剪;校验失败快速 fail,不要 silent fix。
- 两处必验:tool_calls 后、execute 前;MCP 返回后、写回模型前(若 result 会进上下文)。
- 本地先验:上线前在 JSON 工具箱粘贴 Schema + 样例 arguments,确认错误信息可读。
- 与 Structured Output 分工:用户-facing 回复用 response Schema;工具参数用 tools Schema——勿混为一份。
常见问题 FAQ
Tool Calling 可以不用 JSON Schema,只用自然语言描述参数吗?
原型可以,生产不建议。自然语言无法 fail-fast、无法 CI 版本化,模型易漏字段或类型漂移。主流 API 与 MCP 已默认 Schema 化。
arguments 是字符串还是对象?
多数 Chat Completions 风格 API 把 arguments 做成 JSON 字符串,宿主需 JSON.parse 后再校验。部分新接口直接返回对象;无论哪种,落地前用同一份 Schema 验证。
校验失败应该重试几次?
常见做法 1–3 次带错误回灌的重试,仍失败则降级为澄清问题或人工介入。无限重试会烧 token 且可能循环幻觉。
ajv 与 Pydantic 选哪个?
Node/TS 宿主用 ajv 直接验 JSON Schema;Python 业务若已是 Pydantic 模型,可 Schema 生成 + 运行时 model_validate。关键是与发给模型的 Schema 同源。
strict mode 开启后还要宿主校验吗?
要。strict 减少模型侧错误,不防 MCP 返回脏数据、不防 Schema 与代码实现漂移、不防业务规则违规。
如何本地验证 Schema 与 arguments?
在 JSON 工具箱浏览器端粘贴 Schema 与样例 JSON,本地校验语法与结构匹配,数据不上传服务器。
总结与下一步
Tool Calling 依赖 JSON Schema,因为它是模型、MCP 与宿主唯一能共享、可校验的参数契约。参数错误(缺、多、错名、语法)与类型错误(类型、enum、嵌套)应在 execute 前被 Schema 拦截,并通过结构化错误回灌让模型 Self-correction。
建议下一步:选一条真实工具链(如工单创建),写清 Schema + 合法/非法样例,在 JSON 工具箱本地验通后接入 Agent;系列阅读顺序:演进 → 数据流 → Contract → 本文(验证)。