这篇文章面向正在处理 Agent 参数错误、JSON 解析失败和工具执行异常的开发与运维团队。文章沿输入契约、模型生成、API 响应、解析验证、工具执行和结果回传 6 个环节拆解问题,并提供两张排障对比表、可勾选检查清单与最小复现流程。
先定位 6 个数据流环节,再修 AI Agent JSON 错误;不要一看到解析失败就重试或归因于模型。 适用条件是系统同时使用 Structured Output、Function Calling、JSON Schema 和多步骤工具调用,需要判断错误究竟发生在输入、响应、验证还是执行阶段。
这篇文章适合三类人:被解析错误困扰的后端开发者,可以按错误位置缩小范围;维护多步骤 Agent 的工程师,需要核对状态、调用标识与结果回传;负责生产事故的运维人员,应建立能够串联整条调用链的日志。
先把“坏 JSON”拆成 6 个可能位置
一条失败调用通常不是“模型输出 JSON”这一件事,而是下面这条链路中的某一段出了问题:
输入契约
→ 模型生成
→ API 响应状态
→ JSON 解析与 Schema 验证
→ 工具执行
→ 结果回传与下一轮上下文
如果程序只记录了 JSON.parse() 的异常信息,前面的拒绝、截断、Schema 不兼容,以及后面的权限失败都会被压缩成同一种“JSON 错误”。这会导致团队不断增加重试次数,却没有减少真正的故障。
| 故障位置 | 常见症状 | 首要证据 | 不应立即做的事 |
|---|---|---|---|
| 输入契约 | 请求直接被拒绝,或返回参数验证错误 | 实际发送的 Schema、接口版本、API 错误体 | 不要先修改提示词 |
| 模型生成 | 字段缺失、类型变化、额外文本 | 原始输出、停止原因、拒绝字段 | 不要只保留解析后的对象 |
| API 响应 | 响应状态不是完成,内容不完整 | status、stop_reason、incomplete_details |
不要把所有非 200 视为可重试 |
| 解析验证 | JSON 语法错误或 Schema 校验失败 | 解析器版本、验证错误路径 | 不要更换验证器掩盖差异 |
| 工具执行 | JSON 合法,但订单、路径或账号不可用 | 工具入参、权限结果、资源状态 | 不要让模型自行猜测资源状态 |
| 结果回传 | 下一轮出现上下文丢失或调用 ID 不匹配 | 完整历史、调用 ID、回传顺序 | 不要只回传工具结果文本 |
JSON Schema 的版本也必须显式记录。当前公开规范页面列出的最新正式版本是 Draft 2020-12,且 $schema 用来声明方言;如果应用、测试环境和生产环境采用不同默认方言,同一份 Schema 可能得到不同验证结果。可参考 JSON Schema 规范版本说明 与 JSON Schema 中声明 $schema 的基础指南。
第一步:先让最小 Schema 通过,再恢复约束
输入契约本身无法被平台接受时,模型根本还没有进入“生成坏 JSON”的阶段。排查时应先拿掉复杂的 $ref、深层嵌套、互斥条件和不确定的格式限制,只保留一个对象、少量字段、明确的 type 与 required,确认平台能够接受后,再逐项恢复业务约束。
不同平台对 JSON Schema 的支持并不等同于完整规范。以 Gemini 的 Structured Output 为例,官方文档明确说明它支持 JSON Schema 的一个子集,复杂或深层 Schema 可能被拒绝;因此不能把本地验证器能够接受的 Schema,直接视为模型接口必然能够接受的 Schema。可对照 Gemini Structured Output 的官方限制与支持类型。
| Schema 方案 | 适合场景 | 风险 | 决策 |
|---|---|---|---|
| 最小对象:字符串、整数、布尔值 | 首次接入、故障复现 | 业务约束较少 | ✅ 先用它确认链路 |
带 enum、数组和必填字段 |
稳定的工具参数 | 平台支持子集可能不同 | ✅ 逐项恢复并回归 |
| 大量引用、深层嵌套、复杂组合 | 大型业务对象 | 请求可能在平台侧被拒绝 | ⚠️ 拆成多个工具或阶段 |
| 只依赖提示词要求 JSON | 兼容性测试、旧接口 | 额外文本、字段缺失和类型漂移 | ❌ 不作为生产契约 |
应用层仍然要保留二次验证。严格结构化输出可以降低语法和字段形状问题,但不能证明 account_id 真实存在,也不能证明文件路径可访问;平台文档同样强调,结构合法的输出仍需在应用中验证具体值。Gemini 官方文档也将“语法正确”与“语义正确”分开处理,具体可查看 Structured Output 的验证与错误处理建议。
建议按以下顺序恢复 Schema:
- 先只保留顶层
object。 - 加入业务必需字段,并明确写入
required。 - 为字符串、整数和布尔值补充类型。
- 对固定选项使用
enum,不要让模型自由拼写状态值。 - 最后再加入数组长度、格式、引用和字段组合约束。
- 每恢复一项,就运行同一组最小测试,不要一次性恢复整份生产 Schema。
第二步:先读响应状态,再进入解析器
模型响应被拒绝或截断时,返回体可能不是可解析的业务 JSON。OpenAI 的 Responses API 文档区分了拒绝内容与 response.incomplete,其中不完整响应可以带有 incomplete_details.reason,例如达到输出限制;流式 Function Calling 也可能在响应中断、不完整或取消时结束参数事件。可参考 OpenAI Responses API 流式响应状态说明 和 OpenAI Structured Outputs 官方文档。
因此,解析入口不应是:
data = json.loads(response.output_text)
而应先分支处理:
if response.status == "incomplete":
record("incomplete", response.incomplete_details)
return retry_only_if_transient(response)
if has_refusal(response):
record("refusal", refusal_text=response.refusal)
return handle_refusal()
raw = extract_completed_output(response)
data = json.loads(raw)
这里的关键不是照抄某个平台的字段名,而是建立统一的内部状态,例如:
schema_rejected:请求契约未被接口接受;model_refusal:模型拒绝生成内容;response_incomplete:输出达到限制或被中断;parse_failed:拿到文本后 JSON 解析失败;schema_failed:JSON 合法但不符合 Schema;tool_failed:参数合法但业务执行失败;history_mismatch:多轮调用状态或标识不一致。
⚠️ 重试只能用于明确的瞬时错误,例如网络超时、上游限流或短暂服务异常。拒绝、Schema 不兼容、权限不足和业务资源不存在,重试通常只会增加成本并隐藏根因。
第三步:统一解析器与 Schema 方言
开发环境使用一个验证器,生产环境使用另一个验证器,是 AI Agent JSON 错误中非常隐蔽的一类。常见差异包括:
- 一个验证器默认按 Draft 7 解释,另一个按 Draft 2020-12 解释;
format只被当作描述信息,或被当作强制断言;$ref的解析基准 URI 不一致;additionalProperties的默认行为被误解;- SDK 在发送请求前删除了某些平台不支持的字段,但日志没有保留转换前 Schema。
每一条 Schema 都应记录以下信息:
{
"schema_id": "order-tool-v3",
"schema_dialect": "2020-12",
"validator": "validator-name",
"validator_version": "x.y.z",
"provider_adapter_version": "a.b.c"
}
示例中的版本值只是脱敏占位符,不代表某个真实生产配置。真正重要的是让日志能够回答:哪一份 Schema、经过哪一个适配器、由哪一个验证器,在什么版本下完成判断。
当工具参数出现缺项时,应同时比较 3 份对象:
- 发送给模型的工具定义;
- 模型返回的原始调用参数;
- 进入业务函数前的适配器对象。
若第 1 份已有字段而第 2 份没有,问题在模型响应或工具选择;若第 2 份完整而第 3 份缺字段,问题在 SDK 映射、字段重命名或反序列化逻辑。
第四步:区分“JSON 合法”与“工具可执行”
JSON 合法只代表字符、括号、字符串和数据结构满足语法;Schema 验证通过,也只代表字段形状符合契约。工具真正执行前,至少还要检查资源存在性、权限、字段间关系和业务范围。
例如:
{
"account_id": "acct_redacted",
"path": "/workspace/report.json",
"action": "delete"
}
这段 JSON 可以完全合法,但仍可能出现以下失败:
account_id不存在或已被禁用;- Agent 没有删除权限;
path位于禁止操作的目录;action与当前任务状态冲突;- 工具要求绝对路径,而传入值经过了错误拼接;
- 资源在模型生成后已经发生变化。
工具执行器应把业务校验放在真正副作用之前:
解析参数
→ Schema 验证
→ 资源存在性检查
→ 权限检查
→ 字段关系检查
→ 业务范围检查
→ 执行副作用
→ 记录结果
这也是 Function Calling 与 Structured Output 的边界:前者用于请求应用执行动作,后者更偏向约束最终响应格式。Google 的工具调用文档将流程拆分为工具声明、模型返回调用、应用执行函数和结果回传 4 个阶段,具体可参考 Gemini Function Calling 官方流程说明。
中段 FAQ:按症状缩小范围
输出总是无法通过 JSON 解析,应该先检查哪里?
先检查响应是否完成,以及是否存在拒绝、截断或流式中断,再检查输出中是否混入 Markdown 代码块和解释文本。若请求本身采用宽松提示词,优先切换到平台支持的严格 Structured Output;如果严格模式仍失败,就回到最小 JSON Schema,确认不是契约复杂度导致的拒绝。
工具调用的参数少了字段,如何判断是哪一层丢失?
把工具定义、模型原始调用、适配器输入和业务函数签名并排记录,重点核对 required、参数名称、大小写、嵌套层级和默认值。字段只在适配器之后消失,通常不是模型问题,而是 SDK 映射或应用代码丢失了数据。
已启用 Structured Output,为什么请求仍可能失败?
它可能因 Schema 超出平台支持子集而在请求阶段失败,也可能因拒绝、长度限制或响应中断而没有完整结果。即便最终得到合法 JSON,Structured Output 也不负责确认资源存在、权限有效和业务关系成立,所以应用层校验不能删除。
工具执行完成后,下一轮为什么像是忘记了前面的调用?
检查下一轮请求是否包含完整的模型调用对象、调用 ID、工具结果和必要的历史位置。不要只把工具返回的文本重新放进提示词;部分接口要求特定角色、内容块顺序或签名原样回传,必须按对应接口的状态规则重建消息。
解析成功但工具仍然拒绝执行,通常缺少哪些检查?
通常是语法层已经通过,但业务层没有通过。订单、账号、路径、权限、状态和字段组合都可能让工具拒绝执行。应把业务错误作为独立状态记录,并向模型返回经过脱敏的结构化错误,而不是把异常堆栈直接拼入下一轮上下文。
第六步:用最小复现和关联日志收尾
最小复现的目标不是“让模型再生成一次”,而是固定足够多的变量,使团队能够判断错误在哪一段发生。建议保留以下字段:
trace_id
parent_run_id
request_id
model_name
interface_name
sdk_version
adapter_version
schema_id
schema_dialect
validator_name
validator_version
response_status
stop_reason
refusal_present
incomplete_reason
raw_output_hash
parse_error_path
schema_error_path
tool_name
tool_call_id
execution_status
retry_count
created_at
输入和输出都应脱敏,尤其要处理账号、令牌、路径、订单号和文件内容。原始响应可以保存加密副本或哈希关联,但不能为了排障把生产凭据写入普通应用日志。
可勾选的验收清单如下:
- [ ] 请求发送前记录了最终 Schema,而不是只记录业务对象;
- [ ] Schema 声明了方言,且开发、测试、生产验证器版本可追踪;
- [ ] API 响应状态、停止原因和拒绝信息先于 JSON 解析处理;
- [ ] 流式响应没有把中间事件误当成最终 JSON;
- [ ] 工具调用的名称、参数和调用 ID 被完整保留;
- [ ] 工具结果按接口要求回传,未丢失必要上下文;
- [ ] Schema 校验之后仍有权限、资源和业务范围校验;
- [ ] 重试仅针对已确认的瞬时错误;
- [ ] 最小复现包含脱敏输入、Schema、接口版本和验证错误;
- [ ] 修复后加入同一份回归样本,而不是只验证一次成功请求。
如果系统通过 MCP 连接多个工具,还应记录 MCP 客户端、服务器、工具名称和返回内容的边界。MCP 官方规范将工具定义、输入 Schema、调用结果和错误处理分别列为协议对象,不能把 MCP 层的错误与模型层的 JSON 解析错误混在一起。可查看 MCP 工具规范 与 MCP 官方规范总览。
输入和输出都应脱敏,尤其要处理账号、令牌、路径、订单号和文件内容。原始响应可以保存加密副本或哈希关联,但不能为了排障把生产凭据写入普通应用日志。
当问题只在生产环境出现时,可以将最小复现包放入隔离环境,使用固定的模型版本、SDK 版本和验证器版本重新执行。若团队需要临时的远程 Mac 排障环境,可先通过 nuvcloud 的帮助页面 查看环境接入与使用说明,再把复现步骤、日志字段和工具依赖整理后执行;控制台相关操作则可从 nuvcloud 控制中心 进入。
如果当前方案是在开发者本地机器上直接复现,常见缺点是环境版本不固定、生产依赖难以隔离、多人无法共享同一份故障现场;如果改用普通云主机,又可能遇到 macOS 专属工具链、桌面调试能力和本地接口访问不完整的问题。对于需要短期验证 Agent 调用链、复现 SDK 行为或隔离一次生产事故的团队,租赁 nuvcloud 的 Mac 环境通常比临时改造现有机器更省步骤;但长期持续重负载、必须拥有物理接口或需要永久保存本地数据的项目,仍应评估自购设备或固定基础设施。
下一次遇到 AI Agent JSON 错误时,先不要增加重试次数:从 trace_id、Schema、响应状态、调用 ID 和执行结果开始,整理出一条可复现的数据流,再针对具体环节修复。
让 AI Agent 在稳定环境中快速完成排障
通过 nuvcloud 租用远程 Mac,在真实运行环境中复现参数错误、JSON 解析失败和工具执行异常。
按需选择合适的 Mac 配置与使用时长,减少本地设备投入,让调试工作更快启动。