← 返回 2026-09-10

Diff 与整文件之争:Flutter/Dart 代码模型迭代编辑式与直接生成的实证比较 Diffs vs. Whole Files: An Empirical Comparison of Iterative Edit-Based and Direct Generation for Flutter/Dart Code Models

Andrej Andrejev 📅 2026-09-05 👍 8 2026-09-12 18:30
LLM评测 代码生成 代码编辑 实证研究 训练目标设计

四种受控对比模型实证表明整文件直接生成全面优于Diff迭代编辑,Diff仅在短局部编辑占优

前置知识

Diff 式迭代编辑(search/replace edits)

把代码修改表示为一系列局部查找/替换块:模型每步输出一个 (动作类型与自然语言描述)及一或多个 / 块,系统用精确匹配在当前文件中定位查找串并替换,循环直到模型输出 或步数预算(本文为 20 步)耗尽。这与 Aider 等编程助手的编辑格式同源:每轮生成 token 少、改动范围可控,但要求查找串在文件中唯一可定位,出现多次匹配就需要消歧启发式。

它是本文比较的两种输出机制之一。理解其应用机制(精确匹配、零次匹配报 apply_failed、多次匹配触发首现回退)是读懂失败归因分析(Section 4.2–4.3)的前提。

teacher forcing 与 bits_per_byte

bits_per_byte 是模型在参考补全上的困惑度类指标:以教师强制方式对参考 final_code 做单次前向评分,计算平均每字节的比特数,数值越低说明模型对参考代码的预测越准。本文对 direct 与 steps 两种模式用完全相同的方式计算(单次 teacher-forced 打分),与 steps 的多步轨迹本身解耦。

它是论文四大核心指标之一(Table 2),而且作者专门提醒:其逐行胜率接近 0(0.1–0.3%)是结构性的,不反映编辑质量,避免读者误读 Table 8。

LLM-as-judge 盲评协议

用一个 LLM(本文为 gpt-4.1)按结构化输出 schema 给候选代码打 1–5 分,维度为 goal_fulfillment、correctness、code_quality,外加自由文本理由。盲评指评委只看到任务指令、初始文件和单个候选文件,不知道模型名、训练模式或静态分析结果,且每次只评一个输出而非成对比较。本文覆盖了几乎全部 held-out 集,共 7,161 个评判行。

它用于验证二元静态分析信号 dart_pass 是否掩盖了'两侧都编译通过'时代码质量的差异,是论文核心结论'direct 残余质量优势真实存在'(Table 7)的关键证据。

匹配 ID 对比(matched-ID comparison)

为排除'steps 的失败恰好集中在更难任务'这一选择偏差,先筛出 steps 模式的干净子集(轨迹正常 done、歧义回退次数为 0、未触及 20 步预算),再在这些样本 ID 上取 direct 模式的结果作同 ID 对比,而不是与 direct 全量结果比。进一步把干净子集限制到 dart_pass=True 的行则称为 best-case 比较。

这是论文控制任务难度混杂的方法学支柱:Table 6 显示控制后差距反而扩大(24.9pp/12.4pp),Table 7 的盲评也建立在 matched-ID 行上。

Wilson 置信区间

对二值比例(如 dart_pass 通过率)的置信区间估计方法,基于 Wilson 得分区间,相比正态近似在样本量不大或比例接近 0/1 时更稳健、不会出现越界区间。本文 Table 2 为每个臂的 dart_pass 报告 95% Wilson CI,例如 Rainbow-Pony direct 为 [0.783, 0.820]。

用于判断 direct 与 steps 通过率差距的统计显著性:两组区间完全不重叠是作者声称'宽幅、非重叠优势'的依据。

研究动机

当语言模型被要求修改一个已有源文件时,业界几乎清一色选择 Diff 式编辑:diff 比整文件短、生成更便宜、不容易静默破坏模型没被要求改的代码,也更接近人类评审 PR diff 的工作单元。但这一直觉作为训练目标(而不只是推理接口)是否成立并不确定,现有证据相互矛盾:Aider 的持续基准发现 diff 类格式会显著提高'格式错误的编辑'(harness 无法机械应用)的比例,弱模型尤甚;LintSeq(Piterbarg et al., 2025)则显示在合成编辑序列上训练能提升下游代码合成的 pass@1,主张编辑序列是更好的课程;Cheng et al. (2026) 进一步主张两种格式没有谁全面更好,正确格式是每个具体编辑的属性,应自适应选择。这些结论来自不同基座模型、不同数据集、不同 diff 语法的第三方基准,跨论文比较根本无法把效应归因到'输出机制'本身。

本文的目标是本文要在单一领域(Flutter/Dart 代码编辑)内端到端回答'direct 还是 steps':训练两个架构与预训练史差异极大的模型——从零训练、约 100M 参数的 Rainbow-Pony(98,146,432 参数,仅见过 Flutter/Dart 与英文)与微调自 Qwen2.5-Coder-0.5B(493.81M 参数)的模型——各自在 direct(一次生成完整修改后文件)与 steps(迭代 search/replace 编辑,预算 20 步)两种机制下微调,固定训练数据(同一 14,600 个手工设计任务池)、分词管线、批大小 8、块大小 1,024 与评测脚手架,得到四个模型臂,在约 1,790 个 held-out 任务上系统比较 dart analyze 静态分析通过率、teacher-forced bits_per_byte、字符相似度与 gpt-4.1 盲评的三维 1–5 分,从而把输出机制本身的效应从众多混杂因素中干净地剥离出来。

与已有工作不同的是,与依赖第三方榜单或 API 调用的工作不同,本文全部在自己训练的模型上端到端进行,并刻意用'纯 Flutter/Dart 从零小模型 + 多语言预训练模型'的配对设计交叉检验:若差距只出现在从零模型上,就可能是'没学会编辑格式'的欠训练伪影。论文还以少见的坦诚主动披露两处实验瑕疵——steps 与 direct 微调 token 不对等(50M vs 5M,约 10 倍)和 qwen-direct 的余弦学习率调度器按 43,000 步构建而实际只跑 5,000 步导致 LR 停在 $2.9\text{–}3.0\times10^{-5}$ 未退火到 $3\times10^{-6}$——并基于'约 84%/70% 的 steps 失败发生在正常完成的轨迹中'这一反直觉观察切入,最终提出 task locality(任务局部性)把类别效应与轨迹长度效应统一为同一机制。

核心方法

直觉上,整文件直接重写简单粗暴但一次成型、不依赖定位;迭代 diff 每步省 token,却引入查找定位、歧义消解与跨步误差累积。技术路线是一个受控四臂实验:两种骨干——从零预训练 119,000 步、约 19.5 亿 token(0.79 epoch over 26 亿语料,70% Flutter/Dart + 30% 英文,16k BPE 词表,余弦 LR $3\times10^{-4}\to2\times10^{-5}$,最终损失 1.28/1.31)的 Rainbow-Pony-100M(98,163,350 参数),与微调自 Qwen2.5-Coder-0.5B(493.81M 参数)的模型——各自在 direct(一次生成完整修改后文件)与 steps(迭代 search/replace 编辑,预算 20 步)两种机制下微调。steps 格式用 /// 标签组织,模型每步补全一个 加若干 查找/替换块,末步附 。四臂共享同一 14,600 任务池与训练配置(批大小 8、块大小 1,024),在约 1,790 个 held-out 任务上贪心解码评测 dart_pass、bits_per_byte、similarity_ratio、stop_reason、num_steps、num_fallback_steps,并用 gpt-4.1 盲评三个维度,共 7,161 个评判行。

核心创新是两点。第一,把'输出机制'做成训练目标层面的受控变量:同一数据、同一分词、同一评测脚手架下的四臂对比,解决了跨论文比较中基座、数据、diff 语法混杂无法归因的问题;这与 Aider 只测推理接口、LintSeq 只测代码合成课程都不同。第二,task locality 统一解释。失败归因表明 steps 的落后不是预算耗尽或解析失败(done 轨迹占 85.2%/81.3%),而是正常完成轨迹内的'静默语义漂移',其中大部分由歧义搜索串的首现回退启发式错误消歧造成——done 轨迹中用过回退的 dart_pass 仅 0.123/0.175,未用的为 0.570/0.800。而 steps 唯一能赢的场景——重构类(胜出占比 18.6%/14.3%)与错误处理类(15.1%/15.9%,基线约 11.2%)恰是平均步数最低的两类(4.14/6.84 与 4.49/7.24 步),且胜者轨迹更短(5.54/6.65 步 vs 其余 8.94/9.02 步)——表面是两个发现,实为'修改所需的空间局部性与自包含程度'这同一变量的两次显影。

方法步骤详情

第一步,从零预训练 Rainbow-Pony:在约 26 亿 token 语料上以批大小 16、块大小 1,024(每步 16,384 token)训练 119,000 步,余弦学习率 $3\times10^{-4}\to2\times10^{-5}$,词表缩容到 16,022 以容纳 、 等结构 token。第二步,构建微调数据:同一 14,600 个 (goal, initial_code, final_code) 三元组(36 类任务、3 个复杂度档)直接作 direct 数据(5M token),并经类 LintSeq 的步分解把每个全文件样例展开为逐编辑序列数据集(steps 模式采样 50M token)。第三步,四臂微调:direct 臂各 5,000 步约 3 epoch(RP-direct 最终损失 0.157/0.189,Qwen-direct 0.074/0.162),steps 臂各 43,000 步约 3 epoch(RP-steps 0.221/0.292,Qwen-steps 0.247),余弦 LR $3\times10^{-5}\to3\times10^{-6}$(qwen-direct 因调度器错误 LR 未退火,作者如实披露并报告实际 checkpoint)。第四步,steps 推理时机械应用编辑:查找串唯一则替换,零次报 apply_failed,多次触发首现回退。第五步,评测:约 1,790 个 held-out 任务(RP n=1,789,Qwen n=1,792)贪心解码,记录四类指标与轨迹元数据,再做 matched-ID 子集分析与 gpt-4.1 盲评。

技术新颖性

新颖性不在新模型或新算法,而在实验设计与解释框架。其一,受控变量法:把通常藏在推理接口选择背后的输出机制提升为训练目标的实验变量,两个容量与预训练史迥异的骨干交叉验证差距稳健性,且明确与 LintSeq(编辑序列作为合成课程有效)方向相反、与之调和——合成新程序与在语义已受约束的既有文件中定位修改是不同任务。其二,失败归因颗粒度:用 stop_reason 四分类与 num_fallback_steps 把'steps 更差'分解为正常完成但内容漂移(占失败约 84%/70%)、预算耗尽、应用失败、格式错误,并进一步把 done 桶内约 65%/71% 的失败定位到歧义回退这一可指认的机制,而不是笼统归咎'模型笨'。其三,task locality:把类别效应与轨迹长度效应(胜率随 num_steps 从 10–18% 单调降至约 0%,Welch 检验 2×3 全部 $p<0.001$)统一到单一变量,与 Cheng et al. 的自适应格式主张相互独立印证。其四,方法透明度:公开逐表可复算的评测数据集并主动披露 LR 配置错误与 token 不对等。

实验结果

聚合层面 direct 全面碾压(Table 2):dart_pass 上 Rainbow-Pony direct 0.802 [0.783, 0.820] vs steps 0.347 [0.325, 0.369](差 45.5 个百分点),Qwen direct 0.900 vs steps 0.501(差 39.9 点);bits_per_byte 0.107 vs 0.180 与 0.088 vs 0.142、similarity_ratio 0.511 vs 0.439 与 0.568 vs 0.493 全部同向。失败归因(Table 3–4)显示差距不是流程性伪影:done 轨迹占 85.2%/81.3%,约 84%/70% 的失败发生在正常完成的轨迹中;回退分析(Table 5)进一步定位机制——done 轨迹内用过歧义回退的通过率仅 0.123/0.175(n=730/537),未用的 0.570/0.800(n=795/919),回退轨迹贡献 done 桶内约 65%/71% 的失败。匹配 ID 对比(Table 6)控制任务选择偏差后差距反而扩大:RP 792 个干净 ID 上 clean-steps 57.2% vs matched-direct 82.1%(24.9pp),Qwen 919 个 ID 上 80.0% vs 92.4%(12.4pp)。再限制到两侧都编译的行(Table 7,n=390/688),gpt-4.1 盲评六维全部 direct 显著更高(RP 代码质量 3.96 vs 4.46;Qwen 目标达成 4.36 vs 4.72,Welch $p<0.001$)——差距约一半来自编译率,另一半是真实残余质量差。steps 的胜利小而可解释(Table 8–12):dart_pass 逆转率仅 3.5–4.8%,judge 胜率 4.8–9.2%(similarity 的 33.7–34.8% 是度量伪影);胜出超配集中在重构类(18.6%/14.3%)与错误处理类(15.1%/15.9%,基线 11.2%),两类平均步数最低(4.14/6.84 与 4.49/7.24 步);胜者轨迹更短(5.54/6.65 vs 8.94/9.02 步,$t=-8.60/-4.54$),胜率随步数从 10–18% 单调降到约 0%,而 direct 同行分数基本持平。

Fine-tuning configuration and outcome per arm, as logged by the training harness.
Table 1: Fine-tuning configuration and outcome per arm, as logged by the training harness.
Aggregate evaluation metrics by architecture and mode.
Table 2: Aggregate evaluation metrics by architecture and mode.
Steps-mode trajectory outcomes (stop_reason) by architecture.
Table 3: Steps-mode trajectory outcomes (stop_reason) by architecture.
dart_pass rate conditional on stop_reason, steps mode.
Table 4: dart_pass rate conditional on stop_reason, steps mode.
dart_pass rate within done-only trajectories, split by whether the trajectory required at least one ambiguity-fallback edit.
Table 5: dart_pass rate within done-only trajectories, split by whether the trajectory required at least one ambiguity-fallback edit.
Matched-ID comparison: clean steps-mode trajectories vs. direct mode on the identical sample IDs.
Table 6: Matched-ID comparison: clean steps-mode trajectories vs. direct mode on the identical sample IDs.
Blinded LLM-judge scores (1–5 scale) among matched-ID rows where both steps and direct pass dart_pass.
Table 7: Blinded LLM-judge scores (1–5 scale) among matched-ID rows where both steps and direct pass dart_pass.
Steps-mode per-row win rate by metric, across both architectures.
Table 8: Steps-mode per-row win rate by metric, across both architectures.
Task categories overrepresented among dart_pass steps-mode wins, relative to their ~11.2% baseline share of the dataset.
Table 9: Task categories overrepresented among dart_pass steps-mode wins, relative to their ~11.2% baseline share of the dataset.
Trajectory length (num_steps) for composite judge-majority wins vs. all other rows.
Table 10: Trajectory length (num_steps) for composite judge-majority wins vs. all other rows.
Per-dimension Welch's t-tests: trajectory length of steps-mode judge wins vs. non-wins. All p < 0.001.
Table 11: Per-dimension Welch's t-tests: trajectory length of steps-mode judge wins vs. non-wins. All p < 0.001.
Mean num_steps for the two win-overrepresented categories vs. the full 9-category distribution.
Table 12: Mean num_steps for the two win-overrepresented categories vs. the full 9-category distribution.
Summary panel: dart_pass rate, bits_per_byte, similarity_ratio, stop-reason and fallback breakdowns, the matched-ID best-case comparison, and blinded judge scores, across both architectures.
Figure 1: Summary panel: dart_pass rate, bits_per_byte, similarity_ratio, stop-reason and fallback breakdowns, the matched-ID best-case comparison, and blinded judge scores, across both architectures.
Heatmap summary of all four arms across six key metrics (darker = better, per-column normalized).
Figure 2: Heatmap summary of all four arms across six key metrics (darker = better, per-column normalized).
Same heatmap restricted to the best-case steps population (done, no fallback, <20 steps, dart_pass=True) vs. matched direct-mode rows.
Figure 3: Same heatmap restricted to the best-case steps population (done, no fallback, <20 steps, dart_pass=True) vs. matched direct-mode rows.
Judge win rate by trajectory length (num_steps), all steps rows vs. best-case-only steps rows, per architecture and judge dimension.
Figure 4: Judge win rate by trajectory length (num_steps), all steps rows vs. best-case-only steps rows, per architecture and judge dimension.
Mean judge score by trajectory length: steps (all data), steps (best-case IDs), direct (all data), direct (best-case IDs).
Figure 5: Mean judge score by trajectory length: steps (all data), steps (best-case IDs), direct (all data), direct (best-case IDs).
Win rate by trajectory length for both directions (steps-wins, direct-wins), all data vs. best-case IDs.
Figure 6: Win rate by trajectory length for both directions (steps-wins, direct-wins), all data vs. best-case IDs.
Judge score by trajectory length, best-case steps (done, no fallback, <20 steps, dart_pass=True) vs. matched direct-mode rows on the same sample IDs.
Figure 7: Judge score by trajectory length, best-case steps (done, no fallback, <20 steps, dart_pass=True) vs. matched direct-mode rows on the same sample IDs.
查看结构化数据
任务指标本文基线提升
Flutter/Dart 代码编辑(Rainbow-Pony-100M,n=1,789) dart_pass(dart analyze 静态分析通过率,95% Wilson CI) direct 80.2%([78.3%, 82.0%]) steps 34.7%([32.5%, 36.9%]) +45.5 个百分点,置信区间完全不重叠
Flutter/Dart 代码编辑(Qwen2.5-Coder-0.5B,n=1,792) dart_pass direct 90.0%([88.5%, 91.3%]) steps 50.1%([47.8%, 52.4%]) +39.9 个百分点
匹配 ID 干净子集对比(Rainbow-Pony,792 个 clean ID:done、无回退、<20 步) dart_pass matched-direct 82.1% clean-steps 57.2% +24.9 个百分点(控制任务难度后差距大于全量差距)
匹配 ID 干净子集对比(Qwen,919 个 clean ID) dart_pass matched-direct 92.4% clean-steps 80.0% +12.4 个百分点
双侧编译通过子集盲评(Rainbow-Pony,n=390 配对行) gpt-4.1 盲评 code_quality(1–5 分) direct 4.46 steps 3.96 +0.50,Welch t=-6.87,p < 10^-6
双侧编译通过子集盲评(Qwen,n=688 配对行) gpt-4.1 盲评 code_quality(1–5 分) direct 4.82 steps 4.52 +0.30,Welch t=-8.25,p < 10^-6
teacher-forced 参考补全评分(Qwen) bits_per_byte(越低越好) direct 0.088 steps 0.142 降低 0.054(Rainbow-Pony 为 0.107 vs 0.180)
steps 逐行逆转分析(两架构) steps 在 dart_pass 上的逐行胜率 3.5%–4.8%(judge 维度胜率 4.8%–9.2%) direct 同任务通过 steps 仅在极少数短局部编辑任务上反超

局限与改进

作者承认的:微调 token 不对等(steps 50M vs direct 5M,约 10 倍),虽方向上反而强化结论(steps 拿更多预算仍更差),但未做 token 匹配对照,长轨迹可能在 steps 微调集内欠训练;单一领域(Flutter/Dart 小文件),不外推到其他语言或多文件仓库;贪心解码单样本,未测 pass@k;steps 无执行反馈,不能外推到 SWE-bench 式代理循环;首现回退启发式是静默错误源(部分'模式更差'可能是'应用器猜错');盲评 judge 未做人类一致性校验;类别级胜出分析每架构仅 63–86 行、无显著性检验;qwen-direct LR 未退火(val loss 在约 2,400 步后停滞于 0.143–0.164 震荡带),尽管它仍是全场最佳臂。我的补充:100M 与 0.5B 模型都远小于生产级 7B+ 编码模型,强模型可能足够擅长遵循 diff 语法而缩小甚至反转差距;20 步预算与'查找串唯一'假设对长文件偏紧;task locality 仅用 num_steps 作代理,未直接度量编辑 span 的空间集中度;similarity_ratio 被作者自证为伪影却仍列为主指标。

独立分析的弱点

第一,模型规模代表性不足:100M 从零模型对结构化 diff 语法的学习能力天然受限,0.5B 也远低于主流 7B+ 编码模型,'direct 更好'可能是小模型学不好格式的产物,改进方向是在 7B–70B 模型上重复同一四臂设计。第二,首现回退启发式是自造的薄弱环节:歧义匹配无论取首现还是末现都可能静默改错位置,更好的做法是要求查找串携带上下文锚点或直接拒绝过短/过泛的搜索块,作者自己也承认这混淆了'模式差'与'应用器差'。第三,steps 被实现为无反馈的单次提交(模型只看自己先前的编辑),与真实代理循环不同,改进方向是加入逐步编译/测试反馈后再比较。第四,task locality 只用轨迹长度代理,未度量编辑的真实空间跨度(hunk 间距、行跨度、与文件长度之比),改进方向是构造直接的空间局部性特征并训练逐编辑级的格式路由器。第五,token 与学习率两处配置瑕疵虽被披露,最干净的做法仍是重跑 token 匹配且 LR 正确退火的对照臂。第六,gpt-4.1 盲评未与人类评分校准,judge 对'整文件重写更整洁'的潜在偏好可能系统性偏向 direct。

未来方向

作者方向:token 匹配的对照实验以排除欠训练混杂;更严格的编辑应用器(拒绝歧义搜索块而非首现猜测);对 judge 做人评一致性抽查;把 task locality 用于逐编辑自适应格式选择(per-edit 路由),与 Cheng et al. 的自适应格式主张对接。基于成果可延伸:把 task locality 形式化为可计算特征(编辑 hunk 数、行跨度、目标行到文件头尾的距离),训练轻量分类器在推理时按编辑难度自动选 direct 或 diff,这直接可落地为编码助手的混合输出模式;在带执行反馈的代理循环中重测(作者明确声明当前结论不适用该场景);扩展到多文件仓库级编辑与 pass@k 评测;探究 steps 模式下编辑错误沿轨迹的累积动力学(如每步条件熵增长);利用已发布的四套共享 sample_id 评测数据做第三方元分析与 judge 校准研究。

复现评估

复现条件在同类实证论文中属顶级:四个评测数据集(各约 1,790 行,含任务指令、初始/参考/输出文件、dart analyze 计数、轨迹元数据与 judge 四列,按共享 sample_id 跨四臂对齐)发布在 HuggingFace(MIT 许可);五个模型 checkpoint(Rainbow-Pony base/direct/steps、Qwen direct/steps)同为 MIT;两个微调数据集 flutter-full-examples-v1(14,600 行)与 flutter-diff-steps-v1(100K–1M 行)为 Apache-2.0;训练与评测代码(微调脚本、步分解管线、Dart 静态分析脚手架、judge_outputs.py)在 github.com/bbidpa/rainbow-pony。论文声明所有表格直接从发布的评测数据重算,并附逐表的学习率日志核对。算力门槛低:100M 预训练约 19.5 亿 token、四臂微联合计约 1.5 亿 processed token,单卡量级可承受;复现盲评需 gpt-4.1 API,跑 dart_pass 需 Dart SDK。主要风险点是步分解与 prompt 渲染函数的细节一致性,但论文给出了未经编辑的真实训练实例(Listing 1)可对照。