新浪财经 股票

AI改代码时,一次性重写整个文件,还是一小段一小段地打补丁?

市场资讯 10.14 17:07

(来源:科技行者)

你有没有想过,AI帮你改代码的时候,它到底应该怎么"动手"?

现在几乎所有的AI编程工具,比如Aider这类产品,都默认选择了一种听起来特别合理的方式:让AI只输出改动的那一小块,就像你在Word里用"查找替换"一样,找到某段文字,换成新的文字,其他地方一个字都不动。这种方式在业内有个说法,叫diff式生成

diff式生成:AI不重写整个文件,只输出一小段一小段的"查找-替换"指令,系统再把这些指令自动应用到原文件上,最终拼出修改后的完整代码。

这个思路乍一看无懈可击。改动小,输出的字数就少,速度快、成本低。而且这也更接近人类工程师review代码的习惯,你审查一个Pull Request的时候,看的从来不是整个项目重新写了一遍,而是一份diff,清清楚楚地标出哪几行加了、哪几行删了。

但有一篇最新的研究,用扎扎实实的实验数据告诉我们:这个"显然正确"的直觉,在很多场景下是错的。

这篇论文的作者训练了两个AI模型,一个是从零开始训练的1亿参数小模型,叫Rainbow-Pony-100M,另一个是基于阿里通义千问团队开源的Qwen2.5-Coder-0.5B微调而来。每个模型都分别用两种方式训练:一种是直接生成完整文件(direct模式),另一种是像人类一样一步步打补丁(steps模式)。然后把这四个模型都拿到同一批将近1800道Flutter/Dart编程题上考试,结果发现:无论是哪个模型架构,直接生成完整文件的方式,在几乎所有评价指标上都碾压了打补丁的方式。

这个结果有意思在哪?它不是说"AI暂时还不擅长打补丁",而是揪出了一个更深层的、跟"要改的东西离得远不远"有关的规律。我们一步步来看。

**当时的行业共识是什么,这篇论文在挑战什么**

在这篇论文出现之前,业界的默契基本是:diff式编辑是更专业、更高效的做法。

Aider项目自己做的基准测试就发现,diff式的输出格式确实能省下不少token(也就是AI处理文字的最小单位,可以粗略理解成"字数"),但代价是格式错误率上升——有些AI生成的"补丁",系统根本没法应用到原文件上,尤其是那些能力较弱的模型。另一篇叫LintSeq的研究则从另一个角度支持了"分步编辑"的价值:如果把一段完整代码拆解成一串一步步、每步都不出错的小修改去训练模型,反而能提升模型最终生成代码的质量。这篇研究做的实验对象是"从零写代码"(code synthesis),不是"改已有代码"。

这就是这篇新论文想要厘清的地方:分步编辑训练,到底是提升代码能力的"更好的学习方式",还是仅仅是"看起来更省资源的接口选择"?这两者听起来像是同一件事,其实完全不同。前者说的是训练模型时用什么方式让它学得更好,后者说的是模型学好之后,在实际使用时用什么格式输出更省钱。论文的作者决定不引用别人的实验数据,而是自己动手,在完全同一套训练数据、同一套打分标准之下,把两种方式都跑一遍,看到底谁赢。

**核心实验:直接生成 vs 一步步打补丁,差距有多大**

先看最硬的数据。评价代码好不好,最基本的一条线是能不能通过Dart语言的静态检查(dart analyze),这是判断代码语法和结构对不对的工具,相当于代码能不能顺利跑起来的第一道门槛。

结果是,小模型Rainbow-Pony用直接生成方式,通过率是80.2%,而用打补丁方式,通过率只有34.7%,足足差了45.5个百分点。换个角度理解这个数字:每10道题里,直接生成能对8道,打补丁只能对3.5道,差了将近一半的正确率。

用了千问底座、参数量更大的Qwen模型也没能逃过这个规律。直接生成的通过率是90.0%,打补丁只有50.1%,差了39.9个百分点。也就是说,哪怕换了一个见过更多代码、能力更强的模型底座,这个"打补丁不如整篇重写"的现象依然稳稳存在。这排除了一种可能的解释:是不是小模型太笨,还没学会怎么打补丁?答案是不是,因为连能力更强的Qwen也一样输。

除了能不能编译通过,论文还测了另外两个指标。一个叫bits per byte,可以理解成AI对"标准答案"的预测准确度,数值越低说明AI越"懂"这段代码应该长什么样;另一个叫字符相似度,就是看AI写出来的代码和标准答案长得有多像。这两项打补丁模式全部落后。

这里要提一个特别有意思的反常识细节:字符相似度这一项,打补丁模式其实在33%到35%的题目里"赢了"直接生成模式,单看这个数字,你可能觉得打补丁模式有优势。但作者很坦诚地指出,这是个假象。因为打补丁模式的生成过程本身就是从原始代码一点点改出来的,标准答案也是这么改出来的,两者在字面上天然就会长得比较像,跟改得对不对没关系。这就好比两个人抄同一份作业,字迹相似度肯定高,但不代表两份答案都是对的。作者很老实地把这个指标标注为"metric artifact"(指标产生的假象),没有藏着掖着说这是个优势,这是这篇论文让我比较信服的地方,它没有为了让研究结果看起来更漂亮而回避不利于自己叙事的细节。

**打补丁为什么会输?不是因为它"跑崩了",而是悄悄"跑偏了"**

如果你觉得打补丁模式输,是因为AI经常改到一半改不动、或者输出了系统读不懂的格式,那你猜错了方向。

论文详细拆解了打补丁模式失败的原因。结果显示,81%到85%的编辑过程是正常走完的,模型自己判断"改完了",主动停止,不是被逼着停下来的。也就是说,绝大多数失败案例,不是过程出了故障,而是模型认认真真、按部就班地走完了整个流程,最后交出来的答案却是错的。作者管这个叫"静默的内容腐化"(silent content corruption),这个说法挺形象的:表面上一切正常,流程走得漂漂亮亮,底下代码的逻辑却已经悄悄坏掉了。

这背后有一个具体的技术漏洞。打补丁的核心操作是"搜索-替换":在原文件里找一段文字(search),把它换成新文字(replace)。但如果那段"要找的文字"在文件里出现了不止一次怎么办?系统没法确定该改哪一处,只能靠一个简单粗暴的规则:默认改第一个出现的地方。

这个设计选择的代价在数据里体现得非常清楚。凡是遇到过至少一次这种"靠猜"的编辑步骤的整条编辑轨迹,最终通过率只有12%到18%,而完全没遇到这种模糊情况的轨迹,通过率能到57%到80%,足足差了四五倍。

这就好比你请一个装修工,告诉他"把客厅的那个灯泡换掉",可是客厅刚好有三个一模一样的灯泡,装修工没多想,直接换了离门最近的那一个。运气好,那正好是坏的那个,没事。运气不好,换错了,而且他自己压根不知道换错了,因为他只是机械地执行了"换第一个碰到的",没有回头核实过。如果不这样"猜",而是碰到有歧义就直接拒绝执行、报错让人来判断,那么这部分风险确实能规避,但代价是会有更多任务因为"过于谨慎"而彻底做不完。这就是为什么作者管这是个"启发式方法"(heuristic),而不是一个"靠谱的解法"(principled fix)——它是权衡之后选的一个折中方案,不是最优解。

而这种搜索目标模糊的问题,恰恰是随着编辑步数增加而越来越容易发生的。步数越多,可能碰到重复文本的次数也越多,出错的机会就越多,错误还会在后续步骤里累积下去,一步错步步错。

**排除"任务难度"这个干扰项:是打补丁本身弱,还是它专挑软柿子捏**

这时候有一个合理的质疑冒出来了:打补丁模式表现差,是不是因为它经常在更难的题目上翻车,而直接生成模式凭运气分到了更简单的题目?

论文用了一个很扎实的办法来排除这种可能性,叫matched-ID比较(按相同题目编号做匹配的对照实验)。

具体做法是:先把打补丁模式里那些"完美走完全程"的编辑轨迹挑出来,标准是必须正常结束、没用过前面说的"猜第一个"的补救办法、而且没超过步数上限。这批被称为clean(干净)的轨迹,理论上是打补丁模式发挥得最好的那一批样本。

结果发现两件事。第一,这批"干净"的题目,对直接生成模式来说,通过率也比它在全部题目上的平均通过率更高,这说明这批题目本身确实偏简单,直接生成模式在这些题上"躺赢"的成分更高。第二,也是更重要的一点,即便把这个"挑软柿子"的偏差校正掉,打补丁模式和直接生成模式之间的差距反而变大了,不是变小了。以小模型为例,校正后的差距从原来的45.5个百分点扩大到了将近25个百分点(在同一批"干净"样本里比较),而Qwen模型上的差距在校正后也依然有12.4个百分点。

换句话说,打补丁模式即便在它自己表现最好的那批题目里,拿去跟直接生成模式在同一批题目上一对一比,依然是输的。这不是运气不好分到难题,这是能力本身有差距。

**用AI裁判做二次验证:代码能跑不代表代码写得好**

静态检查能告诉你代码语法对不对,但没法告诉你代码写得优不优雅、有没有真正解决用户提出的需求。为了验证这一点,作者又请了一个AI裁判来做盲测打分。

这里的"盲测"是认真的:裁判模型看到的只有任务说明、原始代码、和一份要打分的候选答案,完全不知道这份答案出自哪个模型、用的哪种模式、甚至连"这份代码能不能编译通过"这个信息都不告诉它。裁判从三个维度打分,分别是目标达成度、正确性和代码质量,每项1到5分。这个裁判模型用的是gpt-4.1,几乎覆盖了全部将近1800道题目,一共打了7161条评分记录。

结果依然是直接生成模式碾压。而且这次的比较对象,是那些两种模式都成功通过了静态检查的代码,也就是抛开"能不能跑"这个因素之后,单纯比"写得好不好"。三个维度、两个模型架构,六组对比全部呈现统计学意义上显著的差距(用统计学里的Welch's t检验,p值都小于0.001,可以理解成这个差距几乎不可能是随机波动造成的)。

这个结果说明什么?说明打补丁模式和直接生成模式之间的差距,大约一半来自"打补丁模式更容易写出编译不通过的代码",另一半则是即便两边都能编译通过,打补丁模式写出来的代码在裁判看来质量也更差。这是一个挺重要的区分,因为它意味着单纯优化"怎么让打补丁的代码更容易编译成功"是解决不了全部问题的,深层的质量差距依然存在。

**打补丁模式真的一无是处吗?答案藏在"改动范围"里**

如果打补丁模式全面落后,那这篇论文为什么还要花大量篇幅去分析"打补丁模式什么时候赢"?

因为作者发现了一个非常一致、跨越两种模型架构都能复现的规律。打补丁模式确实有它能打赢的场景,而这个场景不是随机分布的,它精准地集中在编辑步数少的短任务上。

论文按任务类别做了统计,发现在9大类任务里,有两类任务是打补丁模式明显更容易赢的:一类是重构类编辑(refactoring_edits),一类是错误处理和边界情况修复(error_handling_and_edge_cases)。这两类任务在打补丁模式的"赢局"里出现的比例,明显高于它们在整个数据集里本应占的份额(理论上应该是11.2%左右)。

更关键的发现出现在下一步分析里。作者去查这两类任务需要多少编辑步数才能完成,结果发现——重构类任务平均只需要4步左右,错误处理类任务平均需要7步左右,这两类恰恰是9个任务类别里步数最少的两类,在两个模型架构上都是这样。

这不是巧合。作者进一步验证了这个规律:凡是打补丁模式在裁判打分上"赢过"直接生成模式的样本,它们平均需要的编辑步数明显更短(小模型上是5.5步,对比"输"的样本平均要8.9步;Qwen上是6.65步对9.02步),而且这个差距在统计学上是显著的。随着编辑步数从1步一路涨到20步,打补丁模式的胜率呈现一路下滑的趋势,从最开始的10%到18%,一路跌到接近0%。

这就好比找一个学徒去修一台机器。如果问题很明确、很局部,比如"把这颗螺丝换掉",学徒直接照着说明动手就行,出错空间很小。但如果任务是"重新设计整套传动系统",学徒需要拆掉一部分、装回一部分、再调整另一部分,每一步的判断都建立在上一步的结果之上,一旦某一步理解错了,后面所有步骤都会跟着走偏,而学徒自己可能压根意识不到哪里开始不对了。这就是为什么步数越长,打补丁模式越吃亏:每一步都是一次新的赌博,赌注还会累积。

作者管这个规律叫任务局部性(task locality),意思是一个编辑任务在空间上有多"局部"、多"自成一体"。这个概念把前面看起来是两条独立线索的发现,重构/错误处理这两类任务打补丁模式更有优势,和短编辑轨迹打补丁模式更有优势,合并成了同一件事的两种表现。任务类别本身不是决定性因素,真正决定打补丁模式能不能打赢的,是这个任务需要改动的范围有多小、有多集中。

**这篇论文和别的研究是什么关系**

这里有个特别值得琢磨的地方。前面提到的LintSeq研究得出的结论,是"分步编辑训练能提升代码生成质量",而这篇论文的结论听起来正好相反。这两个结论到底谁对谁错?

作者给出的解释是:这两项研究做的根本不是同一件事。LintSeq研究的场景是从空白开始写一段全新的代码,每一步编辑在训练数据构造阶段都经过了lint工具(代码风格检查工具)的验证,保证每一步都是干净可用的,相当于是在搭积木,一块一块往上垫,新的一块搭上去之前经过了检验。而这篇论文研究的场景是改动一份已经写好、已经能正常工作的代码,这时候每一步编辑都有可能不小心破坏掉原本运行良好的部分,而且没有额外的检验机制去确认这一步改得对不对。

这就好比装修一栋新房子和翻修一栋已经有人住的老房子。造新房子的时候,你可以一层一层往上盖,每盖完一层验收一次再盖下一层,过程本身天然带着质检环节。但翻修一栋正在住人的老房子,你动一根管子可能就影响了另一个房间的电路,而这种连带影响往往不会立刻显现出来,直到某天突然出了问题你才发现。这两种"分步骥"的工程,风险结构完全不一样。

论文也提到另一篇叫Cheng et al.(2026)的研究,主张diff格式和整篇重写格式不该是二选一的,而应该按具体的编辑内容自适应地选择用哪种方式。这篇论文的任务局部性发现恰好独立地支持了这个观点:局部小改动适合打补丁,大范围结构性改动适合整篇重写,两者不是竞争关系,而是分别适用于同一根标尺的两端。

**这篇研究还有哪些没说满的地方**

作者在论文里很坦诚地列了一串自己研究的局限性,这点我觉得值得单独说一说,因为学术论文里主动"自曝其短"的态度并不常见。

比如两种训练模式用的训练数据量根本不对等,打补丁模式用了5000万个token,直接生成模式只用了500万个,足足差了10倍。这个差距按理说应该对打补丁模式更有利才对,可它还是输了,这反而让论文的核心结论更站得住脚,如果多给了10倍的训练量还打不过,那说明差距不是"没练够",而是这条路径本身有更根本的限制。

再比如整个实验只在Flutter/Dart这一个语言生态里做的,没法直接说这个结论能推广到别的编程语言或者更大规模的多文件项目上。还有一个挺有趣的意外情况,Qwen模型的直接生成版本训练时学习率调度出了个bug,原本该逐渐降低的学习率一直没降下来,结果反而是这个"带瑕疵"的版本成了全场表现最好的模型。作者的态度是老老实实把这个瑕疵写出来,而不是悄悄重新训练一遍换个好看的结果,这种诚实度在我看来是这篇论文比较难得的地方。

写在后面

读完这篇论文,我印象最深的其实不是"直接生成赢了"这个结论本身,而是作者拆解失败原因时那种不厌其烦的追问方式。很多研究到"方法A比方法B好"这一步就打住了,但这篇论文继续往下问:好在哪?差距从哪来?是过程出故障,还是结果本身有问题?是任务难度不公平造成的,还是能力真的有差距?每问一层,就排除掉一种简单粗暴的解释,直到剩下"搜索目标歧义"和"任务局部性"这两个具体到可以指着说出来的机制。

这种层层剥洋葱式的排查方法,其实跟做工程排障或者医学诊断的思路很像。表面症状是"发烧",但真正有用的诊断从来不会停在"发烧"这一层,得一直往下问,直到问出具体是哪个器官出了什么问题。这篇论文对"AI打补丁为什么写得差"这件事,做的正是这样一次彻底的诊断。

还有一点让我觉得挺意外的是,论文里提到的"字符相似度"这个假象指标。如果只看这一个数字,你完全可以写出一篇结论相反的论文,标题都能想好:"打补丁模式在三分之一的任务上更接近标准答案"。这提醒我一件事,评价一个AI系统好不好,选错了尺子,结论可以直接翻转。这大概是这篇论文最值得记住的地方,不是它证明了什么,而是它示范了怎么去质疑自己手里的每一个数字。

Q&A

Q1:AI改代码时用打补丁方式和直接重写整个文件哪个更好?

A:根据这篇论文的实验,在Flutter/Dart代码编辑任务上,直接生成完整文件的方式在几乎所有指标上都明显胜过打补丁式的分步编辑,通过率能高出40到45个百分点。

Q2:为什么AI打补丁式编辑容易出错,是因为经常卡死或格式错误吗?

A:不是。论文发现81%到85%的打补丁编辑过程都能正常走完,真正的问题是"静默的内容腐化",也就是流程看起来顺利,但代码逻辑已经悄悄改错了,尤其是遇到搜索目标有歧义、系统只能"猜"改哪一处的时候。

Q3:打补丁式AI编辑完全没有优势吗?

A:不是完全没有优势。研究发现打补丁模式在编辑步数少、改动范围局部的任务上,比如代码重构和错误处理修复,表现能接近甚至优于直接生成模式,这个现象被称为"任务局部性"。

加载中...