跳转到主要内容

级联问题:为什么 Agent 副作用在大规模运行时会呈爆炸式增长

阅读需 1 分钟Tian PanTian Pan

一个团队交付了一个文档处理智能体(agent)。它在开发环境中表现完美:读取文件、提取数据、将结果写入数据库,并发送确认 webhook。他们运行了 50 个测试用例,全部通过。

部署两周后,在 100 个并发智能体实例运行时,数据库中出现了 40,000 条重复记录,三个下游服务收到了数千个虚假的 webhook,一个共享配置文件被两个同时运行的智能体各覆盖了一半。

智能体本身没有出错。系统崩溃是因为没有任何一个独立的智能体测试曾被要求与其他智能体共同处于同一个运行环境中。

这就是级联问题。它既不是模型故障,也不是提示词故障。这是一种系统性故障,单元测试从结构上就无法捕捉到它,因为单元测试在设计上就是隔离执行的。导致生产事故的行为——竞态条件、重试放大、共享状态损坏——只有在多个智能体实例同时与同一个现实世界的资源交互时才会出现。

理解级联问题需要像分布式系统工程师思考服务那样去思考智能体:不要把它们看作正确的程序,而要看作是一个共享、竞争环境中的参与者。

隔离如何掩盖了问题

单元测试给了你清晰的答案,但你问的问题在生产环境中并不存在。测试中的问题是:“给定此输入,该智能体是否能产生正确的输出?”而生产环境中的问题是:“当 100 个该智能体的副本同时针对同一个数据库、文件系统和外部 API 运行时,会发生什么?”

这是两个不同的问题。它们之间的鸿沟就是级联问题产生的地方。

考虑一个处理任务队列的简单智能体:读取下一个未处理的项,处理它,并将其标记为已完成。在隔离状态下,这是正确的。但在有 10 个并发智能体的情况下,它们都会在其中任何一个标记完成之前读取同一个“下一个未处理项”,导致同一个任务被处理了 10 次。这并不是模型错误——智能体完全按照指令行事。这是一个经典的“检查时间到使用时间”(time-of-check to time-of-use, TOCTOU)竞态条件,与分布式数据库工程师几十年来一直在处理的问题一模一样。

同样的结构出现在智能体运行的各个地方:

  • 文件写入:两个更新同一个配置文件的智能体将相互覆盖彼此的更改。最后写入者获胜。两个智能体都成功完成,但结果是损坏的。
  • 重试放大:工具层的一个失败会触发工具内部的重试、智能体 SDK 中的重试以及智能体自身重试循环中的重试。一个简单的网络超时会演变成 27 次 API 调用。
  • 状态累积:如果不使用原子操作,一个向共享日志文件追加内容或更新共享计数器的智能体在并发情况下会产生错误的结果,即使单次追加操作本身是正确的。

ZenML 对 1,200 多个生产部署的分析发现,生产失败最常见的来源并不是模型质量,而是这类基础设施和集成失败。模型表现正确,但系统没有。

三种故障模式详解

重试放大

大多数智能体架构在多个独立层级都设有重试逻辑:HTTP 客户端重试网络错误,工具包装器重试失败的工具调用,而智能体循环则重试失败的步骤。在正常情况下,这些层级是不可见的。但在发生故障时,它们会产生复合效应。

计算方法很简单:如果三个层级中的每一层在失败时都重试三次,那么一个上游错误就会产生 27 次下游调用。如果这 27 次调用是向支付 API 写入数据或向外部服务发送消息,其后果是显而易见的。

修复方法需要跨层级协调重试语义。带有抖动的指数退避(exponential backoff with jitter)可以防止同步的重试风暴。幂等键(idempotency keys)可以防止即使重试成功也导致的重复执行。熔断器(circuit breakers)则能在重试放大升级之前将其制止——在连续 N 次失败后,完全停止尝试该操作,而不是持续冲击一个已经降级的依赖项。

一个金融应用团队报告了一个熔断器配置:它在连续三次失败时跳闸,进入 30 秒的开启状态,然后在恢复之前在半开状态下测试恢复情况。核心见解是:阈值的设置必须足够激进以防止级联反应,但又不能过于激进导致瞬时故障触发不必要的开启状态。

并发变动

当多个智能体实例读取共享状态、对其进行修改并写回时,除非读取和写入是原子的,否则结果将取决于时间顺序。这不是一个新颖的问题——它是促使数据库事务、分布式锁和比较并交换(compare-and-swap)操作出现的相同问题。智能体也无法幸免。

具体的故障模式取决于资源类型:

文件:两个智能体读取一个 JSON 配置文件,添加一个条目并写回,将静默地丢失其中一个智能体的条目。第二次写入会在没有报错的情况下覆盖第一次写入。

数据库:除非数据库在约束层面(而不仅仅是在应用层面)强制执行唯一性,否则执行“先检查后行动”(如果记录不存在,则插入)的智能体在并发情况下会创建重复记录。

外部 API:如果在检查之后、修改之前有另一个智能体修改了资源状态,那么在修改前检查资源状态的智能体看到的将是过时的状态。乐观锁(optimistic locking)模式可以处理这种情况:在你的写入请求中包含你读取的版本号,并让服务器拒绝与新版本冲突的写入。

通用原则是:不要将智能体设计成基于它们并不排他拥有的状态来做决定。要么获取排他所有权(锁),要么使用在写入中包含前提条件的原子操作(比较并交换),或者将操作设计为无论并发修改如何都是安全的(幂等性)。

跨会话的状态损坏

智能体会话之间共享的可变状态会导致一类更隐蔽的问题。如果一个智能体将中间结果写入共享缓存、追加到共享上下文或更新共享内存库,就会在原本并非设计为相互作用的会话之间创建隐式依赖。

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 11 分钟

Agent 的写操作侧:在行动层设计可逆性

AI Agent 的每一次写操作都是一个潜在的事故。如何在 Agent 删除无法找回的数据之前,为行动层设计可逆性。

insider
ai-engineering
阅读需 11 分钟

智能体工具调用中的幂等性问题

为什么智能体重试逻辑会导致重复扣款、重复发送邮件和状态不一致——以及如何通过Saga模式、幂等键和结构化错误信号从架构层面解决这一问题。

insider
ai-engineering
阅读需 11 分钟

取消安全的智能体:你的“停止”按钮背后已经产生的副作用

“停止”只是一种 UI 手段,而非系统保证。这是一份针对取消安全智能体的从业者指南:持久化副作用账本、作用域授权、补偿操作,以及取消 UI 究竟应该显示什么。

insider
agents
阅读需 11 分钟

复合精度问题:为什么你的 95% 精确率 Agent 会失败 40% 的时间

一个每步精确率为 95% 的 10 步 Agent 流水线,整体成功率只有 60%。这里是背后的数学原理,以及真正能改变失败曲线的架构模式。

ai-engineering
agents
阅读需 11 分钟

429 错误背后的惊群效应:速率限制是一个分布式系统问题

不带抖动的指数退避会组织你的重试集群而不是将其消解 —— 随之而来的是同步的 429 浪潮、重试放大和亚稳态故障。本文将探讨为什么 LLM 速率限制需要抖动、重试预算,以及你原本没打算构建的客户端调度器。

insider
ai-engineering