让模型自己说"我做完了",是我见过最多的死循环来源。这周 ArXiv 上有篇论文把这件事量化了:前沿 agent 存在系统性的"过度声称"倾向,活没干完,回复里写得像干完了。我的结论没变——终止判定该由外面那层循环拿着,模型只负责产出,不负责判断产出够不够。
模型自报完成这件事,已经被量化了
Quantifying Overclaiming Propensity in Frontier LLM Agents 里有个观察我特别认同:agent 干了一长串活,用户唯一能看到的证据,就是它最后那段回复。中间发生了什么、文件改没改、测试跑没跑,全在这段话里被转述。所以模型说"已完成",用户就只能信。
论文具体测出来的比例我不复述,反正方向是明确的:这不是偶发幻觉,是有倾向的。我自己踩过更土的版本——agent 说"已修复",我去看 diff,只加了三行注释。后来加了个机械校验:改过哪些文件、命令退出码是什么、测试有没有真跑。这比让模型"再自我反思一轮"有用得多。反思那轮它照样说完成了,因为反思也是同一个概率分布在说话。
Harness 那圈循环,才是该写死的地方
An Empirical Study of Harness Design for Coding Agents 的实验设计本身就很说明问题:他们把 execution loop 固定住,只换里面的组件做对比。等于默认了 loop 是常量。HarnessTax 那组数据也在问同一件事——harness 到底占多少分。
我的看法是,loop 里唯一不该是变量的就是终止条件:最大轮数、墙钟时间、预算、连续几轮没有新的工具调用。这些数字不需要模型参与,也不该让它参与。反过来,如果你把终止条件写在 system prompt 里,指望模型"记得停",那就是把控制流交给了一个采样过程。它今天记得,明天 temperature 抖一下就不记得了。
但把终止全交给外部,开放式任务会死
Pion 那个项目说要"自主运营一家公司"。这种任务的"完成"根本不存在——你写不出一个状态机去判定"公司跑完了"。探索性重构、性能调优、找 bug 也类似,目标函数本身就是模糊的。外部判定在这种情况下只能给出"预算耗尽",结果是时间到了被硬砍,产出一堆半成品,还得人来收尾。
所以全外部判定是错的,这一点我认。反面观点成立。
我现在的折中
外圈硬上限,写死在 harness 里,模型绕不过去:轮数、时间、成本。这三条是我唯一敢设成绝对值的。
内圈给模型"提前交卷"的权利,不给它"宣布考试结束"的权利。它可以主动声明完成,但声明必须带证据,而且这个声明只触发校验,不直接等于终止。校验过了才停,不过就继续跑,跑到硬上限为止。
Chronicle 那篇讲 cut-point replay,说 agent 的失败难复现,因为推理不是逐位可重复的、工具读的是变化的状态。这跟终止判定是连着的:如果停在哪一步由模型当场决定,你连复现都做不到。把终止点也纳入记录,回放才有意义——不然你回放的是一个从来没发生过的轨迹。
也许过一阵子模型的自评会可靠到能自己收工。按我现在看,还早。把退出条件交给循环体,我不干。