这是一篇关于如何利用**“历史经验”来让人工智能(AI)更擅长“修 Bug(代码错误)”**的研究报告。
为了让你轻松理解,我们可以把写代码想象成盖房子,把AI 模型想象成一位天才但有点“健忘”的建筑师。
1. 核心问题:天才建筑师为什么也会修错房子?
现在的 AI(大语言模型)非常聪明,能写代码、能改错。但是,它们通常有一个毛病:只看眼前,不看历史。
- 现状:当你要 AI 修一个 Bug 时,你通常只给它看**“现在的图纸”**(当前版本的代码)。
- 比喻:这就像你指着房子墙上的一个裂缝问建筑师:“这里裂了,怎么修?”建筑师只看裂缝,却完全不知道这面墙以前是谁砌的、为什么砌成那样、之前有没有人动过这里。
- 后果:因为缺乏背景知识,建筑师可能会用一种“看起来能补上,但以后还会裂”的方法去修,或者根本修不对。
2. 解决方案:HAFix(给建筑师一本“历史日记”)
这篇论文提出了一种叫 HAFix 的新方法。它的核心思想是:别只给 AI 看现在的图纸,把房子的“历史日记”也给它看!
什么是“历史日记”?
在软件开发中,每一次代码的修改都会被记录下来(就像 Git 提交记录)。这篇论文利用了一个叫**“ blame commit(责任提交)”**的概念。
- 比喻:想象一下,这面墙上的裂缝,是张三在去年某天改的。HAFix 会去翻出张三那天为什么改墙、当时还改了哪些地方、他用了什么材料。
- 具体做法:研究者从历史记录中提取了7 种线索(比如:当时改了哪些文件?哪些函数一起变了?代码具体哪里动了?)。
HAFix 怎么工作?
它把这 7 种历史线索整理好,像讲故事一样讲给 AI 听:“嘿,这个 Bug 是因为张三去年改这里时,不小心把隔壁的窗户也带歪了。你看,这是当时的情况……"
有了这些背景,AI 就能更精准地找到病根,开出正确的“药方”。
3. 实验结果:历史经验真的有用吗?
研究者找了两个大数据库(一个是 Python 项目,一个是 Java 项目),里面有几百个真实的 Bug。他们让 AI 在**“只看眼前”和“看了历史日记”**两种情况下修 Bug。
- 结果惊人:
- 单独看:有些历史线索(比如“当时改了哪些函数”)特别管用,修好的 Bug 数量比只看眼前多了40%~50%。
- 组合拳(HAFix-Agg):如果把 7 种线索都结合起来,让 AI 从不同角度去分析,效果更是爆炸式增长。
- 比喻:就像医生看病,以前只给 AI 看“发烧”这个症状;现在 HAFix 把“病人昨天吃了什么、家族病史、最近去过哪里”都告诉 AI。结果 AI 确诊率大幅提升,甚至能治好以前治不好的疑难杂症。
4. 两个关键发现:怎么问很重要,怎么省钱也很重要
除了“看历史”,论文还发现了两个有趣的点:
A. 提问的方式(Prompt)很重要
研究者尝试了三种问法:
- 直接指令:“这是代码,这里有个错,帮我修。”(效果最好)
- 标记指令:在代码里把错的地方标红,说“修这里”。
- 填空指令:把错的地方挖空,让 AI 填空。
结论:对于这种需要结合历史背景的任务,**直接、清晰的指令(Instruction)**效果最好。就像你给助手下命令,直接说“把墙修好,参考张三去年的做法”,比让他猜谜或者填空更有效。
B. 别“死磕”,学会“见好就收”(成本分析)
给 AI 看历史日记,虽然效果好,但代价大(需要更多的计算时间,消耗更多的“算力币”)。
- 全量模式(Exhaustive):不管修没修好,把 7 种历史线索全跑一遍。这就像为了修一个裂缝,把整栋房子的历史档案全翻一遍,太慢了。
- 早停模式(Early Stop):一旦 AI 用某一种线索修好了 Bug,立刻停止,不再尝试其他线索。
- 结论:使用“早停”策略,可以在保持修好率几乎不变的情况下,节省 69% 的时间和 73% 的算力成本!这就像:你问第一个专家,他修好了,你就别问第二个、第三个专家了,既省钱又省时。
5. 总结:这对我们意味着什么?
这篇论文告诉我们,在让 AI 做软件开发工作时:
- 不要只给 AI 看“现在”:把**“过去”**(代码的历史演变)告诉它,它能变得更聪明、更靠谱。
- 要会“讲故事”:用清晰的方式把历史背景讲给 AI 听,效果最好。
- 要懂得“性价比”:不需要每次都把所有历史都翻一遍,学会在修好 Bug 后及时止损,能大大降低成本。
一句话总结:
HAFix 就是给 AI 修 Bug 配了一本“历史回忆录”,让它不再是个“健忘”的修理工,而是一个懂行情的“老法师”,既修得快,又修得准,还懂得怎么省钱。
1. 研究背景与问题 (Problem)
核心问题:
现有的基于大语言模型(LLM)的自动程序修复(APR)方法主要依赖当前项目快照(Current Snapshot)中的上下文信息(如函数代码、文件结构等),而忽略了软件仓库中丰富的历史数据(Historical Data)。此外,在引入历史上下文时,不同的提示词风格(Prompt Styles)对修复效果的影响尚未得到充分探索,且缺乏对引入历史数据所带来的推理成本(时间、Token 消耗)与性能提升之间的权衡分析。
研究缺口:
- 缺乏利用“ blame commit"( blame 提交,即最后修改该行代码的提交)等历史演化信息来辅助 LLM 理解 Bug 根源的研究。
- 缺乏系统性的提示词风格对比(指令式、标签式、掩码式)在历史上下文场景下的表现。
- 缺乏对引入历史数据后,推理成本与修复成功率之间实际权衡的量化分析。
2. 方法论 (Methodology)
作者提出了 HAFix(History-Augmented LLMs on Bug Fixing),一种利用从 blame commit 中提取的七种历史启发式信息(Heuristics)来增强 LLM 修复能力的框架。
2.1 核心组件:历史启发式信息 (Historical Heuristics)
研究从 blame commit(V2)及其前一个提交(V1)中提取了七种历史信号,分为三类:
- 基于名称的启发式 (Name-based):
CFN-modified: 修改的 Bug 文件中被修改的函数名。
CFN-all: 修改的所有文件中被修改的函数名。
FN-modified: 修改的 Bug 文件中所有函数名(无论是否修改)。
FN-all: 修改的所有文件中所有函数名。
FLN-all: 修改的所有文件名。
- 代码演化启发式 (Code-evolution):
FN-pair: 修改前后(V1 和 V2)的函数代码对,展示代码如何演变。
- 补丁级启发式 (Patch-level):
FL-diff: 提交 V2 中的 Git diff 补丁,展示具体的代码变更。
2.2 聚合策略 (HAFix-Agg)
由于将所有历史数据放入单个 Prompt 会导致输入过大,作者提出了 HAFix-Agg 变体。该策略分别对每个启发式信息进行推理,然后聚合结果。这种方法利用了不同启发式信息的互补性(Complementary Strengths),即不同的启发式可能修复不同的 Bug 子集。
2.3 提示词风格 (Prompt Styles)
研究对比了三种提示词风格:
- Instruction: 在指令文本中高亮显示 Bug 行,提供完整上下文。
- InstructionLabel: 在代码中用
<BUGGY LINE> 标签明确标记 Bug 行。
- InstructionMask: 用
<FILL ME> 占位符替换 Bug 行,要求模型填空(Infilling)。
2.4 实验设置
- 数据集: 51 个 Python 单行 Bug (BugsInPy) 和 116 个 Java 单行 Bug (Defects4J)。
- 模型: CodeLlama-7B, DeepSeek-Coder-6.7B, DeepSeek-Coder-V2-Lite-16B。
- 基线 (Baseline): 模仿 GitHub Copilot 的提示词构建方式,仅使用当前快照信息,不含历史数据。
- 评估指标: Pass@k (k=1, 5, 10),统计生成代码通过测试用例的概率。
- 成本分析: 定义了四种执行场景:Exhaustive(全量执行)、ES(早停)、ES-AccSorted(按修复数量排序早停)、ES-UniSorted(按唯一修复数量排序早停)。
3. 主要贡献 (Key Contributions)
- 引入历史上下文增强修复: 首次系统性地评估了从 blame commit 提取的七种历史启发式信息对 LLM 修复 Bug 的效果。
- 提出 HAFix-Agg 聚合方法: 证明了通过聚合多个启发式信息的推理结果,可以显著覆盖基线模型无法修复的 Bug,实现互补优势。
- 提示词风格评估: 系统比较了三种提示词风格,发现 Instruction 风格在大多数情况下最有效,能最好地利用历史上下文。
- 性能 - 成本权衡分析: 提供了详细的推理时间和 Token 消耗分析,证明了早停策略 (Early Stopping) 可以在保持高性能的同时大幅降低成本。
4. 实验结果 (Results)
4.1 性能提升 (RQ1)
- 显著改进: 多个 HAFix 启发式(如
FN-modified, FN-all)在 Defects4J 数据集上相比基线取得了统计显著的提升,且效应量(Effect Size)很大。
- 互补性: 单个启发式虽然整体 Pass@k 可能略低于或接近基线,但它们能唯一修复(Unique Fixes)许多基线漏掉的 Bug。平均而言,每个启发式能额外修复约 3 个 (BugsInPy) 到 9 个 (Defects4J) Bug。
- HAFix-Agg 效果: 聚合所有启发式后,HAFix-Agg 相比基线实现了巨大的性能飞跃:
- BugsInPy: 平均提升 45.05%。
- Defects4J: 平均提升 49.92%。
- 在 Pass@k 评估中,HAFix-Agg 在所有配置下均显著优于基线。
4.2 提示词风格影响 (RQ2)
- Instruction 最优: 在大多数模型和数据集配置下,Instruction 提示词风格表现最好,显著优于 InstructionLabel 和 InstructionMask。
- 例外情况: 对于 DeepSeek-Coder-V2-Lite-16B 在 BugsInPy 上的表现,InstructionMask 略优,表明不同模型对提示词风格的敏感度不同。
4.3 成本与效率分析 (RQ3)
- 早停策略 (Early Stopping): 相比全量执行 (Exhaustive),早停策略(ES, ES-AccSorted, ES-UniSorted)平均减少了 69% 的推理时间和 73% 的 Token 消耗,同时保持了极具竞争力的修复性能。
- 启发式效率差异:
FL-diff (Diff 补丁) 和 FN-all (所有函数名) 通常是最耗时且 Token 消耗最大的启发式,但性价比(时间/性能比)较低。
FN-modified, CFN-modified, CFN-all 在时间和性能之间提供了更好的平衡。
- 排序优化: 按“唯一修复数量”排序的早停策略 (ES-UniSorted) 在某些配置下能进一步减少失败尝试的 Token 消耗。
4.4 与 SOTA 工具对比
HAFix-Agg 在使用较小参数量的开源模型(6.7B-16B)时,修复的 Bug 数量超过了使用更大闭源模型(ChatGPT)的 ChatRepair 和迭代训练方法 ITER。
5. 研究意义与结论 (Significance & Conclusion)
- 理论意义: 验证了软件历史数据(特别是 blame commit 信息)是理解 Bug 根源和生成修复方案的关键上下文,填补了 LLM 在 SE 任务中利用历史演化信息的空白。
- 实践指导:
- 提示词设计: 推荐使用 Instruction 风格来构建包含历史上下文的 Prompt。
- 部署策略: 在实际部署中,不应盲目全量运行所有启发式。建议采用 HAFix-Agg + 早停策略 (Early Stopping),按启发式效果排序执行,以在极低的额外成本下获得最大的修复覆盖率。
- 成本效益: 证明了通过合理的策略(如早停和启发式选择),可以在不显著增加推理成本的前提下,大幅提升 LLM 的 Bug 修复能力。
总结: HAFix 证明了将软件仓库的历史演化信息(如代码变更历史、共演化文件等)融入 LLM 提示词,是提升自动程序修复性能的有效途径。通过聚合多种历史启发式并优化执行策略,可以以合理的成本实现接近 50% 的修复率提升,为工业界大规模应用 LLM 进行代码修复提供了可行的技术方案。
每周获取最佳 computer science 论文。
受到斯坦福、剑桥和法国科学院研究人员的信赖。
请查收邮箱确认订阅。
出了点问题,再试一次?
无垃圾邮件,随时退订。