软件工厂为何失败:SlopCodeBench基准测试揭示真相
本文是《Why Software Factories Fail》系列第三部分,聚焦于新提出的长期编程基准测试SlopCodeBench。作者测试了Claude Opus 5、Sonnet 5和Opus 4.8在逐步揭示需求的编程挑战中的表现,发现即使是最先进的模型也仅取得24%的严格通过率(Opus 5),且随着检查点推进,代码膨胀、复杂度上升、重复率增加等“垃圾代码”指标显著恶化。文章指出,当前模型在需要长期维护代码库的真实软件工程任务中仍不可靠,无法实现“无人值守”开发。作者认为SlopCodeBench是衡量模型维护代码能力的有前途的信号,并讨论了未来方向,如使用更小的模型来放大代码质量信号。

这是《为什么软件工厂会失败》第一部分和第二部分第一部分:脚手架不够 第二部分:重新开灯的延续。
我们有了更好的基准
还记得我在第一部分说过这句话吗?
没有好的基准来衡量模型维护代码库质量的能力。
这并不完全正确,但我当时想稍微埋个伏笔。在这篇文章中,我们将展望未来。
我们将深入探讨 SlopCodeBench,一个较新的(2026年3月)长周期编码基准,来自威斯康星大学麦迪逊分校 @GOrlanski 的实验室。它正好解决了我们在第一部分中指出的问题——即使是“更大”、更复杂的基准,仍然会提前透露整个问题:

然而,SlopCodeBench 中的每个挑战都有多个“检查点”——模型一开始不知道整个问题,它必须随着新需求的逐步透露而不断演化代码库。这是一篇不错的论文,篇幅不长。你应该读一读。

这个基准的酷炫之处在于它尚未饱和——在运行时,可用的最佳模型 GPT-5.4 和 Opus 4.6 分别只取得了 11% 和 17% 的严格通过率。

在 SlopCodeBench 上测试 Opus 5
上周五,我在 SlopCodeBench 的一个子集上运行了三个 Claude 模型(Opus 4.8、Sonnet 5 和 Opus 5),并实时观看了六个小时。Opus 5 在技术上胜出,但在我看来,它们都没有做得很好。稍后将发布更多结果,其中还会包括 Fable 和 5.6 Sol。

最大的新闻是,在我运行的基准小规模子集上,Opus 5 获得了 24% 的严格通过率——比原始论文中 Opus 4.6 的 17% 严格通过率高不了多少。所有测试模型在挑战过程中,在冗长度、复杂性以及许多其他代码坏味指标上都显示出显著增长,其中 Opus 5 在相同挑战集上写出的函数/可调用对象数量是 Opus 4.8 的五倍。

我个人对这个 23% 通过率的解读是,SlopCodeBench 提供了信号,证实了我在第一部分中的感觉——对于真实的软件工程工作,一次只构建一个问题,今天的模型在没有引导的情况下无法可靠地实现无人值守运行。
基准子集
我让 Claude 从仓库中挑选了 3 个问题,总共 17 个检查点,混合了标记为容易/中等/困难的问题:
- circuit_eval — 容易(8 个检查点)
- database_migration — 中等(5 个检查点)
- dynamic_config_service_api — 困难(4 个检查点)
文末有一个附录,详细解释了所有 17 个检查点,但这里我不全部列出。
然后,我在所有三个模型上并行运行它们,每个检查点使用全新的上下文窗口。所有模型都收到了相同的提示,并在 Claude Code 框架中运行。
我决定关注的指标是严格通过:所有新内容都是绿色的,包括从之前检查点继承的所有回归测试。
如果模型的解决方案有缺陷,则检查点失败——缺陷通过以下方式检测:获取模型的输出(一个 CLI 用于运行,或者在某些情况下例如一个 API 服务器用于探测),然后针对生成的入口点运行一组保留的黑盒测试。
模型为 ck1 编写代码
评估框架对 ck1 运行黑盒测试
模型为 ck2 编写代码
评估对 ck1 和 ck2 运行黑盒测试
等等
再次强调,严格通过标准意味着,如果模型在检查点 4 搞砸了某事,它就无法通过后续检查点,因为那部分失败的代码会延续下去(除非模型在检查点 6 无意中修复了检查点 4 中损坏的评估用例,但我们在实践中没有看到这种情况发生)。
在所有 9 次测试运行中,没有任何模型在任何一个挑战中完成所有检查点且全部通过,即使在标记为“容易”难度的问题上也是如此。
运行期间
Sonnet 的第一个检查点更昂贵,但到问题 1 结束时,Sonnet 成为了三个模型中最便宜的。(似乎一旦基础构建完成,工作转变为维护,成本节省就开始显现。)
对于第一个挑战,上一代模型的缺陷稳步累积,Opus 5 在检查点 4 和 5 各出现了一个缺陷。

在最初的两个小时里,Opus 5 是唯一一个有任何严格通过的模型——连续三个通过。

随着进展,情况发生了变化。Claude 勤奋地更新了 HTML。

与其他模型相比,Opus 5 在问题 1(circuit_eval)上技术上更好。但在出色完成前三个检查点后,每个后续解决方案都至少有一个缺陷(失败的测试用例)。
最终结果
如果我们对成功的定义是“到达最终检查点且没有缺陷”,那么 Opus 5 在所有三个问题上都失败了,但比其它模型失败得稍好一些。

关于成本与缺陷报告,我真的很讨厌 Claude 式的语言,但这一条我决定保留:
每一美元都买到了正确性。但没人买够。
(显然,这个基准的小规模子集不能确切告诉我们花更多钱一定会带来更高的通过率。)

就严格通过而言,Opus 5 获得了四个(24% 通过率)(circuit_eval 的前三个检查点,加上 database_migration 的 ck1)。
Opus 4.8 和 Sonnet 5 都获得了一个严格通过(6% 通过率),即 Opus 5 也通过的同一个 database_migration ck1。
因此,获胜者通过了 17 个中的 4 个,其中 3 个是某个问题的开头检查点。看来我们为下一波模型准备了一个尚未饱和的基准。干得好,@GOrlanski 和团队。
坏味测量器
我还没有完全相信“通过 lint 消除坏味”,因为我认为目前还无法确定性地解析特定代码库检查点的“可维护性”。但代码质量指标值得关注,而且它们的方向很可能是正确的。
使用 SlopCodeBench,你可以在每个检查点之后获得各种质量指标的结果。结果文件中有 41 个指标。大致分组如下:
- 大小——源代码行数、文件数、函数数、方法数、类数、语句数,以及在该检查点添加和删除的行数
- 复杂度——圈复杂度的平均值、最大值和分布,落在“高”和“极端”区间的函数数量,复杂度的集中程度,最大嵌套深度,以及平均函数长度
- 重复——克隆行数,以及克隆行占源代码的比例
- 分解——单次使用函数、无关包装器、未使用变量、每符号行数
- 规则违规——lint 错误数量以及可自动修复的数量,针对测试坏味规则的 ast-grep 命中次数,以及标记为冗长的行数比例
- 依赖图——传播成本(变更波及多远)、循环依赖质量、依赖熵(可能是我最感兴趣的一个)
每个指标都是在每个检查点之后,使用当前代码状态确定性计算的。
下图显示了 circuit_eval 挑战中,各模型在 ck1 得分与 ck8 得分之间的差异。(也就是说,在挑战检查点的生命周期内,坏味指标增加了多少。)最有趣的是,大多数指标无法区分不同的模型。

我喜欢这些度量是可重复的,并且不使用模型进行判断。但其中任何一个指标与“这个代码库是否易于修改和演化”之间的联系尚未建立。
更高的正确性是以多得多的代码为代价的

但其中很多是“更多的测试”——实际的生产代码量,Opus 5 大约是 Opus 4.8 的 1.8 倍。

我的猜测是……这里的昂贵冗长并没有直接转化为更好的结果。
需要进一步深入研究,才能知道这是模型的一个固有信号,还是仅仅因为“这确实是一个非常困难的问题,需要这么多代码”。
几乎所有编写的代码都触发了坏味测量器
对于所有模型,绝大多数代码行至少触发了基准坏味规则中的一条。三个问题的平均值:
- opus 4.8 — 98%
- opus 5 — 93%
- sonnet 5 — 89%
具体来说,被标记为过于冗长的行数,在每个模型的轨迹中都在上升,从 ck1 的大约 65% 到 ck8 的 80%,即使对于 Opus 5 也是如此。
我实际上可能会说,这表明某些代码质量指标有点过于激进。我尝试将规则集应用于我们的 TypeScript monorepo,但目前的 slop-code-bench 检测器仅适用于 Python。
所以我让 5.6-Sol 为 TypeScript 制作了一个子集规则,但它只产生了 76 个坏味检测器(而 SCB Python 库有 200 多个)——但发现了一些方向性的结果:Opus 5 无人值守生成的解决方案中,每 kLOC 触发的坏味次数比我们那个 99% AI 生成但也经过仔细审查的 TypeScript monorepo 高出 11 倍以上(是的,增加了 1000%)。

显然,这个发现存在大量前提条件(规则更少,尚未审查等价性等),但至少可以说很有趣。
这些模型写了很多函数
另一个有趣的数据点——Opus 5 写出的函数数量是其他两个模型的 5 倍。但 Opus 4.8 写出的单次使用函数比例更高(其近 50% 的函数只被调用了一次)。而 Sonnet 5 的单次使用函数比例最高,达到 71.5%。

顺便说一句,我不认为很多小函数不好。我现在对此持保留态度,但我曾经是一个坚定的 Clean Code 拥护者。小的描述性函数名远好于大量注释,等等等等。
所有模型的复杂度都随时间增长
大约一年来,我一直在说模型会随着时间的推移降低代码库质量,主要是凭感觉。但现在我们有了数据。

没有一个模型能在所有挑战中不增加跨检查点的复杂度。虽然 Opus 5 的平均复杂度最低,但它写了 2000 个函数。这里有一个权衡:很多小函数还是少量大函数。我认为这些复杂度指标中任何一个都不能单独使用,但它们提供了某种复合信号。

Sonnet 和 Opus 4.8 都通过使单个函数变大来应对挑战检查点复杂度的增加,而不是重新组织代码。Opus 4.8 最为极端,在八个检查点中上升了 70%,其单个最差函数最终圈复杂度达到了 93。
重复是一个分水岭。Opus 4.8 从 4.6% 上升到 16.8%,在 ck3 处出现拐点——大致是当新需求开始与初始设计产生冲突时。
顺便提一下,以下是 circuit_eval 前三个检查点的要求(所有挑战的完整列表见文末附录):
- ck1 — 一个带有 --help、--version、JSON 输出模式以及解析和验证 .circ 电路文件的 check 命令的 CLI。每个信号都是单个比特。
- ck2 — 一个 eval 命令:向电路输入一些值,得到输出。仍然是每信号单比特,标准布尔运算符。
- ck3 — 信号变成向量。data[7:0] 代替 data,加上切片、索引、拼接、新运算符以及每个操作数的宽度检查。
到最后,每六行中就有一行是另一行的副本。其他两个模型在同一阶段有所下降。
然而,Opus 5 基本持平,从 2.41 到 2.64。在第一部分中,我有一张图表假设“随着时间的推移改善代码库质量”在模型代际之间没有太大变化。因此,如果你相信重复率是一个黄金指标,你可以认为在过去大约 3 个月里,我们确实取得了渐进式的改进。但这是个很大的假设,我认为大多数软件架构专家都会同意这不是非黑即白的问题。
更好的软件质量预言机的形态
虽然代码质量指标很有趣,但我认为它们并不能说明全部问题,而且模型很容易通过 hack 来迎合其中任何一个指标。就像 SWE-bench 形状的问题曾是“一次性解决软件问题”的最佳验证器(因为它们映射到那个“缩放级别”的真实世界工作),我认为“通过逐步披露规范的所有验证器”是衡量“模型能否长期维护代码库”的一个非常现实的评估标准。
也就是说,如果代码库变得难以维护,后续阶段的检查点就会失败,因此较高的严格通过率表明模型善于构建可维护的代码库。
随着像 Fable / Sol 这样的前沿模型被证明是专家级的调试器和反向工程师,将成本/时间/Token 指标纳入考量可能会随着时间的推移变得更加重要——像 Fable 和 Sol 这样的前沿模型可能能够在最糟糕的代码库中完成任务,但我敢说,一个结构良好的代码库往往会使得未来问题的解决更简短、更节省 Token。
虽然“在 8 个检查点中构建整个功能”比“解决一个 15 分钟的 SWE-bench 多语言问题”慢得多,但它可以无人值守执行,并且最终接受确定性行为验证器的检验。因此,在我看来,它是一个比“另一个模型认为这段代码是否干净”等更好的预言机。
我认为,从一个真正擅长维护代码库的模型中,我们希望获得的更好的信号是,我们可以尝试让像 opus 5、fable 5 或 gpt-5.6-sol 这样的前沿模型编写前 N 个检查点,然后看看像 sonnet 5 或 gpt-5.6-terra 这样的较笨模型是否能够实现检查点 N+1。

这放大了信号:聪明的模型是否在维护高质量、易于更改的代码方面做得很好。像 Sonnet、Terra 甚至 Haiku 这样的小模型能否实现检查点 8,会影响聪明模型在检查点 1-7 上的得分。
为什么软件工厂会失败:你可以衡量的东西
我个人的解读是,SlopCodeBench 提供了信号,证实了第一部分中的一些感觉——对于真实的软件工程工作,一次只构建一个问题,今天的模型在没有引导的情况下无法可靠地实现无人值守运行。
SCB 是我将密切关注的一个未来度量指标——我在第一部分说过,我不会把我的代码库押在 Frontier Code、SWE-Marathon 或 DeepSWE 上,但如果模型能够在像 SlopCodeBench 这样衡量随时间迭代的(良好保留的)基准上获得 80% 以上的分数,我会感觉好多了,可以放心让它们无人值守运行。
我不会断言这何时会发生,因为“何时”不如拥有一个好的信号来知道它正在发生重要。(假设在此期间没有人“意外”地在测试集上训练。)
下一步 / 我会做得不同的事情
我将更深入地阅读一些 slopcodebench 问题以获取灵感,并挑选一些我认为能很好地映射到我们在 @humanlayer_dev 日常构建工作的问题。
Claude 决定按模型进行并行化,依次运行三个挑战。我们本可以轻松地以 9 个并行会话运行 3 个模型 x 3 个挑战,并在 1-2 小时内完成,而不是 6 小时。
正如我所说,我尝试将规则集应用于我们的 TypeScript monorepo,但目前的 slop-code-bench 检测器仅适用于 Python。将这些检测器移植到 TS 和其他几种语言会很有趣。我讨厌成为那样的人,但我敢打赌 Python 是比大多数语言更容易产生坏味的语言。
我认为与其专注于严格通过和总缺陷数,探索基准的更多维度将会很有趣。在当前评分中,我们将沿途的任何失败视为累积缺陷——除非模型在未来的会话中恰好解决了过去的缺陷,否则所有后续检查点都无法通过。
许多软件工厂包括提示以获得更好的风格,在代码循环中包含针对复杂性和许多其他软件质量指标的确定性反馈。今天的结果没有评估在这些防护措施下的模型代码质量或成功率。我们使用了 SlopCodeBench 的“仅解决”版本提示,但也有其他变体,比如在提示中包含关于质量/重复的说明。重新运行整个评估,并加入 1) 一个由模型判断质量的“对抗性审查”循环和/或 2) 针对圈复杂度等指标的代码质量反向压力,将会非常有趣。
我既没有无限的资金也没有无限的时间,但在这里使用更大的数据集会很有趣。
当然,最有趣的是这个想法:“能否通过将 fable 的代码库交给像 sonnet 这样更小的模型来放大坏味信号”。
感受检查——前沿仍然很蠢
在整个实验进行的同时,在另一个会话中,opus 5 决定擅自行动,用新格式重写一封电子邮件草稿,然后未经与我确认就发给了 100 个人。糟糕透了。
用户很生气,因为我犯了一个严重的错误:我用最终版本覆盖了他们编辑的草稿,然后将其发送了出去。
如果你收到了一封带有难看页眉横幅的 HumanLayer 产品更新邮件,我很抱歉(我认为 Claude 对此的新说法是“kicker”??)
朋友们,我真的感受到了这里的通用人工智能
🫡 -dex
附注:我们仍然对此着迷
我们正在构建 humanlayer.com,一个 Agentic IDE 和协作平台,帮助你以人工智能的速度前进,同时保持人类(或非常接近人类)水平的代码质量。
我们正在朝着两个想法努力:“软件工厂的构建模块”和“软件可维护性的更好验证器”(甚至可能是更好的模型)。
HumanLayer 对最多 3 人的小团队免费,如果你需要入门帮助,可以加入我们的 discord 或给我们发邮件至 founders@humanlayer.dev。
本文链接
- 为什么软件工厂会失败,第一部分:脚手架不够
- 为什么软件工厂会失败,第二部分:重新开灯
- SlopCodeBench(论文)
- SlopCodeBench 运行器(SprocketLab/slop-code-bench)
- SlopCodeBench 问题(gabeorlanski/scb-problems)
- Gabe Orlanski 在 X 上
- SWE-bench Multilingual(数据集)
- SWE-Marathon(Abundant AI)
- DeepSWE(Datacurve)
- Frontier Code(Cognition)
- 变异测试(Wikipedia)
- HumanLayer
- HumanLayer discord
附录:挑战检查点
所有 17 个,按顺序,直接取自提供给模型的提示。每个都是冷启动——模型不知道存在任何后续检查点。
circuit_eval — 容易,模拟,8 个检查点
- ck1 — 一个带有 --help、--version、JSON 输出模式以及解析和验证 .circ 电路文件的 check 命令的 CLI。每个信号都是单个比特。
- ck2 — 一个 eval 命令:向电路输入一些值,得到输出。仍然是每信号单比特,标准布尔运算符。
- ck3 — 信号变成向量。data[7:0] 代替 data,加上切片、索引、拼接、新运算符(MUX、归约、EQ)、每个操作数的宽度检查,以及 --radix 输出格式。
- ck4 — 三值逻辑。输入现在可以是 X(未知),每个运算符必须说明如何处理 X。
- ck5 — 另外两种输入格式。check 和 eval 现在可以读取 .json 和 .bench 文件以及 .circ,通过 --format 标志。
- ck6 — 三个分析命令:stats 用于度量,lint 用于警告,dot 用于 Graphviz 导出。所有命令都支持所有三种格式。
- ck7 — cone(提取子电路)、truth-table(枚举每个输出)、equiv(检查两个电路是否匹配),加上一个 --seed 标志用于可重现的随机性。
- ck8 — opt:一个带有可配置遍、确定性输出、可选等价验证以及 BENCH 导出的电路优化器。
database_migration — 中等,数据库,5 个检查点
- ck1 — 一个 CLI,从 JSON 文件中读取迁移规范并将其应用于 SQLite 数据库:创建表、添加列、改变表结构。
- ck2 — 数据迁移。使用 SQL 表达式转换已有的行,而不仅仅是转换围绕它们的模式。
- ck3 — 外键、自定义索引和高级约束。关系完整性和查询性能。
- ck4 — 回滚。逐个或批量撤销迁移,带有依赖处理。
- ck5 — 依赖管理。迁移声明 depends_on,工具必须解析顺序并检测循环依赖。
dynamic_config_service_api — 困难,系统设计,4 个检查点
- ck1 — 一个 REST 服务,存储带有不可变版本、作用域、回滚到任何早期版本以及跨配置的导入/继承的 JSON 配置对象。
- ck2 — 一个拥有自己版本控制的模式注册表、绑定到配置的模式、创建和解析时的验证,以及将原始 YAML/TOML/JSON 解析为内部规范 JSON。
- ck3 — 一个变更管理工作流。每个新版本以草稿开始,提案收集人工审核,激活需要法定人数,每个提案携带一个确定性差异。
- ck4 — 一个组织级护栏层,针对已解析配置及其周围图运行策略包,阻止不安全的提案,并提供与模式错误不同的违规详情。
- 原文链接: x.com/dexhorthy/status/2...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~