Kvmzen 博客
← 返回技术实践

AI Agent 为什么离不开 JSON?从 Tool Calling、Function Calling 到 MCP 的完整数据流解析

AIAgent ·约 15 分钟阅读

AI Agent 为什么离不开 JSON?从 Tool Calling、Function Calling 到 MCP 的完整数据流解析

工具参数看起来是合法 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 要求 pathcommand,模型只生成了 path
  • 类型错误:接口要求数字、布尔值或数组,实际传入了字符串。
  • 额外字段:执行器拒绝未知字段,模型却添加了未声明的参数。
  • 枚举错误:字段结构正确,但值不在允许范围内。
  • 平台子集差异:不同模型平台支持的 JSON Schema 关键字和严格模式并不完全相同。

JSON Schema 的作用是描述和验证数据结构,不是把任意业务规则都写进一个声明文件。官方规范目前以 2020-12 为主版本;同时,Schema 本身也不能表达所有跨字段的业务语义,因此通常还需要第二层程序验证。JSON Schema 规范说明

例如,下面的 Schema 可以限制类型和必填字段:

{
  "type": "object",
  "properties": {
    "path": {
      "type": "string"
    },
    "timeout": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": ["path"]
}

但它无法独立判断这个路径是否属于当前租户,也无法知道用户是否有权执行该路径下的命令。你需要把校验拆成:

  1. JSON 语法解析。
  2. JSON Schema 结构验证。
  3. 工具名称与任务上下文验证。
  4. 权限、资源和业务规则验证。

不要为了修复工具选错问题,继续无限收紧字段类型。Schema 能解决“参数长什么样”,不能完全解决“这个工具是不是该被调用”。

第二关:参数正确,工具仍可能选错

Schema 合规不代表模型理解了工具用途。

假设你同时暴露了:

  • read_mac_file
  • write_mac_file
  • delete_mac_file

如果三个工具的描述都写成“处理 Mac 文件”,模型即使生成完全合规的参数,也可能调用了危险或不相关的工具。

改进工具选择,应优先调整以下内容:

工具命名:使用明确动作和对象,例如 read_project_config,不要使用 file_action
工具描述:说明适用场景、不适用场景、是否产生副作用。
候选范围:根据任务阶段减少暴露给模型的工具数量。
上下文约束:在用户请求、系统状态和工具说明中保持对象名称一致。
权限前置:高风险工具先进入审批状态,不让模型直接获得执行权。

❌ 不要只增加 required 字段来修复工具选择。
❌ 不要把多个动作塞进一个工具,再期待模型通过自然语言参数自行分流。
❌ 不要把认证失败包装成“参数不正确”,这会误导模型进行无效重试。

Tool Calling 和 Function Calling 在不同平台的消息字段可能不同,但排障原则一致:模型负责提出调用意图,应用负责决定是否执行。不要把某个平台的 tool_callfunction_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 工具通常包含 namedescriptioninputSchema,也可以提供 outputSchema;工具调用使用 tools/call,结果则可能包含 contentstructuredContentisErrorMCP 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 结果同时检查 contentstructuredContentisError
  • [ ] 存在 outputSchema 时,客户端会再次验证结果。
  • [ ] 不支持结构化字段的客户端仍能读取兼容文本。
  • [ ] 长任务可以从事件日志恢复,而不是依赖进程内存。
  • [ ] 依赖 macOS 工具的测试记录了系统权限和环境状态。
  • [ ] 失败调用不会因为模糊错误文本触发无限重试。

如果你正在做 MCP 工具结果验证,优先验收输出 Schema 和客户端兼容性;如果正在排查远程 macOS 上的 Agent 执行,则要把环境状态与 trace_id 绑定,而不是只复制模型返回内容。

对于只在本地开发机上运行的 Agent,问题通常被本机缓存、权限和已安装依赖掩盖。与 Windows 或 Linux 云主机相比,直接在 macOS 环境中复现 Xcode、钥匙串、签名和系统工具调用,往往更接近真实故障,但也会带来远程连接、权限隔离和环境固定成本。

因此,当前方案如果依赖个人 Mac,常见缺点是环境不可复现、多人共享困难、日志与系统状态分散;如果依赖通用云主机,又可能缺少 macOS 专属工具链,导致“API 调用成功、真实执行失败”。需要持续重现跨层故障时,使用 Kvmzen 的远程 Mac 环境,可以把固定依赖、隔离日志和远程复现放在同一套测试流程里;但如果你的任务是长期满负载运行,或必须接入特定物理设备,自购 Mac 仍可能更合适。你可以先查看 Mac 云租用方案,再按任务周期和设备依赖决定是否租赁。

限时特惠

不只是一台 Mac,是你在云端的开发基地

独享算力 · 全球节点 · 按月订阅 · 无需购置硬件

返回首页
限时优惠 点击查看套餐