AI / Programming Languages
如果编程已经被解决,接下来呢?衡量代码的“草率度”
LLM 能生成近乎正确的代码,却仍会制造重复、冗余和逐步累积的糟糕决策。本文比较 AI 评审、人工评审、代码行数、冗余度与侵蚀度等衡量方法,并结合 SlopCodeBench 解释为什么人的直觉与品味依然不可替代。
LLMLLMLarge Language Model通过大规模文本训练,能够理解和生成自然语言、代码或结构化输出的神经网络模型。查看详细解释 → 生成代码的能力已经接近完美,但故事并没有就此结束。代码在形式上正确,不代表它没有引入不必要的抽象、制造重复,或整体上作出糟糕的决策。这并不是什么惊人的新发现:多数用 vibe codingVibe Coding主要用自然语言驱动 AI 生成和修改代码,并弱化逐行理解过程的一种开发方式。查看详细解释 → 做过项目的人都会注意到,每增加一个功能,有时都会带来代码行数(LOC)的爆炸式增长。
结果是人的能动性逐渐丧失:当项目每个月新增数百万行代码时,人类很难跟上。¹ 有些人可能认为这根本不是问题,因为他们相信 智能体AI AgentArtificial Intelligence Agent能围绕目标规划步骤、调用工具、观察结果并继续行动的 AI 系统。查看详细解释 → 能处理这些代码。坏消息是,智能体其实也处理不了这些“草率代码”。
我有物理学背景,解决问题时一向偏好实验和定量方法。刚加入 Earendil、开始研究如何衡量代码的草率程度时,我的本能反应是先深入查阅文献,再看看其他公司在做什么。
坦率地说,除少数有洞见的研究论文外,我对行业目前这种过于依赖“感觉”的状态感到失望。研究过程中以及在 X 上,我不断看到“端到端编程智能体”“AI 不只是建议代码——它会直接交付”“无需人类级成本的人类级评估”等说法。和所有讲得动听的故事一样,它们确实包含一部分事实。
LLM 能写出几乎完全正确的代码,原因在于代码既具备可扩展性,也具备可验证性。让 LLM 生成代码,再用隐藏测试检查,便能得到清晰的奖励信号。与此形成鲜明对比的是,判断代码是否“草率”往往需要人的直觉和品味,本身就是一项极其困难的任务。理解这一点的最好方式,是逐一考察几种可能的衡量方法。
谁来判断代码质量
让 AI 担任裁判
这可能是行业里最常见的 代码质量评估AI EvaluationArtificial Intelligence Evaluation用任务、数据和明确判定标准,系统衡量 AI 模型或智能体行为的方法。查看详细解释 → 方法,而据我的观察,它很少奏效。最朴素的做法是让模型按 1 到 10 分评价代码质量,这基本等同于随机数生成器。
更复杂的做法是给裁判模型两个方案 A 和 B,让它选择更偏好的方案;但只要重命名这两个方案,模型就可能改变偏好。我这里多少有些故意说得刻薄,而且这种效应在更大的模型上没有那么明显,但核心问题依然存在:让 LLM 评判自己写出的代码,并不能替代严谨的评估。使用评分细则,或让 LLM 编写测试,确实是一些有趣的方向,但距离真正消除草率代码仍然很远。
由人评审 AI
如果暂且忽略软件工程师水平差异巨大的事实,这会是确保代码仍然适合人类阅读的最佳方案。缺点是,无论用它来训练 AI,还是构建同时覆盖多家模型提供商和智能体载体的大型基准测试,都无法规模化。²
最简单的方法:代码行数变化
在我的研究和测试中,直接观察代码行数的变化,竟然是一项相当有效的草率度指标。讽刺的是,一旦我们开始针对这个指标进行优化,它就会因为古德哈特定律而失去衡量意义。
接下来的两个指标来自论文 SlopCodeBench。它们能够很好地区分遗留代码库与 LLM 产生的草率代码,因此看起来颇有希望。
冗余度:重复和不必要代码的占比
冗余度(Verbosity)试图衡量重复代码和不必要的冗长代码行所占的比例。³
这里的分子是被 AST-Grep 规则标记的代码行与克隆代码行的并集大小,分母是代码总行数。
侵蚀度:质量是否集中在少数复杂函数中
侵蚀度(Erosion)试图衡量代码库的“质量”有多大比例集中在少数庞大而复杂的函数里。首先为每个函数定义质量:
其中,f 表示函数,SLOC 是源代码行数,CC(f) 是函数的圈复杂度。随后定义:
也就是说,侵蚀度就是圈复杂度大于 10 的函数质量之和,占所有函数质量总和的比例。
智能体代码与成熟代码库的差距
将 SlopCodeBench 评估中生成代码的平均冗余度和侵蚀度,与一组成熟代码仓库比较,会看到非常明显的差异:
| 指标 | 成熟代码仓库 | 智能体生成代码 |
|---|---|---|
| 冗余度 | 0.15 ± 0.06 | 0.33 ± 0.10 |
| 侵蚀度 | 0.31 ± 0.17 | 0.68 ± 0.20 |
智能体代码的冗余和侵蚀程度,平均约为人类代码的两倍。随后我又调查了自己的一些 vibe-coded 项目,其中许多项目的冗余度高达 0.4,侵蚀度高达 0.75。因此,这些结果可能不只是评估设置制造出来的假象。⁴
为什么智能体无法自行收拾残局
要回到“为什么智能体无法真正自行处理草率代码”这个问题,就需要看 SlopCodeBench 的评估方式。
其他编程基准通常会在开始时向智能体提供一份完整指令,再用一组隐藏测试判断程序是否通过。SlopCodeBench 反其道而行之:它设置多轮指令与测试迭代,并在各个检查点之间清除模型的上下文。这更接近人类实际使用编程智能体时的迭代过程。
结果是,糟糕的编程决策会随着时间不断累积。在要求所有检查点的全部测试都通过的严格解题率下,即使最先进的模型也只有 0% 的通过率。⁵ 对那些乐于每天增加数万、甚至数十万行代码的人而言,这应该是一个警告。当然,这里仍然存在常见的保留条件,例如测试可能过于严格,个别题目描述可能略有歧义;但总体趋势依然成立。
通过考察这些指标,我希望你已经更清楚地看到:评估代码的草率程度为何如此困难,以及人的直觉和品味为什么仍会以显式或隐式的方式嵌入评估过程。
还有一些颇有希望的方向值得继续探索,例如函数之间的耦合度、代码流失率(code churn)与内聚性等。如果你也在从事评估工作并希望交流,可以联系 sebastian@earendil.com。
注释
- 这让我想起一句话:“用代码行数衡量编程进度,就像用重量衡量飞机制造进度。”
- 我可不想为了得到一个不断变化的模型提供商排名,而逼迫任何人评审数百万行代码。
- 这一指标使用一组手工编写的启发式规则,并通过 AST-Grep 实现;这再次说明人的因素始终存在。
- 不过,一个以 vibe coding 著称的开放项目在这两个指标上的得分都不算高。原因可能是函数之间的耦合度很高,也可能只是大量彼此无关的函数拉低了平均值。
- 尚未在 Fable 5.1 或 Astra 上测试,但已经在 GPT 5.6 sol xhigh 等模型上测试过。