跳到主要内容

智能体熔断机制:为什么步骤预算是保险丝,而非断路器

· 阅读需 13 分钟
Tian Pan
Software Engineer

每个将智能体(agent)投入生产环境的团队,最终都会遇到类似的事故。智能体进入了一个无法退出的状态。它在六个小时内反复调用同一个工具,只是参数在表面上略有不同。它在两个前提条件互斥的计划之间摇摆不定。它每隔两百毫秒重试一次瞬时的 429 错误,一直持续到天亮。它生成了一个包含百万 token 的计划,却从未执行。等到有人察觉时,token 账单已达四位数,下游 API 被限流,客户会话已超时十二次,而值班工程师正被针对同一根因的三个不同告警狂轰滥炸。

每个团队首先想到的解决方法都是步骤计数预算(step-count budget)。将智能体限制在 20 次迭代。限制在 50 次。定个数字,然后上线。步骤预算确实让事故报告消失了,但它并没有消除底层问题 —— 一旦你理解了其中的机制,你就会发现步骤预算相当于智能体世界里的家用保险丝:它是在损害造成之后才熔断的,保险丝盒本身现在成了维护负担,而下次发生故障时,你的本能反应是换一个更大规格的保险丝,而不是去追究到底哪里短路了。

智能体需要的是断路器(circuit breakers),而不是保险丝。这个区别比 AI 行业还要古老:在分布式系统文献中,断路器模式 封装了一个受保护的调用,监控失败信号,并在下游崩溃向上游蔓延之前主动触发熔断。“半开”(Half-open)状态允许系统探测底层问题是否已解决。阈值调优是一等公民的关注点。而在步骤计数上限中,这些都不存在。步骤预算只回答了一个问题 —— “智能体是否做得太多了?” —— 而且它是在最糟糕的时机回答的,即在智能体已经做了所有这些事情之后。

步骤预算到底能带给你什么

步骤预算是一个有用的原语,但它是底线,而非上限。它保证了单次智能体调用不会无限制地运行。这确实很有价值:如果没有它,陷入困境的智能体将一直运行,直到有其他机制阻止它,在最坏的情况下,这可能是凌晨 3 点的供应商配额限制,或者是某个注意到仪表盘的工程师。所以,保留步骤预算。错误在于将其视为最终答案。

步骤预算之所以不足,是因为每一步的成本并不是恒定的。一个调用具有 200K token 上下文窗口的推理模型的步骤,比一个在短提示词上调用小模型的步骤要贵数百倍。对于单次检索智能体和深度研究智能体来说,50 步的预算意味着完全不同的成本上限。当智能体在完成一项艰巨任务之前达到预算时,第一直觉往往是提高预算。如果团队为了处理单个失败任务而将上限翻倍,那么他们也就将下一次失控的爆炸半径翻了一倍。保险丝越换越大,而布线却原封不动。

更深层的问题是,“步骤过多”是一个滞后指标。当你做了 50 件没有进展的事情时,金钱成本已经沉没,频率限制已经触发,对话历史已经变得庞大,下游系统已经记录了 50 个现在需要对账的事件。断路器应该在损害扩大之前触发,而不是在损害造成之后。

语义循环检测:在账单产生前发现重复

对步骤预算最有效的补充是语义循环检测(semantic loop detection)。对每个工具调用及其参数进行哈希处理,并在滑动窗口中查找重复项。如果智能体在过去 7 步中调用了 5 次 read_file("/etc/config"),那么一定是出了问题。无论步骤预算是 50 还是 500,智能体都没有取得进展。

实现过程简单且成本极低。维护一个包含最后 N 个(工具名称,规范化参数)元组的环形缓冲区。在每一步中,计算当前调用的哈希值,并检查它在缓冲区中出现的频率是否超过某个阈值。一旦超过,立即触发断路器。参数规范化至关重要:智能体经常会在表面上改变参数 —— 增加一个空格,将 "true" 改为 true 后重试,或者交换 JSON 对象中键的顺序 —— 简单的字节相等检查会漏掉这些情况。规范形式的标准化(排序键、去除空白符、类型强制转换的基元)可以封堵大部分规避手段。

有两个改进值得一做。首先,让检测结果感知(result-aware):如果同样的调用每次产生不同的可观察结果,那确实是新的信息,智能体可能正在取得进展。你想捕捉的模式是“相同调用且相同结果”的重复,而不仅仅是“相同调用”。其次,区分两种失败模式 —— 智能体反复调用同一个工具(工具卡死循环)和智能体在两个互相抵消工作的竞争工具之间摇摆(振荡循环)。前者表现为环形缓冲区中的简单重复;后者表现为长度为 2 或 3 的短周期循环。两者都可以通过相同的原语检测到。

当断路器因语义循环触发时,默认操作不应该是终止。默认操作应该是注入一条纠错系统消息 —— “你已经使用相同的参数和相同的结果调用了此工具五次;请尝试其他方法或报告失败” —— 并给智能体多一步机会进行调整。如果智能体在警告后仍坚持循环,再行终止。这种二级升级机制显著减少了误报;智能体暂时卡住但可恢复是常见情况,而在第一次检测到时就直接硬性终止通常比“提醒”一下效果更差。

进度信号:强制规划器声明胜出条件

循环检测能抓住那些愚蠢的案例。更隐蔽的失败模式是智能体并不完全是在循环,而是在“徘徊”。每一步都是新的,每个工具调用的参数也不同,但智能体并没有收敛到结果上。它在探索搜索空间的各个分支,而目标节点自第一步起就在不断后退。这就是那种消耗了三千步来生成“计划”而不是答案的智能体。

解决方法是进度信号。要求规划器每隔 N 步就明确说明它取得了哪些具体进展,以及剩下的工作是什么。如果它无法说明,断路器就会开启。信号可以很简单,比如要求智能体填写一个包含 claims_so_farnext_subgoalestimated_remaining_steps 等字段的 JSON 对象。格式并不重要,重要的是强制功能:智能体必须承诺一个可衡量的状态,而一个独立的进程可以检查该状态是否在推进。

一个鲁棒的进度信号不仅仅是“智能体回答问题了吗?”——那是运行结束时的二元判断,对于运行中的干预毫无用处。它应该是一个在某个方向上单调递减的状态向量。预计剩余步骤应该呈下降趋势。未解决的子目标集合应该缩小。智能体认为仍需调用的不同工具的数量应该减少。如果一个规划器连续三个检查点都报告相同的 next_subgoal,那么即使它的工具调用表面上看起来各不相同,它也已经陷入了困境。

Token 速率上限:按分钟而非按步骤为循环定价

步骤预算计算的是步骤数。成本仪表板计算的是每次请求的美元数。两者都无法捕捉到那些每分钟默默消耗一百美元的智能体。你想要的是速率指标——一个导数,而不是一个数值。每分钟的 Token 消耗、每分钟的美元消耗、每分钟的工具调用次数。当速率超过你为智能体正常工作负载校准的区间时,进行限流。

速率上限在本质上与预算不同。预算说的是“这次运行最多花费 X 美元”——你最后才会知道。而速率上限说的是“这次运行每分钟不能超过 X 美元”——你在一分钟内就能发现。对于一个正常请求只需花费 10 美分的智能体,突然以每分钟 5 美元的速度运行就是异常的,无论它最终是否能在单次运行预算内完成。断路器应该根据速率触发,而不是累加值。

调优取决于工作负载。对于面向客户的聊天智能体,正常速率受到用户打字速度和响应节奏的限制;每分钟激增到 20 次调用几乎总是意味着陷入了循环。对于无人值守运行的批处理智能体,稳态通常很高,激增通常是高于基准的百分比,而不是绝对数值。两者使用相同的原语,但阈值是特定于工作负载的,在设置之前需要一段实际的测量期。

工具调用分布警报:当组合方式成为线索

另一个值得监测的信号是单次运行中工具调用的分布,并将其与所有运行的基准分布进行对比。如果典型的智能体运行会调用 read_file 三次和 web_search 一次,而当前的运行已经调用了 read_file 两百次且没有调用 web_search,那么这种组合方式就是警报。总步数可能仍在预算内。语义循环检测器可能尚未触发,因为文件路径各不相同。但智能体正在做的事情在性质上与健康智能体不同,这仅凭直方图就能检测出来。

这种分布警报是对先验概率的卡方检验。你不需要任何复杂的东西——将基于工具名称的滑动窗口计数与发布的基准进行比较就足够了。警报应该是一个软信号,而不是硬性终止:智能体可能确实处于一条需要三百次文件读取的路径上。但它应该是值班工程师在仪表板中看到的标志,以免运行突破硬性限制。

中断并移交:优于终止的失败模式

通过杀死智能体并返回错误来开启断路器是最简单的实现方式,但往往是错误的。智能体拥有昂贵的上下文——部分推理、中间产物、探索了一半的计划——仅因为触发了一个启发式规则就丢弃所有内容是浪费的,且通常对用户不友好。客户想要的是答案;而他们得到的却是堆栈跟踪。

触发断路器后更好的默认操作是移交。开启电路,冻结智能体状态,并将部分上下文路由给人工审核员(或者在某些架构中,路由给另一个智能体——一个能力更强的模型或专门的工具)。移交保留了工作成果,为客户提供了一个承认困难而非拒绝服务的答案,并为评估集生成了一个带标签的示例。团队能从每一次断路器触发中学习;如果采用触发即终止的策略,团队只能看到计数器在增加。

移交模式还改变了断路器的政治经济学。如果团队知道触发断路器意味着人工审核员会被传唤,他们就不会愿意将阈值设得太低——每一个误报都会浪费审核员的一分钟。如果团队知道触发断路器意味着愤怒的用户会收到通用的错误消息,他们同样不愿设低阈值,但原因却是错误的——他们在保护指标,而不是保护用户。移交路径让断路器变得诚实,因为触发的成本在团队内部是可见的,而不是转嫁给用户。

架构启示

任何生产环境的 Agent 技术栈都必须落地一种分层熔断器模式:作为绝对安全保障的步骤预算;作为最常用防御手段的、带有两级升级机制的语义循环检测器;强制规划者声明成功条件的进度信号;捕捉异常成本消耗的 Token 速率上限;作为金丝雀指标的工具调用分布告警;以及在上述任何机制触发时,能够保留工作进度的“停止并移交”路径。

成本控制框架是大多数团队最容易忽视的一点。Agent 的 Bug 风险面是步骤数与单步成本的乘积。一个运行时间比预期长 10 倍,且单步成本比平时高 10 倍的 Bug,是一个 100 倍级别的事故,而不仅仅是 10 倍。熔断器不仅是安全原语,更是成本原语。事实上,它们恰好能阻止客户信任的失控损害,仅凭财务控制这一点,引入熔断器就完全值得。

分布式系统在 2010 年代已经吸取了这个教训,当时足够多的级联故障事件让这一话题变得习以为常。Agent 现在正处于类似的时刻 —— 故障模式清晰可见,模式已被充分理解,剩下的唯一问题是,你的团队是在生产事故发生之前还是之后安装熔断器。步骤预算就像保险丝。它烧断一次,告诉你线路过载,然后你换个更大的。而熔断器是一个控制平面。它告诉你哪个保护机制被触发了、原因是什么,以及需要更改什么。前者是你为了预防事故而交付的;后者则是你为了确保不再发生下一次事故而交付的。

References:Let's stay in touch and Follow me for more thoughts and updates