工具参数看起来是合法 JSON,但 API 仍被拒绝,或者 MCP 已返回结果,模型却无法继续。
最快解法:把 AI Agent JSON 数据流拆成“语法、Schema、工具选择、权限执行、调用关联、结果验证”几层,逐层记录并验证,不要只检查 JSON 能不能解析。
这篇文章适合三类人:
- Agent 开发者:需要理解调用链中 JSON 的真实角色。
- 排障工程师:正在定位参数、状态、权限或工具结果错误。
- 平台架构师:需要设计统一事件、日志和 Schema 版本管理。
AI Agent JSON 数据流到底传递了什么?
AI Agent 并不是依赖 JSON 完成推理,而是依赖 JSON 及 Schema,在模型、编排器、工具和 API 之间传递可验证的机器数据。
一个完整调用通常可以抽象为:
用户请求
↓
模型选择工具并生成参数
↓
编排器解析与校验
↓
权限判断、API 执行
↓
工具结果结构化
↓
模型接收结果并继续推理
↓
业务层验证最终动作
这里至少有四个责任主体:
| 层级 | 主要责任 | 常见故障 | 你应该记录什么 |
|---|---|---|---|
| 模型生成层 | 选择工具、生成调用名与参数 | 选错工具、字段误解、参数缺失 | 模型版本、工具清单、原始调用 |
| 应用编排层 | 解析、Schema 校验、权限和状态管理 | 调用 ID 丢失、历史不完整、错误吞掉 | interaction ID、call ID、Schema 版本 |
| 工具执行层 | 访问 API、数据库或系统命令 | 认证失败、授权不足、资源状态错误 | HTTP 状态、业务错误码、耗时 |
| 协议与业务层 | 传递结果、验证业务含义 | MCP 结果不兼容、结果错配 | content、structuredContent、最终状态 |
OpenAI 的工具调用文档将工具定义为由 JSON Schema 描述的函数接口;模型可以提出工具调用,但你的应用仍需要提取工具名和参数后自行执行。OpenAI Function Calling 官方文档 对这一责任边界有明确说明。Google 的 Function Calling 流程也把“执行函数”放在应用侧,而不是模型侧。Gemini Function Calling 官方文档
这就是为什么同一个错误不能只归因于“模型输出了坏 JSON”。模型可能生成了语法正确的请求,而真正失败点在后面的权限、状态或结果关联。
第一关:JSON 能解析,但 Schema 仍可能不合规
先区分两个概念:
{
"path": "/Users/demo/project",
"timeout": "30"
}
这段内容可以是合法 JSON,但如果 Schema 要求 timeout 必须是数字,那么它仍然不符合结构约束。
典型差异包括:
- 字段缺失:Schema 要求
path和command,模型只生成了path。 - 类型错误:接口要求数字、布尔值或数组,实际传入了字符串。
- 额外字段:执行器拒绝未知字段,模型却添加了未声明的参数。
- 枚举错误:字段结构正确,但值不在允许范围内。
- 平台子集差异:不同模型平台支持的 JSON Schema 关键字和严格模式并不完全相同。
JSON Schema 的作用是描述和验证数据结构,不是把任意业务规则都写进一个声明文件。官方规范目前以 2020-12 为主版本;同时,Schema 本身也不能表达所有跨字段的业务语义,因此通常还需要第二层程序验证。JSON Schema 规范说明
例如,下面的 Schema 可以限制类型和必填字段:
{
"type": "object",
"properties": {
"path": {
"type": "string"
},
"timeout": {
"type": "integer",
"minimum": 1
}
},
"required": ["path"]
}
但它无法独立判断这个路径是否属于当前租户,也无法知道用户是否有权执行该路径下的命令。你需要把校验拆成:
- JSON 语法解析。
- JSON Schema 结构验证。
- 工具名称与任务上下文验证。
- 权限、资源和业务规则验证。
不要为了修复工具选错问题,继续无限收紧字段类型。Schema 能解决“参数长什么样”,不能完全解决“这个工具是不是该被调用”。
第二关:参数正确,工具仍可能选错
Schema 合规不代表模型理解了工具用途。
假设你同时暴露了:
read_mac_filewrite_mac_filedelete_mac_file
如果三个工具的描述都写成“处理 Mac 文件”,模型即使生成完全合规的参数,也可能调用了危险或不相关的工具。
改进工具选择,应优先调整以下内容:
✅ 工具命名:使用明确动作和对象,例如 read_project_config,不要使用 file_action。
✅ 工具描述:说明适用场景、不适用场景、是否产生副作用。
✅ 候选范围:根据任务阶段减少暴露给模型的工具数量。
✅ 上下文约束:在用户请求、系统状态和工具说明中保持对象名称一致。
✅ 权限前置:高风险工具先进入审批状态,不让模型直接获得执行权。
❌ 不要只增加 required 字段来修复工具选择。
❌ 不要把多个动作塞进一个工具,再期待模型通过自然语言参数自行分流。
❌ 不要把认证失败包装成“参数不正确”,这会误导模型进行无效重试。
Tool Calling 和 Function Calling 在不同平台的消息字段可能不同,但排障原则一致:模型负责提出调用意图,应用负责决定是否执行。不要把某个平台的 tool_call、function_call 或响应字段直接复制到另一个平台。
第三关:参数正确,API 为什么仍然拒绝?
当请求已经通过 JSON 和 Schema 验证,下一层通常是执行问题。
常见原因有:
- 认证失败:令牌过期、密钥错误、环境变量未注入。
- 授权不足:身份存在,但没有访问目标资源的权限。
- 资源状态不允许:任务已完成、锁已存在、文件已删除或实例处于停止状态。
- 网络与超时:DNS、代理、TLS、带宽或上游服务不可用。
- 业务规则冲突:金额、区域、配额、版本或依赖条件不满足。
执行器必须返回结构化错误,而不是只返回一句“失败了”。
建议至少保留:
{
"ok": false,
"error": {
"kind": "authorization",
"code": "RESOURCE_ACCESS_DENIED",
"retryable": false,
"message": "当前身份无权访问目标资源"
}
}
这里的 retryable 很关键。认证错误通常需要刷新凭证,授权错误需要人工处理,网络超时可能允许有限重试,业务冲突则可能要求重新读取状态。若这些信息丢失,模型只能根据模糊文本猜测,容易形成重复调用循环。
调用 ID 和历史状态为什么不能省略?
场景案例很常见:模型同时发起两个工具调用,编排器收到结果后只按数组顺序回传,没有保存每个调用的 ID。第一个结果较慢,第二个结果先返回,模型随后把数据错配到错误任务上。
调用 ID 的职责不是装饰字段,而是关联键。它至少要连接:
用户请求
→ 模型响应
→ 工具调用
→ API 请求
→ API 响应
→ 工具结果
→ 模型下一轮输入
状态丢失后,常见后果包括:
- 同一个动作被重复执行。
- 并行工具结果被交叉配对。
- 多轮循环无法判断哪个调用已经完成。
- 重试时丢失原始参数和权限决策。
- 无法从日志还原一次完整故障。
如果采用无状态调用模式,你需要自行保存完整历史。Google 的官方文档明确要求后续请求带上之前的用户输入、模型生成步骤以及函数结果;如果只保存最终文本,就可能破坏下一轮工具调用所需的上下文。Gemini 交互状态说明
建议每次事件至少记录:
{
"trace_id": "脱敏后的链路 ID",
"interaction_id": "平台交互 ID",
"call_id": "工具调用 ID",
"tool_name": "工具名称",
"schema_version": "工具 Schema 版本",
"sequence": "事件顺序",
"status": "generated|validated|executed|returned"
}
不要用数组下标替代 call_id。数组顺序是传输表现,调用 ID 才是业务关联。
MCP 工具结果如何通过 JSON 回到模型?
Model Context Protocol 将工具定义和调用结果进一步标准化。MCP 工具通常包含 name、description、inputSchema,也可以提供 outputSchema;工具调用使用 tools/call,结果则可能包含 content、structuredContent 和 isError。MCP Tools 官方规范
一个结构化结果可以类似这样:
{
"content": [
{
"type": "text",
"text": "{\"status\":\"ready\",\"port\":8080}"
}
],
"structuredContent": {
"status": "ready",
"port": 8080
},
"isError": false
}
这里有三层含义:
content是兼容性较好的内容载体,客户端或模型可以读取其中的文本。structuredContent是供程序直接消费的结构化对象。outputSchema描述结构化结果应该满足什么约束。
MCP 规范建议,在返回结构化内容时同时保留序列化后的文本内容,以兼容还没有完整支持结构化字段的客户端。客户端也应该主动验证 structuredContent,不能看到字段存在就直接写入数据库。MCP Schema Reference
另一个容易忽略的边界是:MCP 的具体字段不能和其他平台的 Function Calling 消息格式混用。你可以把它们统一映射到内部事件模型,但不能假设所有客户端都支持相同字段。
FAQ:排障时最容易混淆的五个问题
为什么 AI Agent 普遍使用 JSON?
因为 JSON 便于序列化、传输、记录和验证,配合 JSON Schema 后可以把自然语言工具调用转换为明确字段。它解决的是跨组件通信问题,不是模型内部推理问题。模型仍可能选错工具或生成错误业务意图,所以 JSON 不能替代权限和业务验证。
Function Calling 的 JSON 由谁生成和执行?
模型通常生成工具名称及参数,应用编排器负责解析、校验、授权并执行。API、数据库或 macOS 命令都不应该由模型直接执行。执行结果也必须由编排器重新包装,再按平台要求回传给模型。
MCP 工具结果怎样返回给模型?
MCP 服务器可以返回文本 content 和结构化的 structuredContent,并用 outputSchema 描述后者。为了兼容客户端,最好保留可序列化文本;为了避免下游写入错误,客户端仍需按 Schema 验证结构化字段。
JSON 合法为什么 Agent 仍然调用失败?
因为语法合法只代表解析器能读取,不代表 Schema 合规,也不代表工具选择正确、身份有权限、资源状态有效。排查时要分别查看解析错误、结构错误、权限决策、业务错误码和结果关联状态。
工具调用 ID 丢失会发生什么?
调用 ID 丢失后,系统无法可靠地把工具结果绑定到原始请求,尤其容易在并行调用、重试和长对话中出现结果错配、重复执行或循环中断。日志必须把调用 ID 贯穿模型、执行器和结果回传阶段。
建立统一事件日志:不要只保存最终答案
如果你现在只能看到“Agent 失败”或“工具返回异常”,日志设计还不够用。建议把一次调用拆成不可变事件,而不是不断覆盖同一条任务记录。
至少保留以下事件:
request_received:收到用户任务和租户信息。tool_selected:模型选择了哪个工具。arguments_generated:模型生成的原始参数。schema_validated:使用哪个 Schema 版本完成验证。permission_decided:权限判断结果及策略版本。execution_started:实际访问的 API 或系统资源。execution_finished:成功、失败、超时和业务错误码。result_validated:MCP 或工具结果是否符合输出 Schema。model_resumed:结果是否成功回传给模型。business_committed:最终动作是否真正提交。
你还应把日志和运行环境状态关联起来。对于依赖 macOS 工具、Xcode、签名、钥匙串或本地文件权限的 Agent,单看模型消息是不够的,还要记录系统版本、工具版本、环境变量注入状态、网络出口和进程权限。
需要反复复现跨层故障时,可以先阅读 Kvmzen 帮助中心,确认远程环境的连接、权限与运行方式,再把相同的 Schema 错误、权限拒绝、调用 ID 丢失和 MCP 结果不匹配纳入回归测试。
上线前的调用链验收清单
把下面清单交给开发、测试和运维共同验收。每项都要能在日志或测试报告中找到证据。
- [ ] 原始模型调用已保存,未只保留最终自然语言答案。
- [ ] 每个工具都有唯一名称、清晰描述和明确副作用说明。
- [ ] 输入参数先通过 JSON 解析,再通过对应 Schema 校验。
- [ ] Schema 版本随每次工具调用一起记录。
- [ ] 工具选择错误与参数类型错误分开统计。
- [ ] 执行器独立处理认证、授权、网络、超时和业务规则。
- [ ] 错误结果包含错误类别、错误码和是否可重试字段。
- [ ] 每个工具调用都有稳定的
call_id或内部关联 ID。 - [ ] 并行调用按 ID 配对,不按数组顺序配对。
- [ ] MCP 结果同时检查
content、structuredContent和isError。 - [ ] 存在
outputSchema时,客户端会再次验证结果。 - [ ] 不支持结构化字段的客户端仍能读取兼容文本。
- [ ] 长任务可以从事件日志恢复,而不是依赖进程内存。
- [ ] 依赖 macOS 工具的测试记录了系统权限和环境状态。
- [ ] 失败调用不会因为模糊错误文本触发无限重试。
如果你正在做 MCP 工具结果验证,优先验收输出 Schema 和客户端兼容性;如果正在排查远程 macOS 上的 Agent 执行,则要把环境状态与 trace_id 绑定,而不是只复制模型返回内容。
对于只在本地开发机上运行的 Agent,问题通常被本机缓存、权限和已安装依赖掩盖。与 Windows 或 Linux 云主机相比,直接在 macOS 环境中复现 Xcode、钥匙串、签名和系统工具调用,往往更接近真实故障,但也会带来远程连接、权限隔离和环境固定成本。
因此,当前方案如果依赖个人 Mac,常见缺点是环境不可复现、多人共享困难、日志与系统状态分散;如果依赖通用云主机,又可能缺少 macOS 专属工具链,导致“API 调用成功、真实执行失败”。需要持续重现跨层故障时,使用 Kvmzen 的远程 Mac 环境,可以把固定依赖、隔离日志和远程复现放在同一套测试流程里;但如果你的任务是长期满负载运行,或必须接入特定物理设备,自购 Mac 仍可能更合适。你可以先查看 Mac 云租用方案,再按任务周期和设备依赖决定是否租赁。
