基准测试运行中 42 条 poetry 命令有 33 条因二进制无法启动而返回 -1,13 个 Agent 逐一撞墙无人察觉,耗时 128 分钟无报警完成任务。揭示了 AI Agent 系统中静默失败的可怕性。
8 月 12 日,我们的某次基准测试运行在一个沙箱中构建了代码仓库,但该沙箱中的 poetry 二进制文件无法启动。系统向它发出了 42 条命令,其中 33 条从未真正执行。十三个 Agent 一个接一个地撞上这堵墙,持续了一小时四十二分钟,而前一个 Agent 从未将这个情况告知后一个。
运行最终完成了。它交付了一个可工作的仓库,今天在我们的公开榜单上得分 77.6。
没有任何失败,没有触发任何警报,数字也没有任何变动。成本是真实存在的,完全由那些不复存在的轮次承担,花在了我们主动丢弃的分支上。
第一个 Agent 在 17:21 请求了一条 poetry 命令,什么都没有收到。最后一个 Agent 在 19:04 做了同样的请求。这次运行持续了 128 分钟,所以这堵墙贯穿了大部分时间,对最后一个 Agent 的隐藏程度和它对第一个 Agent 的隐藏程度完全一样。
样本量小到可以手动核查。这次运行共执行了 76 条 shell 命令,其中 42 条以 poetry 开头,33 条返回了退出码 -1——在我们的测试框架(即运行 Agent 并管理其调用工具的程序)中,退出码 -1 表示进程从未启动。整个运行中有 34 条命令未能启动;其中 33 条属于 poetry 这一个,另 1 条是一个同样未能启动的 powershell 调用。
撞上这堵墙的十三个 Agent 是四个角色中的十三个独立实例,其中九个是编码 Agent,两个是开发 Agent,一个构建 Agent和一个测试 Agent。运行页面按角色计数而非按实例计数,所以那里的数字更小。
每一个 Agent 都成功恢复了,而这正是导致问题不可见的原因。当一个编码 Agent 无法运行 poetry run pytest 时,它会尝试 python -m pytest,拿到一个结果,然后继续执行任务。恢复是我们期望的行为。也正是这个行为,把一个一次性的环境问题变成了一笔被征收了十三次的税。
第一个怀疑对象显然是模型。我们有一个未曾计划的对照组。同一天,另一个供应商的模型在相同规格下运行。那个运行执行了 137 条命令,其中 59 条以 poetry 开头,44 条从未启动。十九个 Agent 在四小时十三分钟内撞上了这堵墙。
两个供应商的两个模型产生了相同的行为,这让模型成为最不可能的解释。这是一步验证而非定论,因为两次运行都运行在同一个测试框架内,框架层面的缺陷会复现到任何坐进那个位置的模型上。
我们的 Agent 以两种模式工作。Agent 在一个策略对话中规划,即其运行中的规划线程。当它承担一项具体工作时,它打开一个分支——一个独立的短期对话,仅保存那个任务,在其中执行,然后关闭。架构设计文档将隔离表述为一个成功标准。工作分支对话与策略对话完全隔离,只有完成报告跨越边界。
分支随后被丢弃。返回的是完成报告,下一个 Agent 的分支从策略对话开始,后者从未见过前一个分支的内容。
架构要求那份报告按照 Agent 被交付的工作契约来组织,即它所承担的那一项工作的书面规格。这使得报告成为一份关于交付物的陈述,而边界存在的目的是保护这种形态,因为隔离使一个 Agent 的上下文窗口足够小到可以推理、足够便宜到可以在这个规模上运行。
一个死掉的二进制文件不属于任何交付物。报告所回答的契约中没有任何内容问及它。它是途中遇到的摩擦,在一个轮次内被绕过,之后再未被提起,所以它进了分支被丢弃的那一半。下一个 Agent 带着干净的窗口和同样的错误假设到来。
系统记住了它构建了什么,忘记了它学到了什么。它构建的东西有一条从分支中出来的路径。它学到的东西没有。
为什么没有任何东西发现它

这个问题躺了一个月,它躺一个月的原因和它值得被写下来的原因是一样的。我们监控的每一个信号都指向别处。仓库没问题,分数出来了没问题,唯一死掉的二进制文件的痕迹是日志中 33 行没有人有理由去打开的内容。
我们确实有一层具备跨 Agent 视角的东西。一个监督 Agent 在运行无人值守时检查每一个在线 Agent,根据信任分数给予功过评分,并可以将 Agent 撤下岗位。它监视着整个舰队。它监视的是 Agent 行为,而死掉的二进制文件不是 Agent 行为,所以它没有理由去看。
一个在破碎环境中绕行的 Agent 和一个工作出色的 Agent 看起来完全一样。更努力地监视也关闭不了那个差距,因为没有什么异常的东西可以发现。
信号其实已经在退出码里了。我们没有去读它。
在整个运行中,31 条命令成功执行,11 条执行后返回了非零状态,34 条从未启动。后两类在日志中看起来相似,但含义截然相反。
一条执行后返回非零退出的命令是关于你代码的陈述。它归属于发起它的那个 Agent,因为下一个 Agent 在不同的时刻面对不同的文件,答案不会传递。
一条从未启动的命令通常是对机器的陈述,而不是对某个 Agent 代码的陈述。这里对全部十三个来说都是如此,而且这个结论在环境改变之前一直成立。这不是自动的。一个格式错误的调用,或仅限于一个沙箱的权限,也可能启动失败,这就是为什么下面的路由规则是一个默认值而非定律。
退出码 -1 是我们自己的约定,所以可移植的部分是问题本身而非数字。这条命令是执行了但和你不一致,还是根本没有执行?把两类命令在进程返回的地方分开,只给第二类一条从分支中出来的出路。在运行开始时解决一次工具链问题,并把答案写在每个 Agent 都读到的地方。当某样东西在运行中途仍然启动失败时,把它归档为一条环境事实而非任务失败,放到同一个地方。其他一切保持在本地。
克制和路由一样重要。分享每一个失败会重建分支设计本要避免的扩展式公共上下文。关于机器的事实是一个有界的集合,一旦机器不再给你惊喜,它们就停止累积。
尚未修复。上面的运行来自 8 月,行为没有改变,将监督层连接到环境事实是下一个显而易见的行动,但我们还没有做。
如果你用一个 Agent 舰队对抗一台共享机器,你的 Agent 们已经可以从破碎的环境中恢复。它们会一个接一个地做这件事,而且在此过程中会把成本藏起来。这周要检查的是,你的系统中是否有任何一个地方可以放置某个 Agent 摸索出来的东西。
这里的两个声明可以在不同深度上验证。grok-4.6 的运行和第二个供应商在相同规格下的运行都是公开的,页面承载了分数和已发布的运行统计。
Favur 本身是闭源的且仅限邀请,Its runs 产生的仓库是开放的。榜单保存了我们已评分的每一次运行,如果你想自己拆解的话。