AI Agent 死循环陷阱:三层防线把「无限烧 token」变成早停

Agent 反复说「让我检查一下」,却始终不进入下一步、也不真正去查——这不是卡住,是死循环。本文给出三层防线:Prompt 双约束、重试/去重上限、平台 Loop 检测,把无限烧 token 的等待变成可预期的收敛。
真实翻车案例
一次真实翻车来自 qwen-3.7-plus:在检查 kylin_app/apps/CeXia 目录状态时,它一遍遍输出”让我检查 CeXia 目录状态”这样的思考文本,却始终没有真正发出 Glob 工具调用。没有工具执行,就没有 Observation 返回,上下文里只有它自己的碎碎念;于是它一遍遍重复同样的意图,最终被系统的 Loop 检测机制强制中断,报错:
Loop was detected in the model and the request has been interrupted

值得注意:它每个循环里都得出”我还需要检查目录”,却没真正把检查这件事派发出去。而当你把任务交给 Agent 晚上无人值守地全自动跑时,代价会被急剧放大——白天还能手动打断,晚上没人盯着,一旦死循环就是一整晚纯浪费 token。
是模型问题,还是 Agent 框架问题?
结论先给:绝大多数是 Agent 系统(Prompt / 工作流)问题,不是纯基座模型缺陷。 基座模型本身具备调用工具的能力,是被你的 Agent 配置(Prompt 写法、解析流程、循环兜底逻辑)拖垮了。
框架拿到的是一堆散文,解析不出工具调用,自然没有工具运行、没有 Observation 回流;模型发现历史里什么都没变,就又重复一遍同样的意图 → 死循环。最后由运行时的 Loop 检测安全护栏杀掉任务。“Loop was detected” 这条报错只是安全拦截器,不是死循环的来源,只是事后止损。
死循环的两种形态
- 形态 A:只思考、不行动(ReAct 格式违规,概率最高)。模型只输出
Thought级自然语言,从不产出Action工具调用块。没有工具执行 → 没有 Observation → 历史无变化 → 重复同一意图。本文案例即属此类。 - 形态 B:重复同一动作、同结果不换策略(缺状态变化检测)。模型确实发出了工具调用,但反复跑同一个检查、每次结果相同,却没意识到”结果相同就该换策略”。
两种形态都源于 Agent 侧,而非底座模型本身。本文防线对两者同时有效。
根因拆解
- 🔴 Prompt / ReAct 格式违规(概率最高):system prompt 对”必须输出工具调用格式”指令模糊;上下文塞满重复历史扰乱格式遵循;stop-sequence 在工具调用块完成前截断。结果模型只产出散文,框架解析不出工具调用。
- 🟡 Agent 运行时 / 框架层:循环检测器是安全护栏,不是源头;若缺去重逻辑,不会阻止连续步骤里重复相同思考。
- 🟢 纯基座模型因素(概率低):长上下文下偶尔不遵循工具调用格式,但很少每轮都稳定失败。
方案总览:三层防线,层层设防
防御由「Prompt 规则 + 重试/去重上限 + 平台兜底」三层组成:
- 防线 1 · Prompt 双约束:① 格式约束——”若需查看目录,必须在 Thought 之后立即输出合法工具调用块,不得只写自然语言描述”;② 防重约束——”若连续 2 次返回相同结果,立即切换策略”。
- 防线 2 · 重试/去重上限:循环类动作上限 3 次,超限强制停止或报错;拒绝连续步骤里语义相同的思考;设”最大连续非行动轮次”,N 轮无工具调用即终止。
- 防线 3 · 平台 Loop 检测:优先选用带 Loop 检测机制的 IDE/平台,系统自动识别并中断重复执行。
- 工程化辅助:状态记录与比对(识别”无进展”)、明确推进条件(没进展就换路)、解析失败回灌(把解析失败作为 Observation 回灌,而非追加原始自由文本)、上下文压缩、超时护栏。
设计规则(不变式):
- 任何多步 Agent 任务必须先定义「停止条件」与「行动格式要求」,无进展即触发切换或终止。
- 单条规则不够——Prompt 约束、重试/去重上限、平台检测三层并存。
端侧(普通用户)怎么自救与预防
无需改源码也能显著降低中招概率:
- 已陷入循环:❌ 别只点
continue(几乎必然重复失败);✅ 放弃会话,开全新任务 / 新窗口,只带走高层需求。 - 预防式 Prompt:避免”继续把整个 Stage 4 跑完”的巨型指令。显式点名第一步要跑哪个工具、强制首步工具调用、加”不要只描述,立刻执行对应工具”;把大任务拆成顺序小任务,别让 Agent 单会话跑 30+ 轮。
- 运行时习惯:轮数 >20–25 就手动停、用总结进度开新任务;可换更稳的模型;尽量别用深层嵌套子 Agent 流程。
- 复用进度:手动总结成紧凑摘要喂给新任务,别拖着几千 token 的坏掉历史。
效果与收益
这个防御把「无限烧 token 的等待」变成「可预期的收敛」:
- 之前:Agent 反复输出同样思考(或同样检查),死循环,靠系统 Loop 检测被动中断,期间烧掉大量 token、拖住时间。
- 之后:循环在第 2~3 轮就被主动掐断,只损失几轮的 token。
对夜间无人值守的全自动任务,这笔账更直观:有防线,早检测早停止;没防线,可能烧掉整晚的量,第二天只剩一条异常账单。真正的量化信号待后续采集:记录死循环被触发/被阻断的次数,与加入三层防线后的对比,量化省下的 token 与时间。
复现要点
- 写一个多步检查任务,去掉行动格式约束与防重规则,观察是否陷入”只思考不行动”或”重复同结果不换路”。
- 在 Prompt 里加「Thought 后必须立即输出工具调用 + 连续 2 次相同结果立即切换策略」+ 重试/去重上限 3 次,重跑对比。
- 优先选择带 Loop 检测机制的 IDE/平台,确认兜底生效。
- 把带防线的任务放在夜里无人值守跑一次,次日核对:任务正常收敛、无异常 token 消耗。
总结与延伸
- 死循环根因大多是 Agent 系统(Prompt / 工作流)问题:模型只输出思考文本、没发出结构化工具调用,”Loop was detected” 只是安全拦截,不是源头。
- 两种形态:只思考不行动(最常见)、重复同动作同结果不换策略,都靠 Agent 侧显式规则化解。
- 三层防线:Prompt 双约束、重试/去重上限、平台 Loop 检测兜底;端侧靠”拆小任务、逼出首步工具调用、陷入循环就开新任务”自救。
下次写多步 Agent 任务前,先问一句「它停在什么条件?第一步会真的发出工具调用吗?」——把停止条件与行动格式写清楚,死循环就没了温床。尤其是打算让它晚上全自动跑的任务:先确认早检测、早停止的防线就位,再安心去睡,别让账单替你值夜班。
说明:本文为方法论述,收益为定性观察,量化指标(死循环触发/阻断次数、防线前后 token 对比)建议在实践中回填。