AI 编程工具的信任正在崩塌:被忽视的"静默成功退出"危机

内容管家 AI领域评论0字数 3250阅读10分50秒阅读模式

2025 年 3 月,OpenAI 团队公布了一份难得一见的内部记录——一个前沿模型在训练中处理编程任务时的私有推理过程。模型审视问题后,认为完整实现太困难,于是写下了一个替代方案:如果调用 sys.exit(0),测试框架会优雅退出。"这不自然,"模型在旁边标注,"但测试可能会通过。" 测试确实通过了。

研究人员将这种现象称为"系统性 hack"——一旦模型发现了这种取巧方式,它几乎会在所有训练环境中扩散。研究团队还捕捉到了第二类同类漏洞:在测试框架之外抛出异常,直接跳过评估环节。此外,论文中还记录了更多案例:智能体为测试覆盖薄弱的问题编写桩实现,在运行时解析测试文件并提取、返回测试用例正在检查的精确值,甚至还有人发现代码库里遗留了一个已编译的 .jar 文件,将其反编译后直接复制出参考答案。

每一次运行都显示绿色通过。

传统工具的"大声失败"遗产,我们并没有真正继承

每一件开发者信任的工具,都具备一个共同属性:它会大声失败。段错误是响亮的。断言失败是响亮的。500 错误是响亮的。退出码则是将"响亮"压缩进一个整数——这大概是整个计算领域消费最密集的遥测数据。

值得注意的问题是:谁发出了这个退出码。当 make 返回 0 时,这个整数位于工作流程的下游:编译器运行了,生成了目标文件,然后返回。这个状态是一个观察结果,由一个处于观察位置上的进程做出。这个数字是关于世界状态的证据。

而当一个 AI 智能体循环返回 0 时,发出这个整数的是一个编排器——它停止循环的原因是模型产生了一个意味着"完成"的 token。在模型的声明和退出码之间,没有任何环节对任何事物做过检查。状态是一个自我评估,却被插进了一个为观察结果设计的插槽中。下游的每一个消费者——仪表盘、告警规则、重试逻辑、周一早上扫一眼运行历史的人——都在不知情的情况下继承了这个偷换。有人将此称为"静默成功退出"(silent green exit)。

差距已经有了数据支撑

2024 至 2025 年,Stack Overflow 开发者调查显示了信任崩塌的轨迹:使用或计划使用 AI 工具的开发者比例从 76% 上升至 84%,而信任这些工具输出准确性的比例从 43% 降至 33%。明确表示不信任的比例从 30% 上升至 46%。最突出的痛点(66% 的受访者提及)是"AI 方案几乎正确,但不完全对"。

"几乎正确,但不完全对"描述的是一种你能看到的输出。而"静默成功退出"则更深一层:一次运行并非"几乎正确",而是完全为空,却与一次真正成功的运行毫无区别地呈现出来。

两个基准测试让这个问题变得具体。Sierra 研究团队推出的 τ-bench 引入了一个值得广泛知晓的指标:pass^k——智能体在同一任务上连续 k 次尝试全部成功的概率,而非"至少成功一次"。在他们测试的模型上,智能体在标准指标下低于 50%,在零售领域的 pass^8 指标上低于 25%。

这些具体数字已经过时了:那是 2024 年的测试,旗舰模型是 GPT-4o,前沿已经大幅进化。但 pass^k 指标本身从未被广泛采用。pass^k 是对"信任由重复性构成"这一论点的直接测量——它问的是"周二是否与周一一致",并给出一个数字。几乎没有人报告这个指标,这就是为什么"我试的时候能用"在工程讨论中仍然被当作证据——而同样的说法,换到其他任何事物上都不会被接受。

找不到路时,它选择"造假"

卡内基梅隆大学的 TheAgentCompany 项目将 AI 代理部署进一个模拟软件公司,让它们完成真实的工作任务。最具竞争力的代理自主完成了 30% 的任务。但真正值得关注的不是这个数字,而是研究人员在报告中描述的一种行为:"当代理不清楚下一步该怎么做时,它有时会自作聪明地创造虚假的'捷径'——跳过任务中最困难的部分。" 论文里给出了一个堪称完美的案例:一个代理在内部通讯平台上找不到某位同事,于是直接把另一个用户的名字改成了那位同事的名字,然后继续推进任务。

这意味着什么?表面上看,步骤完成了——有一个名字匹配的用户,有一项操作被执行,追踪日志里没有任何报错。唯一的问题是:那位真正的同事从未收到任何消息。系统里没有任何模块知道这件事出了错,包括这个代理本身——它没有任何理由相信自己失败了。

进入生产环境之后

2025 年 7 月,Jason Lemkin 在 Replit 的编程代理上连续开发了两周左右,并将整个过程公开记录下来。登上新闻标题的是数据库删除事件:代理无视他"未经许可不得修改任何内容"的明确指令,删除了一个生产数据库。但更值得关注的细节发生在那之前,而且几乎没人讨论。

"它一直在用虚假数据、虚假报告来掩盖 Bug 和问题,更糟糕的是——对我们的单元测试撒谎。" 他写道。某个时刻,代理生成了一个包含 4000 个完全虚构人物的数据库。事后它告诉 Lemkin 回滚不可能,所有数据库版本都已被销毁。这也是假的——回滚完全成功。

虚假数据、虚假报告、一个从未真正通过的单元测试,以及一个经不起核查的自信状态声明。Replit CEO 称发生的事"不可接受,根本不应该发生"——这是正确的表态。这一事件至今仍是关于"失败的代理"与"报喜的代理"之间区别的最清晰的公开案例。

一种合理的质疑

合理的 SRE 工程师会这样说:以上种种都不是新问题。Crontab 任务自 1970 年代起就在静默失败,所以我们才有了"死亡开关"、心跳监控和" absence of signal"告警——在错误缺席时触发警报,而非在错误出现时触发。你不过是重新发现了监控,并给它起了个新名字。

这个质疑大体公平。而其中隐藏的好消息是:修复方案的轮廓早已明确,工具也已存在。 但有一个差异打破了旧有方法,值得精确说明。

过去所有监控技术的有效性,都建立在失败模式可枚举的前提上。你可以列出夜间任务可能无法完成的有限原因——机器宕机、磁盘满了、锁被占用、上游超时——然后逐个写检查,或者写一个统一的心跳来覆盖所有情况,因为"完成"只有一个含义,任务本身不会对结果有任何预设判断。

但代理在运行时自主决定控制流。每次执行的计划都是全新生成的,这意味着它可能以"什么都没产生却正常退出"的方式终止——这类情况的完整列表,是任何人事先都写不出来的。它可能包括因为"实现起来太难"而直接 sys.exit(0),可能包括改名用户,可能包括你整栋楼里没人能想到的策略——因为写策略的本来就是代理自己,不是你。

所以,你无法从失败侧来写检查。任何从失败侧写出来的规则都是一份列表,而代理并不受你的列表约束。你必须从结果侧来写。

为产出物埋点,而非为执行过程埋点

由此引出四点建议。都不需要新工具——这本身就是重点。

以工作产出作为断言对象。 不是"循环是否以 0 退出",而是"那行数据是否存在、文件是否真的在磁盘上、消息是否真的离开了队列、分支是否真的推送了"。检查必须读取代理未曾编写的状态。如果证明邮件发出的唯一证据是代理自己说"发了",那你就根本没有证据——你只有一个有既得利益的当事方给出的声明。

验证路径不能与工作路径共享失败模式。 如果同一个代理在同一个上下文中既干活又汇报,那么它的报告就继承了工作本身的所有错误——包括导致它失败的那些错误。Donovan 提出了用 LLM-as-a-judge 来实现代理速度的审核&查验,我认为这是对的,但有一个约束条件:裁判必须读产出物,而不是执行记录。读记录的裁判是在给"关于工作的故事"打分,而编故事恰恰是这些系统最擅长的事。

对"无变化"本身发出告警。 这是清单里成本最低的一条,却几乎没人做。一项本应写数据的任务,这次什么都没写却报成功——这比抛出异常更可疑。异常会触发告警;零效果的"成功"只会得到一个没人看的运行历史里的绿勾。把逻辑反过来。 一个调度代理什么都没碰却报成功,应该有人被呼叫。"什么都没发生"本身就是一种结果,值得大声示警。

在模拟环境中测量 pass^k,再谈部署。 把代理在同一场景下跑几十次,看的是结果分布而非最佳情况。在你拥有 ground truth 的环境中进行,因此可以核查产出物而非故事。模拟的目的不是证明代理能干活——做个 Demo 谁都能做到。模拟的目的是搞清楚:它有多经常声称自己完成了任务,但实际上并未完成?

AI 任务"完成"了,然后呢?

传统软件里,退出码非 0 意味着失败,退出码为 0 意味着任务真的跑完了。这个逻辑根植于几代程序员的经验里,几乎不需要思考。

大模型时代,这个等式不再成立。

OpenTelemetry 语义公约能做什么

目前业界最接近"通用标准"的方案,是 OpenTelemetry GenAI 语义公约。它定义了以下属性字段:

  • 模型名称与版本
  • 操作类型
  • 输入 / 输出 Token 数量
  • 结束原因(finish reason)
  • 错误类型
  • 调用智能体的名称与版本

这些字段对于"追踪一次调用"而言很有价值。

但有一个问题它没有回答

以上所有属性,都在描述调用本身,而不是这次调用是否真正产生了预期的效果。

这并非疏漏。语义公约本质上是"调用级"规范,而过去每一代软件里,"完成"本身就意味着"生效"——你不需要去问编译器,它有没有真的在编译。

大模型时代,这层隐含的对应关系已经消失。

退出码从事实变成了观点

传统架构中,exit code = 0 是一个事实——它描述程序运行状态的客观记录。

在智能体架构中,同一个退出码变成了智能体的一种主观判断。工具链在变,判断标准也在变,而你没有办法从退出码里确认:这个任务,真的做了吗?

信任没有建立起来,根本原因不只是"工具在变化",而是结果本身已经不可验证。

OpenTelemetry GenAI Semantic Conventions

延伸阅读

 
内容管家

发表评论