细说agent心跳机制--agentLoop循环机制中死循环处理,带你进一步了解agent工作底层原理

事情是这样的。

你跟 Agent 说:「把项目里所有的 console.log 替换成 logger.info」。

听起来像是个实习生十分钟就能干完的活。结果两小时后你回来一看,它还在那儿:

text

scss 复制代码
read_file("src/index.js")   → 修改 → write_file("src/index.js")
read_file("src/index.js")   → 修改 → write_file("src/index.js")
read_file("src/index.js")   → 修改 → write_file("src/index.js")
...

Token 账单已经烧到 8 美元,文件内容一个字没变。

这不是段子,这是 Agent 出厂设置。今天聊聊怎么给它装保险丝。

先看看 Agent 是怎么把自己玩死的

三种经典死法:

死循环:反复调用同一个工具,做同样的事。像极了找不到钥匙却在同一个口袋摸十遍的你。

Token 烧穿:无限续写。你让它改个日志,它给你写了一篇关于日志系统哲学意义的散文。

输出截断:模型说着说着被掐断了,但它自己不知道,下一轮接着从「总之综上所述」开始,前言不搭后语。

下面逐个拆。

保险丝一:工具死循环检测

OpenClaw 的思路很朴素:给每次工具调用打指纹

text

scss 复制代码
指纹 = SHA256(工具名 + 序列化后的参数)

光看参数还不够,因为 Agent 可能参数一样但结果在变(比如轮询等待)。所以还要记录结果指纹------同样的调用、同样的参数、同样的结果,才算「无进展」。

基于这个框架,四种检测器层层加码。

检测器 1:通用重复检测

同一个工具调用、同样的参数,触发 10 次阈值。

动作:警告,不阻断。

为什么不直接砍?因为有些场景确实需要重试。比如网络抖动、文件锁竞争。10 次是给「合理重试」留的余量,超过这个数基本可以断定它在原地打转。

就像你妈喊你起床,前三次是提醒,第十次就开始掀被子了------但还没动手。

检测器 2:无进展轮询检测

不光看参数,还要看结果。

如果状态长时间没变化,说明这个工具调用对这个任务是无效的。Agent 应该去干点别的,而不是在这儿刷 CD。

举个反例:

text

scss 复制代码
get_build_status() → "building"
get_build_status() → "building"
get_build_status() → "building"
...

参数一样,结果一样,纯纯的浪费。这时候该做的是 sleep 或者换个思路,而不是把同一个接口刷到冒烟。

检测器 3:Ping-Pong 检测

这个最阴间。两个工具交替调用,像是在打乒乓球:

text

erlang 复制代码
read_file  → write_file → read_file → write_file → ...

单独看每一次调用,参数可能都在变,结果也在变,前面两个检测器抓不住。

但把序列拉长一看------这俩在互相喂球,谁也没得分

检测逻辑:两个工具交替执行,且交替后的结果和上一轮相同,判定为死循环。

检测器 4:全局熔断

上面三种情况,本质上都是「无进展」。

只要累计 30 次无进展,强制停止,没有例外

这是最后一道闸。不管你在干嘛,不管你看起来多努力,只要 30 次没推动任务前进,就给我下来。

有点像电路里的空气开关。前面几层是保险丝,这个是总闸。

保险丝二:Token 预算控制

死循环是行为问题,Token 烧穿是资源问题。

解法:每次任务预算好 Token,不能超。

但不是简单粗暴地到点就砍,而是分阶段干预。

90% 阈值:注入提示

当消耗达到预算的 90%,往上下文里塞一条消息:

text

erlang 复制代码
已完成 Token 目标的 87%(26170/30000),继续工作------不要总结

注意最后四个字:不要总结

因为模型有个坏习惯,一看到快没额度了,就开始「综上所述,我已完成...」然后洋洋洒洒写一堆废话,把最后的预算全花在表演上。

这条提示就是在告诉它:别整虚的,干活。

递减回报检测

光看总量还不够,还得看增量效率

text

复制代码
续写第一次:+3000 Token  正常
续写第二次:+2000 Token  正常
续写第三次:+400 Token   增量很小
续写第四次:+200 Token   连续两次递减,停止

逻辑很直白:如果每次续写带来的新内容越来越少,说明模型在挤牙膏。再给它预算也是浪费,直接停。

这就像追剧。第一集信息量爆炸,第二集还行,第三集开始注水,第四集你已经在刷手机了。这时候正确的做法是弃剧,而不是继续充会员。

保险丝三:输出截断恢复

前两个保险丝管的是「别乱花钱」,这个管的是「钱花了但话没说完」。

每个模型都有 max_output_tokens 参数。Claude 默认是 8192 个 token。如果模型要说的话超过这个限制,就会被硬截断

问题是:模型自己不知道

它以为自己在正常输出,下一轮接着从「总之综上所述」开始,结果前言不搭后语。

Claude Code 的处理方式分三步:

第一步:8k 不够,提到 64k

简单粗暴。既然默认值太小,那就把天花板抬高。

但这只是缓解,不是根治。再大的窗口也有满的时候。

第二步:注入恢复消息

一旦检测到输出被截断,往上下文里塞一条:

text

复制代码
输出 Token 限制被触发,直接从断点继续------不要道歉,不要回顾你在做什么。
如果是说到一半被截断了,从那个思路接着说,把剩下的工作拆成更小的块。

这段话信息量很大:

  • 不要道歉:模型被截断后第一反应是「抱歉,我之前的输出被中断了」------纯浪费 token
  • 不要回顾:别重新总结已经说过的内容
  • 从断点继续:直接接上
  • 拆成更小的块:防止下一次又被截断

本质上是教模型「被截断后该怎么办」的 SOP。

第三步:认栽

3 次恢复都不行,那就把不完整的结果返回给用户,标记输出被截断

该认怂就认怂。硬撑只会烧更多 token,还拿不到完整结果。

三根保险丝,各管一段

回头看:

保险丝 管什么 核心手段
工具死循环检测 行为异常 指纹 + 4 层检测器 + 全局熔断
Token 预算控制 资源失控 预算 + 90% 提示 + 递减回报检测
输出截断恢复 输出异常 提高上限 + 恢复消息 + 认栽

三者不是孤立的。死循环会烧 token,烧 token 可能导致输出被截断,截断后的恢复消息又可能触发新一轮循环。

所以真正的工程做法是三层同时上,互相兜底。

Agent 这东西,能力越强,翻车的方式越花哨。保险丝不是为了限制它,是为了让它在正确的方向上跑得更久

毕竟,一个不会原地打转、不会烧穿预算、不会说到一半断片的 Agent,才是能真正交付任务的 Agent。


下次再看到 Agent 对着同一个文件读读写写,记得它不是在努力,它只是缺一根保险丝。

相关推荐
四点半-企业AI获客1 小时前
AI搜索引用机制拆解:从抓取、解析到引用打分的完整链路
人工智能·geo·rag·ai搜索·搜索引擎优化
麦豆GEO1 小时前
本地商家GEO优化落地实操方案:让AI主动推荐你的店
大数据·人工智能
还有你Y1 小时前
Orca 如何用 ADE + 多 Agent 编排重写 AI 编程工作流
人工智能·agent
Htr_1 小时前
Cortex 使用指南:开源 API 知识层
前端·数据库·人工智能·重构·计算机外设
tiger8651 小时前
深入理解状态空间模型 (SSM):RNN 与 Transformer 的优雅融合
人工智能·gpt·rnn·神经网络·自然语言处理·transformer·语音识别
hfywmsj1 小时前
广州餐饮铺位招租画像分析:从流量数据到选址落地
人工智能·广州餐饮铺位招租
麻瓜code1 小时前
[Agent]Spring AI 工具调用实战:让大模型真正“干活“(@Tool 六大工具 + 统一注册)
java·人工智能·spring
IT·陈寒1 小时前
React状态更新为啥有时吞了我的变更?
人工智能·大模型·api·创业·变现·简历优化
pen-ai1 小时前
【优化方法】最小二乘:从误差平方到线性与非线性拟合
人工智能·算法·机器学习