当 Agent “很努力但没用”——伪进展检测

2026-10-06·终止与自愈·连续错误检测

Agent 每轮都在调用不同的工具、尝试不同的参数------看起来在'积极工作'。但结果始终是错误。CycleDetector 不触发------因为没有完全相同的重复。这种'换着花样犯同一类错'的模式叫伪进展------操作在变化,但无一成功。

一个看起来正常的 Run

看这段 Agent 执行日志------每一行都是一次工具调用:

bash 复制代码
轮次 31: exec("python -m pytest tests/models/")         → FAILED (3 errors)
轮次 32: edit("models/query.py", patch_v1)                → OK
轮次 33: exec("python -m pytest tests/models/")         → FAILED (2 errors)
轮次 34: edit("models/query.py", patch_v2)                → OK
轮次 35: exec("python -m pytest tests/models/")         → FAILED (3 errors, different)
轮次 36: edit("models/query.py", patch_v3)                → OK
轮次 37: exec("python -m pytest tests/models/")         → FAILED (2 errors, same as #33)
轮次 38: edit("models/query.py", patch_v4)                → OK
轮次 39: exec("python -m pytest tests/models/")         → FAILED (3 errors, same as #35)
...持续 20+ 轮

从任何单一轮次看------Agent 在正常工作:读文件、改代码、跑测试。每次的 patch 不同、每次的错误信息不同。CycleDetector 的 Identical Call 检测不触发(参数在变),Category Streak 检测也不触发(exploration 和 action 在交替)。

但从全局看------Agent 在"打地鼠"。 修了错误 A 引入了错误 B;修了 B 又引回了 A。测试错误数在 2 和 3 之间反复震荡,从未降到 0。20 轮的工具调用消耗了大量 token------但距离"测试通过"的目标没有任何接近。

这就是伪进展(Pseudo-Progress):操作在变化,但没有一次成功地推进了目标。


伪进展 vs 死循环------为什么 CycleDetector 抓不住

死循环和伪进展是两种不同形态的"Agent 在空转":

维度 死循环 伪进展
操作是否相同 完全相同(参数+结果相同) 不同(参数在变)
表面看起来 "在重复同一件事" "在尝试不同方法"
CycleDetector 检测 ✅ Identical Call 触发 ❌ 参数不同,不触发
人类的直觉判断 容易识别("它怎么又搜同一个东西") 难识别("它在努力呢,再等等")
对用户的误导性 低(明显在重复) 高(看起来在做事)

伪进展比死循环更危险------因为它更难被发现。 死循环的症状是"Agent 在重复"------用户一眼就能看出来。伪进展的症状是"Agent 在工作但进展慢"------用户可能等很久才意识到"它根本没有在接近目标"。

$47,000 事故中的 Analyzer-Verifier 循环就是一种伪进展------Analyzer 每次产出不同的分析报告(不重复),Verifier 每次给出不同的改进建议(不重复),但没有一次批准通过。从 CycleDetector 角度看------完全合法的工作过程。从 ErrorStreak 角度看------Verifier 对 Analyzer 输出的"拒绝"连续了 264 小时。


伪进展的常见模式

模式一:编译-修改震荡

Agent 改代码 → 编译失败 → 修一个错误 → 编译失败(不同的错误)→ 修这个错误 → 编译失败(之前的错误回来了)。

特征:测试/编译错误数在 N 和 N+1 之间震荡,从未单调递减到 0。

根因:Agent 在一个错误的假设下工作------比如它以为问题在 A 文件,但问题实际在 B 文件。它在 A 上的每次修改都引入新错误,因为 A 本身不该被改。

来自设计文档的真实案例------SWE-bench django-11734:Agent 在做评测任务时连续 20+ 轮在错误的环境假设下盲目重试。每次的修改不同但编译/测试一直失败。ErrorStreak 在 L2 阶段(6 次连续错误后)触发了 strategy reset------强制 Agent 退出当前修改策略、创建一个反思性的 Plan 重新分析问题。Agent 发现了之前的错误假设,换了正确的方向,最终在第 196 轮完成任务(10.2M tokens)。

如果没有 ErrorStreak :Agent 会继续在错误假设下重试------可能永远不会自行意识到假设错误。最终被 max_turns 或 token 预算强制停止,196 轮的有效工作全部浪费。

模式二:路径/名称猜测

Agent 尝试安装一个包、访问一个 API、读取一个文件------但用了错误的名字。它换不同的名字继续尝试:

bash 复制代码
轮次 1: exec("npm install phantom-rendering")     → error: 404
轮次 2: exec("npm install phantom-render")         → error: 404
轮次 3: exec("npm install phantomjs-renderer")     → error: 404
轮次 4: exec("npm install phantomjs-prebuilt")     → error: deprecated
轮次 5: exec("pip install phantomjs")              → error: not found
轮次 6: exec("yarn add phantom-render-node")       → error: 404

每次的参数不同------CycleDetector 不触发。但 6 次连续 404/not found 说明模型在一条死路上:它猜测的包名全都不存在。

根因:模型幻觉了一个不存在的包名,然后在"也许拼写不对"的逻辑下反复变换拼写。正确的做法是用搜索引擎确认包名是否存在------而不是继续猜。

模式三:API 调用连续失败

bash 复制代码
轮次 1: exec("curl -X POST https://api.example.com/v1/users ...")  → 401 Unauthorized
轮次 2: exec("curl -H 'Authorization: Bearer xxx' ...")             → 403 Forbidden
轮次 3: exec("curl -H 'X-API-Key: xxx' ...")                        → 403 Forbidden
轮次 4: exec("curl --user admin:password ...")                       → 401 Unauthorized
轮次 5: exec("curl -H 'Authorization: Basic xxx' ...")               → 403 Forbidden

模型在尝试不同的认证方式------但所有尝试都失败。它没有正确的凭据,再怎么换认证头格式也不会成功。

这是伪进展最具欺骗性的形态 ------每次尝试看起来都"合理"(尝试不同的认证方式是面对 401/403 的正常反应),但正确的做法是停下来问用户要凭据,而不是继续猜。

模式四:空响应连续出现

一种特殊的伪进展------模型返回了空响应:没有 tool_call、没有 text response。字面上"什么都没说"。

这不是"任务完成"------是 provider 暂态故障或模型困惑。引擎对空响应先重试(最多 3 次)。仍空则计入 ErrorStreak------因为连续空响应意味着模型陷入了一种"无法产出任何有用输出"的状态。


ErrorStreak 的检测算法

算法核心很简单------一个滑动窗口计数器:

bash 复制代码
每次工具调用完成后:

if 结果是错误:
    if 和上一次失败的调用"相似"(同一类错误):
        consecutive_errors++     // 相似的错误累积
    else:
        consecutive_errors = 1   // 换了一种错误,重新开始计数
        lastFailedCall = 当前调用
else:
    consecutive_errors = 0       // 成功重置一切
    return

if consecutive_errors == WarnThreshold (默认 5):
    → 注入 system 警告:"你正在重复失败的操作。
       最近 5 次工具调用全部返回错误。
       请退一步重新分析问题,而不是继续用相似的参数重试。"

if consecutive_errors == TermThreshold (默认 8):
    → 进入两阶段协议(与 CycleDetector 共享)
    → 首次:strategy reset(强制 Plan 反思)
    → 二次:HITL 问用户 → 超时 60s → 终止

配置参数

参数 默认值 含义
ErrorStreakWarnThreshold 5 连续 5 次错误 → 注入警告

| ErrorStreakTermThreshold | 8 | 连续 8 次错误 → 进入两阶段协议 | | ErrorStreakWindowSize | 10 | 滑动窗口大小 |

重置条件

最重要的设计选择:一次成功就重置一切。

只要有一次工具调用成功(isError=false),连续错误计数归零。

这意味着------如果 Agent 在 8 次连续错误中的第 5 次夹了一次成功的 read 调用:

bash 复制代码
exec("go build")      → error    streak=1
exec("go build")      → error    streak=2
exec("go build")      → error    streak=3
exec("go build")      → error    streak=4
read("error_log.txt") → success  streak=0  ← 重置!
exec("go build")      → error    streak=1  ← 从 1 重新开始

为什么一次成功就重置? 因为成功的工具调用意味着 Agent做了一件有用的事------即使它接下来又开始失败了,那也是一段新的失败序列,不是上一段的延续。Agent 从失败中收集了信息(读了错误日志),这改变了它的认知状态------后续的失败可能有不同的原因。

这和 CycleDetector 的 halving 设计理念一致------区分"盲目重复"和"在尝试中学习"。

"相似"的定义

ErrorStreak 在判断"和上一次失败是否相似"时用的是宽松标准------同一类工具 + 错误类型相近(都是 404、都是编译错误、都是权限拒绝)。不要求参数完全相同。

为什么宽松? 因为伪进展的特征就是"换着花样犯同一类错"------参数不同但错误相似。如果要求"完全相同"------那就变成了 CycleDetector 的 Identical Call 检测,两者重叠。

如果 Agent 真的换了一种完全不同 的策略(从 npm install 换到 pip install,错误类型从 404 变成了 "pip: command not found"),计数器重置为 1------新的策略值得给新的机会。


自愈策略:strategy reset

ErrorStreak 的自愈和 CycleDetector 的自愈不同。CycleDetector 注入"你在重复操作"的纠正消息------因为问题是"重复"。ErrorStreak 注入的是更强力的干预------强制 Plan 反思。

为什么 ErrorStreak 需要更强的自愈

CycleDetector 捕获的循环通常是"模型陷入了一个局部最优"------一条纠正消息就能把它推出来。

ErrorStreak 捕获的伪进展通常是"模型在一个根本错误的假设 下工作"------简单的"请换一种方法"不够,因为模型不知道自己的假设是错的。它需要退后一步重新分析问题。

strategy reset 的具体行为:

  1. 注入一条 system 消息:"你最近的 N 次工具调用全部失败。这可能意味着你当前的策略方向有根本性问题。请暂停当前操作,重新分析问题的根因------你的假设是否正确?是否需要换一种完全不同的方法?"
  2. 如果当前有活跃 Plan------在 Plan 中标记"strategy reset triggered",给模型一个"回到 Plan 视角审视全局"的信号
  3. 重置连续错误计数

SWE-bench django-11734 的 strategy reset 效果:Agent 在收到 strategy reset 后:

  • 停止了在错误文件上的修改
  • 重新读取了任务描述和错误信息
  • 发现了之前的环境假设错误
  • 换了正确的方向继续工作
  • 最终在 196 轮完成任务

如果没有 strategy reset ------Agent 会继续在错误假设下修改代码。max_turns 最终停止它,但 196 轮中前 20+ 轮的浪费已经不可挽回。


行业中的连续错误处理

Claude Code auto mode:不检测连续错误

Claude Code 的 auto mode 分类器审查的是操作的安全性和方向性------"这个操作是否偏离用户请求""是否针对未识别的基础设施"。

但分类器不检测连续失败。 如果 Agent 反复用不同参数调用一个合法的工具(npm install 是安全的、符合方向的)------分类器会让每一次通过,即使连续 20 次都返回 404。

这是分类器方法和模式检测方法的根本差异------分类器看单次操作的"内容",ErrorStreak 看操作序列的"结果模式"。

Cursor Auto-review:同上

Cursor 的分类器也不检测连续失败------它检测的是单次操作的风险等级。连续失败的操作如果每次都是低风险的(exec("npm install xxx")),会被自动通过。

大多数框架:无此机制

翻看截至本文写作时的 LangGraph、CrewAI、OpenAI Agents SDK、Hermes Agent 等主流框架------均没有内置的连续错误检测 。唯一的兜底是 max_turns / max_iterations。

这意味着------一个 Agent 可以连续失败 100 次(但参数每次不同)而不被任何机制拦截。直到 max_turns 生效。

"When Errors Become Narratives"(arXiv:2606.14589)

2026 年 6 月发表的一篇生产 Agent 运行时静默失败分类学论文指出了一个更深层的问题------模型把错误转化为叙事:

"The system does not merely fail to report an error --- the LLM transforms it into fluent, plausible narrative delivered to the user."

当 Agent 的工具调用连续失败时,模型不会说"我失败了 8 次"------它会说"我正在系统性地排查问题,已经排除了 A、B、C 三种可能性"。从用户视角看------Agent 在做有用的排除法。从实际效果看------Agent 在同一面墙前反复撞头,只是每次撞的位置不同。

伪进展最危险的地方:模型会用合理的叙事包装自己的失败。 用户被叙事说服了------"它说在排查呢,再等等"。ErrorStreak 不看叙事------只看结果。连续 8 次错误就是连续 8 次错误,不管中间的解释多么合理。


你现在能做什么

1. 加一个"连续错误计数器"------即使只是最简单的版本。 每次工具调用返回错误 → counter++,成功 → counter=0。counter >= 5 → 注入一条"你的最近 5 次操作全部失败,请重新分析问题"的 system 消息。10 行代码,效果立竿见影。

2. 区分"单次错误"和"连续错误"。 单次编译失败是正常的------改一下再试。连续 5 次编译失败意味着模型的修复策略可能方向错了。不要对单次错误过度反应,但连续错误必须触发干预。

3. strategy reset 比简单纠正更有效。 ErrorStreak 的纠正不是"换一种方法试试"------是"停下来,重新分析你的假设是否正确"。后者迫使模型退后一步看全局,而不是在错误的路上继续跑。

4. 不要被模型的叙事误导。 模型说"正在系统性排查"不等于真的在进展。看结果------如果工具调用连续返回错误,不管模型怎么解释,它就是在失败。

5. 一次成功就重置计数器。 不要累积------成功意味着 Agent 做了一件有用的事。后续的失败是一段新的序列,不是上一段的延续。


引用来源

本文所有数据均可通过以下链接验证。

相关推荐
择势1 小时前
iPhone Duo 适配实践:从几何原理到工程封装
ai编程
时早玖沐1 小时前
给 AstrBot 写一个天气推送插件:12 次提交、6 个坑
python·开源·ai编程
灿飞2 小时前
第 3 章 第一个 Mojo 程序与 CLI 全解
ai编程
大模型真好玩2 小时前
DeepSeek Harness 插件的服务与事件监听详解
人工智能·agent·deepseek
程序员老刘2 小时前
从短剧App的bug看响应式编程在AI时代的优势
flutter·ai编程·客户端
ServBay2 小时前
API Key 泄露如何在 10 分钟内止血?AI 密钥管理必备
后端·ai编程
阿里云云原生3 小时前
Durable Agent 详解:如何让 Agent 在云端长期稳定运行
agent
武子康4 小时前
手机加语音配件,断开连接后谁来保住这段话?
人工智能·后端·agent