代理墙钟预算:一场与工具超时机制的赛跑
有一种 Agent 漏洞,当你孤立地观察任何单个组件时,它都不会出现。模型没问题,工具没问题,重试策略也没问题。纸面上的超时值甚至可以说很慷慨。然而,一个通常在 8 秒内完成的工具,却总是在一个已经在 7.9 秒时将其宣告为失败的 Agent 面前折戟。Agent 围绕一个从未发生过的“错误”重新规划,并启动了第二次调用,而第一次调用的结果即将与其发生碰撞。
漏洞不在任何一个框框里。它存在于两个没人同意应该同步的时钟之间的缝隙中。
有一种 Agent 漏洞,当你孤立地观察任何单个组件时,它都不会出现。模型没问题,工具没问题,重试策略也没问题。纸面上的超时值甚至可以说很慷慨。然而,一个通常在 8 秒内完成的工具,却总是在一个已经在 7.9 秒时将其宣告为失败的 Agent 面前折戟。Agent 围绕一个从未发生过的“错误”重新规划,并启动了第二次调用,而第一次调用的结果即将与其发生碰撞。
漏洞不在任何一个框框里。它存在于两个没人同意应该同步的时钟之间的缝隙中。
用户点击停止。浏览器关闭了 SSE 连接。你的 AI SDK 触发了 onAbort。Agent 运行时检测到信号,停止向模型请求更多 token,并终止其循环。从你的代码库内部来看,这次取消显得非常利索。你所能看到的每个子系统都在执行正确的操作。
与此同时,两秒钟前,模型发出了一个工具调用(tool call)。运行时分发了它。工具的 execute 函数打开了一个连接到第三方 API 的 TCP 连接并发送了 payload。该 HTTP 请求仍在传输中,第三方的服务器仍在处理它,而第三方完全无法得知它所服务的对话已不存在。写入操作成功提交。用户的心智模型认为他们通过点击停止避开了该操作。下游系统的数据库则记录了完全不同的结果。
周一,一位新工程师加入了团队。到周三时,她已经搭建好了本地 Agent 环境:一个桥接到公司部署 API 的 MCP 服务,指向 Staging 环境,并与她的编辑器相连。入职文档引导她完成了 OAuth 流程。她粘贴到服务器环境文件中的令牌是同事发给她的——这与 CI 流水线用于发布到 Staging 环境的是同一个令牌。到周五,她已在共享办公空间与团队一起进行协作开发。
MCP 服务仍在运行。绑定在 127.0.0.1。无需身份验证。令牌已加载到进程中。她没多想,因为她并没在使用它。但那天访问任何网站的任何标签页,都可以通过她自己的浏览器与她的本地服务通信。共享办公空间 Wi-Fi 上的任何其他笔记本电脑也同样可以,因为她没注意到该服务实际上绑定到了 0.0.0.0。你的 CI 流水线用来推送到 Staging 的 OAuth 令牌,现在任何能诱导浏览器向本地 IP 发起请求的人都能触及——在 2026 年,这只需要一个弹窗。
本文讨论的就是这类故障:“我在笔记本上开发”与“我的笔记本是对手可以触及的服务器”之间的鸿沟。MCP 服务在设计上恰好处于这个鸿沟之中。大多数团队尚未察觉。
客户支持团队升级了一个问题:“你的助手以前能正确处理退款资格问题。但上周开始出错了。”值班工程师调取了对话记录,在开发账号中使用相同的模型标识符回放了完全相同的提示词(prompt),得到了正确的回答,于是以“无法复现”为由关闭了工单。两周后,另一名客户提出了同样的投诉。工程师再次在同一个开发账号中进行回放,结果依然正确。团队开始归咎于没人做过的提示词更改。
请求中的模型标识符从未改变。响应字段中的字符串与请求字段中的字符串匹配。评估套件在六周内一直保持绿色。生产流量使用的模型权重与评估套件使用的模型权重是两套不同的集合,而且在该账号的整个生命周期中一直如此——直到过去这六周,它们变成了同一套权重,而团队注意到这一点仅仅是因为客户先发现了。
安全审查称智能体(Agent)“代表”(on behalf of)用户操作。OAuth 令牌却有不同的说法,而审计日志也支持令牌的说法。
语言上的一点微小差别,在架构层面起到了意想不到的作用。“代表”(On behalf of)是安全审查在试图描述一种委派安排时使用的语言,即智能体是一个可识别的委派对象,并受此身份约束。“作为”(Act as)则是运行时的行为,此时智能体持有的令牌与用户本人的令牌完全一致,因此在任何下游系统看来,智能体就是用户。这两个短语描述了完全不同的威胁模型。典型的企业级 OAuth 集成交付的是后者,但宣传的却是前者。