第4章 工具返回格式异常:现象、schema校验、JSON解析错误、兼容性修复

现象识别

Agent调用外部工具后,工具返回的响应体不符合约定的格式规范。典型表现包括:

  • Agent陷入循环重试:反复调用工具但每次返回均被解析失败。
  • JSON解析异常日志:看板上出现json.decoder.JSONDecodeError或SyntaxError: Unexpected token。
  • 字段缺失/类型错误:schema校验报出required field missing或type mismatch。
  • 上游接口版本更替:工具返回了新的JSON结构,Agent代码未同步适配。
快速判断口诀:
返回若能读,schema不匹配 → 字段级异常;
返回读不了,json抛异常 → 语法级异常;
返回读得通,逻辑走不通 → 语义级异常。
工具返回格式异常故障树 返回格式异常 JSON 解析错误 Schema 校验失败 兼容性异常 非法字符 截断响应 字段缺失 类型不符 版本升级 编码变化 定位路径:Agent报错 → 日志截获 → 分类 → 选择修复策略 高优排查:JSON解析错误 > Schema校验 > 兼容性

数据采集

当触发格式异常时,优先收集以下三类数据,用于准确定位故障节点:

数据源 关键字段 采集方法
Agent 原始日志 raw_response、status_code、content_type 开启 Agent 的 debug 级别日志;记录 HTTP 响应的 text 原始内容
工具侧访问日志 request_id、response_body、response_headers 在工具网关或 API 管理平台中按 request_id 检索
Agent schema 定义 openapi.json 中该端点的 response 模型 从 Agent 配置仓库或注册中心获取最新 schema 文件
注意:切勿只收集 Agent 侧聚合后的结构化数据,必须保留 原始响应字符串。部分 JSON 解析错误在结构化打印时会被静默吞掉,只有原始字符串才能暴露非法字符或截断。

原因定位

按照“最易→最难”的排查顺序,使用以下判断规则表快速定位根因:

判定条件 根因分类 置信度
日志中 json.loads() 抛出 JSONDecodeError,且原始响应首字符不是 { 或 [ 非 JSON 响应(如 HTML 5xx 错误页) 高
响应有效 JSON,但 jsonschema.validate() 报 ValidationError,缺少必填字段 Schema 不匹配(字段级变更) 高
响应有效 JSON,字段名或类型与 schema 定义一致,但 Agent 仍无法正常处理后续逻辑 兼容性异常(如枚举值新增、null 处理缺失) 中
响应体包含 \ufeff BOM 头或控制字符 编码问题导致 JSON 解析失败 高
响应内容被截断一半,结尾不完整 超时/缓冲区限制导致的截断 中

推荐使用二分法做初步隔离:

  1. 将原始响应通过独立 parser 测试(如 Python json.loads)→ 区分“能不能解析”。
  2. 若可解析,再将 parsed object 与 schema 做单独校验 → 区分“字段/类型是否匹配”。
  3. 若 schema 校验通过但逻辑异常,则检查兼容性适配层。
实际案例:某天气工具返回的 temperature 字段从整数 23 变为字符串 "23"。Agent 侧 schema 类型定义为 number,导致校验失败。修复前需确认工具侧是否已向后兼容,或是要求工具回滚。

修复动作

根据定位得到的根因类别,执行对应的修复策略:

  • JSON 解析错误修复:
    • 若为非法字符(BOM、控制字符),在 Agent 接收处增加 strip BOM 和 clean control chars 预处理。
    • 若为截断响应,增大 max_response_size 或重试时要求工具分页返回。
    • 若为完全非 JSON,则改为解析 Content-Type 头,若为 text/plain 则尝试 json.loads 前先过滤异常字符。
  • Schema 校验失败修复:
    • 与工具团队协商对齐 API 版本,更新 Agent 侧的 schema 文件。
    • 短期内采用“宽容模式”:在 Agent 内使用 allow_extra 或 skip_missing_optional 策略。
    • 对必填字段缺失,配置默认值或触发降级提示。
  • 兼容性异常修复:
    • 增加适配层:将新字段映射到旧字段(如 temperature_celsius → temperature)。
    • 对新增枚举值做白名单更新,避免 Agent 因未知值而拒绝处理。
    • 若工具同时返回多个版本响应,在 Agent 内按版本号分流处理逻辑。
# 示例:兼容性适配层代码片段(Python)
def normalize_weather_response(raw: dict) -> dict:
    # 兼容工具返回字符串类型的 temperature
    if isinstance(raw.get("temperature"), str):
        raw["temperature"] = float(raw["temperature"])
    # 填充缺失的必填字段
    raw.setdefault("wind_speed", 0.0)
    return raw

复测

完成修复后,按以下三步验证修复有效性:

  1. 单次调用测试:使用该工具的历史异常响应(从数据采集阶段保存的原始日志中回放),确认不再报 JSON 解析或 schema 校验错误。
  2. 回归批量测试:运行 Agent 对该工具的 100 次随机参数调用,检查返回成功率和正确率。
  3. 端到端场景测试:构造包含格式异常和正常响应的混合用例,确保 Agent 不会因一次异常导致整个会话崩溃。
复测通过标准:JSON 解析成功率 ≥ 99.5%,schema 校验通过率 ≥ 99.5%,无未捕获异常导致 Agent 终止。

预防

为避免工具返回格式异常反复出现,建议从以下维度建立防御体系:

维度 措施 优先级
契约测试 在 CI/CD 中为每个工具接口编写响应 schema 契约测试(如 Pact、JSON Schema Test Suite) P0
Agent 护网 所有外部工具响应必须经过 ResponseValidator 中间件,包含预处理和宽容解析 P0
版本协商 Agent 调用时携带 Accept-Version 头,工具侧明确版本号;Agent 支持多版本响应适配 P1
监控告警 实时监控 json_parse_error_count 和 schema_validation_error_count,异常时自动降级 P1
灰度发布 工具侧 API 升级先在小流量 Agent 实例上灰度,观察格式兼容性后再全量发布 P2

最后,建议在 Agent 的代码仓库中维护一份“工具响应异常处理手册”,每发生一次新的格式异常,就将原始响应、根因、修复代码记录到该手册中,形成组织级的知识沉淀。