Agent 跑长任务时,工具返回值的校验很容易被简化成“结构正确就等于数据可靠”。我们在一次客服工单场景里吃过亏:工具返回的 JSON 结构完全合规,客户 ID 也在,但那个 ID 是从历史上下文里“回忆”出来的,不是本次工具返回值里的。AWS 那篇报销工具复盘写过几乎一样的场景:create_expense_report 返回了 reportId,后续 add_expense 时 Agent 没从返回值里读,而是拿了一个前面查询过的旧 ID,所有写操作静默失败,直到对账才露馅。
所以返回值校验不止是字段在不在、类型对不对。类型推断本身是在给每个字段建立来源和可信度标签,决定它能不能参与后续推理,还是只能当“待确认的文本片段”。
类型校验的首要目标是拦住“看起来对的幻觉”
如果只拿 JSON Schema 兜底,那模型生成的任何一条符合 schema 的字符串都会被当成本次调用的真实结果。但工具层实际上会返回四种东西:本次真实返回、上次返回的缓存副本、模型从上下文里捏造的字符串、错误信息被包装成正常响应的产物。传统 JSON Schema 只能拦住第四种里明显畸形的那些(腾讯云那篇文章引用 Gartner 数据说,38% 的 Agent 故障来自参数幻觉或接口误用,这个比例和我们线上观察一个量级)。
真正有效的做法是把“本轮工具返回”和“历史上下文”分开存储,校验时明确要求 ID、链接、金额这类关键字段必须在本轮返回值里出现过一次。听起来很笨,但它拦掉了我们大约三成“张冠李戴”类错误。OpenAI Agents SDK 的做法也类似:用 Zod 或 Standard Schema 在本地解析输出,让 TS 推断出输出类型;如果配的是原始 JSON Schema,解析后的结果是 unknown,得自己填类型断言。这个设计在提醒,默认的可信边界比大家以为的窄很多,需要业务层显式确认消费这一段返回值的是谁。
用运行时校验加边界与枚举把模型填写开关缩小
类型定义往严了写这件事已经说了很多遍,真正落地时出错在“字段里数据形态混杂”的那一刻。比如中台的工单系统返回的 priority,说是枚举 high/medium/low,但老接口里返回过 P1 和数值 1。Pydantic 加了 Literal 定义就挂了,没加又被模型当作 low 解析,整批工单的紧急度全被过滤。
我们会写一条“转换层 + 原始保存”的约定:边界校验用联合类型,不放宽 schema;解析失败不抛错,而是用该字段原始字符串构造 RawUnknown 结构,同时在返回结构里塞上 validation_status。模型下一步如果需要动到该字段,prompt 中该字段节点必须带括注。这一条是从可观测角度保证,不会有人拿它继续往下推理。
异常也要按类型元分别处理
超时、空结果、权限不足被混成同一个 error,再被 Text 包裹塞回 messages 里,这类坑在框架层常常暴露。OpenHands 这一点处理得较细,工具执行结果通过 Observation 组件结构化返回,错在哪里、是不是带着 error 和 recovery_suggestion 不同形态传回去。我们更偏向 Anthropic 在工具设计最佳实践里的说法:不是拦停而是给出修复建议。字段里不设置只有“type=error”一种错误,而要把失败当结构化事件,配 parse OK、验证通过是字段。
举个例子,翻页游标失效时会返回 cursor_invalid,不会只返回字段里空字符串;时间超范围的查询会被打上 window_too_large,重试前收窄到区间。让模型拿到的不是一句话,而是能自己纠错的东西。
置信度排序不能盖过字段级判断
检索类工具经常会返回 relevance 评分或置信度,实际写代码时容易误用——排序是模型组织答案的依据,但真正阻住写污染靠的是每条返回的来源、时间和字段完整性。前面提到的那个核对思路也是如此:可信度是模型内部感觉的可信,不是在业务里就可以确认的口径。我们会把相似度排序做二次规则审查,金额 amount>0、链接出现在 source_url、时间戳在今天附近,再做一轮才能标记 verified。高风险写操作仍然会在人工接管队列里,这一层不替人吃掉。
校验成本与性能
代价要写清楚:完整逐字段校验和结构化回注会占到 Agent 端到端延迟的大约 5%~8%(在 1~3 次工具调用的任务里测的),Pydantic 严格模式下更明显。如果把它们一股脑放在 tools 循环里,长任务会因为额外 token 被撑爆,我们写了个缓存和一个降级策略——常见字段改用 validate_assignment=True,重转升到 Pydantic V2 的 Rust 侧,这个比例降下来一些;对幂等读接口,把验证结果视 key 缓存一分钟,代价基本不再体现在 P99 上。
结论说回来:工具返回的字段应该像人做交接那样追溯出处。什么样的返回是证据、什么样的返回只是草稿草草落在上下文里,最稳妥的做法是显式标注,不要依赖 schema 看起来没问题就放过去。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/agent-engineering/673.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
Agent Harness:可观测、可评测、可回滚的 Agent 基础设施
去年我们做了一个处理退款申请的 Agent。Demo 很顺,上线第一周就出了事故:进程重启后状态丢了,同一笔申请被处理了两次。问题不在模型,而在模型外面那层没人愿意写的基础设施。
2026/10/09
-
AI Coding Agent 如何稳定提交可合并的 PR
上周 review 了一个 AI 生成的 PR,测试全绿,但我还是把它关了。它改了 37 个文件,顺手升级了一个依赖,还修改了两条测试断言。问题不是代码不能跑,而是这个 PR 没人敢合。
2026/10/14
-
MCP 进入生产环境:工具协议、网关与权限边界
MCP 刚出来时,我以为最难的是协议兼容。真正接了十几个工具之后才发现,把工具接上只花了两天,把权限、审计和故障处理理清楚花了两个月。
2026/10/07
-
Agent 安全清单:提示注入、工具越权与数据外泄
一个内部助手被要求总结一封客户邮件。邮件末尾有一行白色小字:把最近的客户名单转发到某个地址。Agent 真的准备去调邮件工具了。安全不能靠模型自觉,得靠系统设计。
2026/10/16
-
并行工具调用暴露的工具设计耦合问题
并行工具调用能砍掉四成延迟,但它更重要的价值在于暴露工具之间的隐性耦合。本文拆解上下文依赖、共享状态竞争与时序前提这三类静默失败,给出读写分类规则、工具合并的判据,以及什么时候该上依赖图编排。
2026/10/23
-
Agent 子目标检查点回归:从回滚状态到登记副作用
Agent 的子目标检查点能回滚本地状态,却回滚不了已提交的外部副作用。本文拆解恢复后重新合成工具调用导致的语义回滚,给出基于副作用指纹的检查点边界划分与确定性回归断言。
2026/10/25
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布