细说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说4 小时前
AI for Testing 提效实战·测试设计(二):让 AI 按等价类 + 边界值把用例补全
人工智能·测试覆盖率
CHEEVEN_QY4 小时前
液冷板热阻计算与流阻优化的实战方法
人工智能·算法·机器学习
李航19834 小时前
AI定制柜建模,需要详细的建模规范和标准流程
人工智能·python·计算机视觉·ai·ai编程
能源革命4 小时前
AI 日报 · 2026-10-03
人工智能
hahaha60164 小时前
色彩恒常性概述
人工智能·嵌入式硬件·数码相机·计算机视觉
FL16238631294 小时前
无人机视角海边沙滩垃圾检测数据集VOC+YOLO格式603张3类别
人工智能·yolo
Zguigo4 小时前
【CUDA6】CUDA Stream 是什么,为什么 CUDA 是异步执行,如何正确测量 GPU 时间以及多个任务如何重叠执行
人工智能·pytorch·深度学习
C++ 老炮儿的技术栈5 小时前
不一样的数值交换
数据结构·c++·人工智能·算法·c·csdn开发云
C++ 老炮儿的技术栈5 小时前
main函数之后的调用
c语言·c++·人工智能·qt·mfc·c
zhizhizhuzhuxia5 小时前
情感解说短视频的配音选型:从工程视角看情感音色与批量出片
人工智能·音视频