智能体从未接收到的服务降级信号
当下游 API 开始出现波动时,人类操作员在任何事情真正崩溃之前,就会通过十几种方式察觉到。状态页变为黄色。变更日志邮件飞进收件箱。提供商的仪表板上出现警告横幅。值班频道因有人在日志中发现 429 错误而热闹起来。队友发帖询问:“还有人看到写入变慢吗?”这些都不是对请求的响应。它们是围绕 API 的环境运行信号,人类几乎是被动地吸收了这些信号。
调用同一个 API 的智能体(agent)只收到一样东西:它刚刚发出的请求的响应。状态码、Header、Body。这就是全部的渠道。它没有收件箱,没有仪表板,没有 Slack,没有外围视野。它察觉不到最后十个调用每个耗时都是之前十个的两倍。它读不了状态页,因为没人给它 URL,它也没有查看状态页的常规指令。当依赖项降级时,智能体是系统中最后一个知情方——而且它通常是通过失败才知晓的。
这种不对称性并非模型能力问题。更聪明的模型也解决不了。智能体对运行信号是盲目的,因为底层设施(plumbing)从未传递过这些信号,而且大多数智能体架构在出厂时,甚至没人注意到缺失了这些底层设施。
智能体实际看到了什么
想象一下观察依赖项的最窄窗口:单个 HTTP 响应。这就是工具调用返回给智能体的内容。如果调用成功,智能体看到 200 和一个 Body。如果失败,它看到 500 或超时。在这两者之间——即“在运行,但不太好”的整个光谱中——智能体几乎看不到任何它被训练去应对的信息。
人类操作员生活在那个中间地带。他们深知系统很少会干脆利落地失效;它们会先降级。延迟悄悄爬升。错误率从 0.1% 升至 2%。队列开始积压。提供商发布公告称“错误率上升,正在调查”。操作员读取这些微弱的信号并做出调整:减速、改变批处理方式、切换到备用方案,或者干脆等待。这种调整早在硬性故障发生之前就开始了。
智能体没有等效的机制。它的循环是请求、响应、决策、重复。除非你刻意为之,否则每一次迭代对于运行健康状况来说都是无状态的。第 200 次调用的响应并不携带第 190 到 199 次调用正在变慢的记忆。智能体将 1.8 秒的响应和 180 毫秒的响应视为同样的结果:成功。因此它会继续做之前做过的事,只不过现在它是在向一个正通过智能体听不见的方式、安静地请求“别打扰我”的系统发起请求。
智能体直接冲进的“棕色故障”
最危险的故障模式不是 API 宕机,而是 API 在维持运行的同时变慢。工程师称之为“棕色故障”(brownout):服务仍然返回 200,但每个请求都要消耗更多的时间、更多的连接和更多的容量。棕色故障是系统在技术上仍在提供服务的同时,恳求减少负载。
人类能立即发现棕色故障,因为人类会看时钟。除非你为它构建一个时钟,否则智能体不会看时钟。因此,当依赖项发生棕色故障时,智能体仍会全速调用。更糟糕的是,如果棕色故障导致少数请求超时,智能体的重试逻辑就会介入——而重试就是加速剂。
在任何多层系统中,这种算术都是残酷的。如果一个由五个服务组成的调用链中的每一层都重试三次,一个原始用户请求就可能扩散为 3^5 = 243 个后端调用。单个 429 触发重试,这些重试消耗了本就稀缺的配额,而这种消耗又触发更多的 429,循环往复形成重试风暴(retry storm)。智能体只看到单个响应,根本不知道自己就是产生风暴的人。它将该事件体验为“API 不太稳定”,并以更多的请求——恰恰是导致不稳定的行为——作为回应。
大家都会采用的修复方案是带抖动的指数退避(exponential backoff with jitter),这确实是必要的。加倍两次尝试之间的等待时间,加入随机性以防大量智能体同步形成“惊群效应”(thundering herd),并将尝试次数限制在五到七次。但退避只有在失败后才会触发。它是对 429 的反应,而不是避免它的方法。一个正确退避的智能体仍然是先撞了墙。为了不撞墙,智能体需要预见墙的到来——而这些信息是存在的。它们只是被丢弃了。
