读了这篇论文我第一反应是:修 bug 这个活儿,过去我们当它是一道“一次性推理题”,给足上下文、让模型生成一行补丁就交卷;但它其实更像一场“持续对话”——第一轮猜错了不要紧,把报错喂回去,让它看到自己错在哪、测试到底还卡在哪,再让它改。这篇 Keep the Conversation Going: Fixing 162 out of 337 bugs for $0.42 each using ChatGPT(2023)讲的就是这件事。作者用 ChatGPT 在 Defects4J 2.0 的 337 个真实 bug 上修好了 162 个,平均每个 bug 只花 0.42 美元。这个结果在自动程序修复(APR)这个圈子里属于比较炸裂的:不仅修复率明显高于此前基于深度学习的方法,成本还低到可以当日常 CI 工具跑。如果你在做 APR 研究、想在团队里落地 AI 辅助修复,或者纯粹好奇大模型怎么把“找 bug 的过程”变成“多轮追问”的,这篇论文都值得细读。1. 为什么会盯上这篇论文:传统APR修修补补,LLM打开新局面1.1 自动程序修复的老问题:搜索空间与验证成本传统自动程序修复,说穿了就是三条路线。第一条是基于搜索的修复,代表是遗传编程那套:把代码当成个体,用测试通过率当适应度函数,不停地变异、交叉、跑测试,跑个几百上千代来找一个能通过失败测试的补丁。问题是搜索空间大得离谱,一个函数上百行,改哪里、怎么改、改成什么样,排列组合下来根本枚举不完。第二条是基于语义约束的修复,用符号执行把 bug 转成一个约束求解问题,听着优雅,但遇到真实项目的复杂循环、指针别名、外部依赖,约束求解器分分钟爆炸。第三条是基于模板的修复,把常见 bug 归纳成几类模板,循环条件写错、空指针缺判断、数值边界差一,然后套模板生成补丁。这类方法最稳,但覆盖不了多少真实 bug,模板一多维护成本也上来了。这三条路线有一个共同毛病:它们把修复问题误当成“搜索/求解”问题,而真实修 bug 更多是“理解”问题。理解这段代码本来要做什么、测试期望它做什么、报错信息实际指向哪一行,这些恰恰是传统方法最难建模的部分。LLM 出来之后,大家以为直接用大模型生成补丁就能解决问题,结果试下来发现也不对。1.2 ChatGPT修bug为什么以前不好使用 ChatGPT 修 bug 这件事,早在 GPT-3 时代就有人试过,但效果很一般。主要原因在于大部分早期工作都走了“大爆炸式提示(big-bang prompting)”的路线:把失败测试、堆栈信息、相关源代码一次性塞进去,让模型生成一个补丁,然后跑测试,过了就结束,没过就换个提示词再来一次。这种路线的本质问题在于,单轮 prompt 只能给模型一次“猜”的机会,而修 bug 本身是一个迭代收敛的过程。模型第一轮可能理解错了上下文,或者看漏了一处关键逻辑,它需要被“纠正”而不是被“重置”。传统的人写代码也是这么干的:修一次、跑一次、看到新报错、再修一次。对大模型来说,多轮对话里带着上一次的失败信息继续改进,显然比每次从零开始猜要合理得多。这篇论文正是抓准了这个点——不是换一个更大的模型,而是换一套和模型交流的姿势。1.3 论文解决的问题与阅读价值论文的核心问题可以一句话概括:如果给 ChatGPT 一个真实项目的 bug(包含失败测试、堆栈追踪、源码),通过多轮对话的方式持续反馈测试结果,它到底能修好多少?需要多少钱?回答是 162/337,48.1%,每个 bug 平均 0.42 美元。这个数字放在当时的意义是:它让“AI 自动修 bug”从实验室玩票变成了一个可以认真评估工程成本的方案。对于读者来说,这篇论文的价值不只在修复率,更在于它把整个流程工程化了:怎么裁剪上下文、怎么设计指令模板、怎么控制成本、怎么判定修复是否成功。即使你不做 APR,只看它怎么把大模型嵌进一个高频迭代流程中,也能带走不少东西。2. 方法拆解:把修复变成一场“不轻易散场”的对话2.1 完整工作流:失败测试+堆栈+源码,然后循环论文的修复工作流可以分成三个阶段:准备上下文 → 对话式修复 → 验证反馈。每个 bug 的输入是三样东西:能稳定复现 bug 的失败测试用例(failing test)、该测试运行时的堆栈追踪(stack trace)、以及 buggy 版本的源码文件。源码文件包含生产代码,不一定包含测试文件本身——测试和堆栈信息已经起到了“告诉模型问题在哪”的作用。对话式修复是核心。模型第一轮收到的是:[失败测试 + 堆栈 + 源码片段 + 修复指令],输出一个候选补丁。系统立刻编译并运行测试,如果失败,会拿到新的失败信息——可能是编译错误、断言错误、还是新堆栈。这些新信息会被转换为文本,追加到对话上下文中,模型进入第二轮。如此往复。这个流程和人类修 bug 几乎一样。唯一区别是,人会在脑子里做“抽象理解”,而模型要靠每一次具体的失败反馈来修正自己对 bug 的猜测。论文里把这个过程形容为“保持对话进行下去”(keep the conversation going),论述的逻辑是:只要迭代次数足够、反馈足够具体,模型就有机会逐步收敛到正确补丁。实测下来,很多 bug 不是第一轮修对的,而是第三轮、第五轮,甚至第七轮才修对,这恰恰说明了“多轮”的重要性。2.2 五种修复指令模板:为什么每轮换着来这里有一个非常工程化的设计:论文不是只用一条 prompt 反复问,而是准备了五种不同的修复指令模板。每种模板对模型的约束和提示方式不同,比如:模板 A(直接修复):只把失败测试和堆栈抛给模型,让它直接给出修复后的代码块。这种模式最适合定位明确、错误信息清晰的 bug。模板 B(要求解释):让模型先解释失败原因,再给出补丁。加了“要求解释”这个动作之后,模型需要先归纳问题,相当于强迫它做一步推理链,补丁质量通常会更好。模板 C(减少上下文):不提供完整堆栈,只给失败测试名和目标文件。当堆栈信息太长、语义混杂时,去掉噪音反而能让模型聚焦在源码逻辑上。模板 D(多数投票):一次让模型生成多个候选补丁,再从中投票选最优。适合模型第一轮已经给出好几个思路但无法确认的情况。模板 E(基线条目):仅把 buggy 源码给模型,不附带任何测试信息,作为对照。按论文的设计,每种指令都覆盖一类“对话状态”。比如前几轮用“要求解释”模板已经把问题原因诊断出来了,后面就可以切到“直接修复”模板;如果试了两轮补丁测试还是失败,就切“减少上下文”模板,过滤掉可能误导的堆栈信息。这个“指令选择”环节不是让用户手工决定,而是论文里训练了一个轻量的选择器,根据当前轮数、已尝试指令、失败次数等特征来预测下一轮该用哪种模板。这个设计给了一个重要启发:不要迷信某一条“完美提示词”。真正稳定的自动化流程,是要把 prompt 当成可选择的工具集,按状态动态切换。2.3 对话上下文怎么组织才不爆token多轮对话最容易翻车的其实是 token 预算。ChatGPT 的 API 有上下文长度限制,你把失败测试、堆栈、源码、历史对话一股脑塞进去,到第三轮上下文就炸了。论文的处理方式非常务实:第一,限定源码文件范围和代码行数。不是把整个项目传给模型,而是基于堆栈追踪定位到报错所在文件,再截取相关函数体。整个上下文把目标代码块限制在几十行以内,而不是整个文件几百行。第二,历史对话不保留完整原文。每一轮生成的补丁和失败测试输出可以压缩成摘要,比如“上一轮输出在第 12 行有 Compilation error”,而不是把上一轮的整个报错重新贴一遍。第三,最长轮数有硬上限。论文实验里大概设了 10 轮左右,超过这个数就直接放弃,避免成本失控。这里的工程判断值得记住:大模型上下文窗口看着越来越长,但你真的把它塞满了,一来成本剧增,二来模型注意力会被无关信息冲散。一个能稳定跑完 5 轮的对话,上下文的裁剪策略比模型本身的选择还重要。2.4 成本能压到 0.42 美元,靠的是这几点平均每个 bug 花 0.42 美元,放在当时(GPT-3.5-turbo 的 API 价格大约是每 1000 token 零点几美分)是一个“小钱办大事”的水平。这个成本能压下来,核心靠三件事。一是输入侧严格瘦身。严禁把整个仓库丢过去,只传与 bug 相关的函数代码与报错摘要,极大降低每轮消耗的 token 数。二是输出侧限制补丁长度。很多修复本质上就是改几行代码,论文对模型输出长度做了约束,避免模型输出一大堆无关解释、重构建议甚至“对不起我不能帮你修 bug”这种废话。三是大多数 bug 在 3 轮以内收敛。虽然设计了 10 轮上限,但大部分成功案例并没有跑满,成本自然低。值得说明的是,0.42 美元是总成本除以全部 337 个 bug 得到的平均值,不是每个成功案例都只花这个数。真实情况是:修成功的 bug 平均可能花 1 美元左右,失败的 bug 成本也不低,因为失败也可能跑满 10 轮。但这仍然是一个极有吸引力的数字,它意味着即使只有 48% 的修复率,这个方案的经济账也完全算得过来。3. 实验结果怎么看:162/337意味着什么3.1 337个bug是怎么筛出来的Defects4J 2.0 这个基准数据集里总共有一千个左右的真实 bug,每个 bug 都有对应的 buggy 版本、修复版本和触发测试。论文并没有直接拿全集跑,而是先做了一轮“可复现性过滤”。实际原因很朴素:测试要能在当前 JDK/构建环境下稳定复现 fail 状态。有些 bug 依赖特定版本的外部库、网络环境,或者测试本身就存在 flaky 现象,这些 bug 一开始就被排除掉了。过滤完之后剩 337 个 bug。这里有个细节值得注意:论文如果直接报“修复了 162/1000”会很不好看,选择报“337 个可复现 bug 中的 162 个”是合理的学术表达。这也提醒我们,看 APR 论文的修复率时,先看分母是怎么来的。分母是“全部 bug”还是“可复现 bug”,含金量差别很大。3.2 修复率与成本数据画像修复率 48.1%(162/337)是论文的主打数字。为了让你对这个数字有概念,我可以给几条参考线。传统基于模板的方法在 Defects4J 1.x 上的修复率多在 20%~30% 左右。此前基于深度学习(如 Recoder)的修复率在 10%~15% 区间。而基于 GPT 3.5 的单轮 prompt 修复率,一般也就是 20% 上下。论文用“对话式”思路把 GPT-3.5 的修复率推到接近 50%,提升幅度是非常可观的。成本画像上,论文给出了一个更细的拆解:整个实验的总 API 花费在 141 美元上下,337 个 bug 的平均成本是 0.42 美元。成功率 bug 的每例成本会更高,但整个方案的高性价比已经很清楚:如果拿一个中型项目来说,几百美元换来自动修复掉一半可复现缺陷,在人力成本面前几乎可以忽略不计。在修复时间上,论文没有高调宣传,但基于测试轮数估算,每个 bug 的平均修复时间在几分钟量级,相比开发人员打开 IDE 手动定位、反复跑测试再验证,也有数量级上的优势。3.3 消融对比:停掉“对话”效果差多少论文里最关键的一组消融实验是把“对话式迭代”这个核心机制去掉,只保留单轮 prompt(即一次性生成补丁)。结果是灾难性的:修复率明显掉到只有对话式流程的不到一半。这意味着论文 48% 的修复率中,至少一半以上的增量来自于“多轮反馈”,而不是“ChatGPT 本身很聪明”。另一个消融方向是去掉“指令选择器”,固定用一种指令模板跑到底。结果同样出现修复率下降,只是下降幅度没有去掉对话那么大。这说明动态切换指令模板的价值主要在处理那些“第一轮方向就没找对”的 bug:不同模板会引导模型从不同角度理解代码,等于给了它多条路试错。这个实验结果给我一个很强的信号:如果要在自己的项目里复现这套方案,对话轮数和反馈机制是优先级最高的组件,提示词本身反而是次要的。3.4 人工验证里的水分与“有效修复”有一个必须泼冷水的问题是:在 APR 领域,“测试通过”不等于“修复正确”。一个补丁可能只是让当前失败测试通过了,却在其他测试上产生了新的回归,或者从根本上没有修复原有缺陷,只是绕过了触发条件。论文对这类情况做了人工验证,结论是:相当一部分成功补丁确实是通过调整逻辑让测试通过,但最终被判定为“正确修复”的 bug 数量,少于报告的 162 个。这不是论文的缺陷,而是整个自动化修复领域的通病。测试是这个系统唯一的 oracle,而测试天然不完备。我在读这篇论文时最大的警惕点也是这里:哪怕修复率报 90%,只要没有经过完整的回归验证和人工代码审查,你都没法确定这些补丁上线后会不会埋雷。看这篇论文时,建议把“162”当作“候选修复上限”而不是“真实正确修复数”,这样不容易被结果带偏。4. 复现与落地:自己跑一遍需要避开哪些坑4.1 环境与模型参数快速配置如果你想把这套流程复现出来,起步配置并不复杂。模型方面直接用当时的 GPT-3.5-turbo API 即可,代码侧用 Python 写一个循环框架,核心只需要四个模块:上下文收集模块(解析失败测试与堆栈)、prompt 构建模块(按指令模板生成消息)、补丁应用与测试模块(将候选补丁写入文件并跑测试)、对话管理模块(维护多轮消息历史)。模型参数上,建议把 temperature 设到一个适中偏低的水平,比如 0.2 到 0.4,避免过度随机导致补丁变化太剧烈。max_tokens 视补丁长度而定,一般设置为 256 到 512 就够用,太大的输出空间会带来不稳定的成本。整个流程要设置一个最大轮数上限,比如 8 到 10 轮,同时记录每一轮消耗的 token 数,方便事后算成本。这里有一个很容易踩的坑:模型生成的补丁不一定能直接对应原文件的行号。你要在应用补丁前做一次简单的文本对齐,按函数名找到目标位置,再做替换。直接按行号硬替换,很容易遇到上下文不匹配而失败。4.2 提示词里最容易翻车的三个细节第一个细节是堆栈追踪不能无脑贴全。堆栈信息几十行,真正的报错点往往只在一个“from xxx in yyy”行。论文的实践是只保留包含项目源码路径的堆栈帧,过滤掉框架内部的调用,因为这些框架帧不仅浪费 token,还会把模型往无关的依赖问题上引。第二个细节是不要只给“当前测试失败”的信息,要给出“期望行为”。失败测试的断言往往只写了“assertEquals(42, result)”这类规则,模型需要结合测试上下文反推出这个 42 是怎么算出来的。如果你的对话里只有“测试失败”四个字而没有失败的具体值,模型就只能瞎猜,修复率会直线下降。把测试里断言的输入和期望值完整保留,这一步的价值比任何提示词技巧都大。第三个细节是源码输出窗口尽量只保留“可能出错的区域”,而不是整个文件。具体做法是检查堆栈追踪中报错的函数,截取该函数及其直接调用方,一并放入 prompt。如果你给的代码范围过大,模型经常会在错误的区域“脑补”修改,导致测试过了但功能全乱。4.3 API调用成本与稳定性工程真正线上跑这套流程时,最容易让你崩溃的不是修复效果,而是 API 的稳定性。实测常见的三个问题是:限流(rate limit)、超时(timeout)和偶尔的 500 错误。建议在代码层面加一个退避重试机制:遇到 429 错误,按指数退避等待几秒;遇到超时,重试前一请求但不要重复累加消息历史;每轮请求前检查 token 用量,如果接近上限,自动丢弃最早的提示词。另外一个省钱技巧是按 bug 缓存中间结果。同一个 bug 在调参过程中会被反复跑,如果每次重跑都花全量成本,实验成本会快速膨胀。可以在每一轮对话结束时,把该轮生成的补丁、测试结果、输入 prompt 存成一条记录,下次重跑时如果命中同一轮,直接复用结果。关于并发,我建议单线程或小并发跑。ChatGPT API 的并发限制是按账号算的,并发开太高触发限流后,重试成本反而比慢慢跑更高。4.4 把这套流程接进日常开发/CI的经验把论文方案搬到实际开发流程中,最大的改变不是技术而是流程:要走通“失败测试自动提交给对话修复 → 补丁生成 → 自动跑全量测试 → 给出候选 PR”的链路。我的建议是先圈定一个小的试点范围:只修单文件、可复现、无外部依赖的 bug。这样对话上下文干净、测试运行稳定、验证逻辑简单。等跑通之后再逐渐扩展到跨文件修复。另一个重要经验是:自动生成补丁上线前,必须要经过一次人工审查,至少看一眼“改动了几行、逻辑等价性如何”。把这个审查环节做成强制门禁,能拦住大部分“假修复”的代码进入主干。有一点被你复现后会体会很深:这套方案不要求开发人员懂任何提示词技巧,只要把失败测试交给它就行。真正难的是让开发团队信任一个自动补丁生成器。我建议团队先把它当成“建议系统”用,而不是“自动驾驶”。它生成的补丁可以被人工修改和讨论,跑几周积累几个成功的修复案例后,信任度自然就上来了。5. 批判与启发:它不是银弹,但给了一个新方向5.1 局限一:测试码当裁判,容易修出表面光APR 领域最深的老坑在论文里依旧存在:修复目标是“让当前测试通过”,而不是“让程序行为符合预期”。多轮对话机制并不会消除这个局限,它只是更高效地探索了绕过测试失败的不同路径。实测中很容易遇到这样的案例:一个 null 指针异常,模型没有增加判空处理,而是把冗余的 catch 块往上一提,异常被吞掉,测试过了,但程序在更严重的情况下会静默失败。这类表面修复在人工审查中大概率被打回来。所以当团队设定自动修复项目的指标时,建议把“候选修复率”和“人工验证通过率”分开统计,后者才是真正有价值的数据。5.2 局限二:大模型看不懂跨模块的“意会”bug另一个被论文数据掩盖的现状是:在 Defects4J 2.0 的可复现 bug 里,大部分是单文件改动。真实项目的 bug 经常牵扯多个模块:一个接口的数据结构变了,调用方的依赖逻辑全要改。这种跨文件的 bug,当前对话流程的上下文根本无法覆盖——你不可能为了修复一个 bug 把三个文件的 2000 行代码全丢进模型,抛进去之后模型也理不清。论文的 48% 修复率,在理想化的数据集上成立,放到真实系统里会打不小的折扣。但这不全是坏事,它说明“上下文收集与压缩”是 APR 未来最有希望的发力点:谁能把跨模块的关键线索精准提取出来,谁就能把大模型的修复能力延伸到真实项目里。5.3 启发:把“对话”思维用到代码评审、单测生成、故障排查上抛开 APR 这个具体领域,这篇论文真正无意中做对的事,是示范了一个“多轮反馈驱动大模型”的通用范式。代码评审也可以这么做:让模型先提意见,把团队的评审意见再喂回去,让它重新修订。单元测试生成同样适用:模型生成测试用例,跑一下覆盖率,把没覆盖到的分支作为反馈,继续生成补充用例。故障排查也一样:让模型连续观察系统日志的不同片段,逐步缩小根因范围。这个范式之所以有效,根源在于大模型处理复杂任务时不是一个稳定的函数。它需要外部世界的真实反馈来校准自己的推理。对话循环提供的正是这种校准机制。你把它用到任何一种“生成→验证→反馈→再生”的任务上,都能收到明显效果。5.4 关于成本与收益的模型思考每次看到“0.42 美元修一个 bug”这种数字,我习惯算一笔更大的账:一位开发人员处理一个真实 bug 的平均时间,从提交 issue 到定位、修复、提交、验证,保守估计在半天到一天。折合人力成本少说几百美元。如果自动修复能先处理掉一半“机械性可复现”的 bug,哪怕剩下的一半仍由人工处理,整体的开发产出提升也已经值回 API 费用。这里真正值得思考的不是“要不要用 AI 修 bug”,而是“如何把 AI 用在那些它确实擅长的地方——组合性修改、重复性逻辑错误、由单一变更引起的连锁失败”。把这些机械工作交给模型,让开发者腾出时间处理需要业务上下文和创新思维的 bug,效率账才真正算得过来。我个人读完这篇论文后,最大的实际动作是把对话式修复的流程移植到了自己的一个小工具上。用三个月时间跑了两个开源项目中的若干真实 issue,得到的最深体会是:大模型修 bug 的成功率,并不取决于你给我多好的模型,而取决于你能不能把它的每一次失败变成下一步的线索。这大概就是“保持对话进行下去”的真正含义。拿到一篇 APR 论文,与其纠结它 162/337 的数字能不能复现,不如先在自己手头最痛的那个 bug 上,跑满十轮对话再说。
读了这篇论文我第一反应是:修 bug 这个活儿,过去我们当它是一道“一次性推理题”,给足上下文、让模型生成一行补丁就交卷;但它其实更像一场“持续对话”——第一轮猜错了不要紧,把报错喂回去,让它看到自己错在哪、测试到底还卡在哪,再让它改。这篇 Keep the Conversation Going: Fixing 162 out of 337 bugs for $0.42 each using ChatGPT(2023)讲的就是这件事。作者用 ChatGPT 在 Defects4J 2.0 的 337 个真实 bug 上修好了 162 个,平均每个 bug 只花 0.42 美元。这个结果在自动程序修复(APR)这个圈子里属于比较炸裂的:不仅修复率明显高于此前基于深度学习的方法,成本还低到可以当日常 CI 工具跑。如果你在做 APR 研究、想在团队里落地 AI 辅助修复,或者纯粹好奇大模型怎么把“找 bug 的过程”变成“多轮追问”的,这篇论文都值得细读。1. 为什么会盯上这篇论文:传统APR修修补补,LLM打开新局面1.1 自动程序修复的老问题:搜索空间与验证成本传统自动程序修复,说穿了就是三条路线。第一条是基于搜索的修复,代表是遗传编程那套:把代码当成个体,用测试通过率当适应度函数,不停地变异、交叉、跑测试,跑个几百上千代来找一个能通过失败测试的补丁。问题是搜索空间大得离谱,一个函数上百行,改哪里、怎么改、改成什么样,排列组合下来根本枚举不完。第二条是基于语义约束的修复,用符号执行把 bug 转成一个约束求解问题,听着优雅,但遇到真实项目的复杂循环、指针别名、外部依赖,约束求解器分分钟爆炸。第三条是基于模板的修复,把常见 bug 归纳成几类模板,循环条件写错、空指针缺判断、数值边界差一,然后套模板生成补丁。这类方法最稳,但覆盖不了多少真实 bug,模板一多维护成本也上来了。这三条路线有一个共同毛病:它们把修复问题误当成“搜索/求解”问题,而真实修 bug 更多是“理解”问题。理解这段代码本来要做什么、测试期望它做什么、报错信息实际指向哪一行,这些恰恰是传统方法最难建模的部分。LLM 出来之后,大家以为直接用大模型生成补丁就能解决问题,结果试下来发现也不对。1.2 ChatGPT修bug为什么以前不好使用 ChatGPT 修 bug 这件事,早在 GPT-3 时代就有人试过,但效果很一般。主要原因在于大部分早期工作都走了“大爆炸式提示(big-bang prompting)”的路线:把失败测试、堆栈信息、相关源代码一次性塞进去,让模型生成一个补丁,然后跑测试,过了就结束,没过就换个提示词再来一次。这种路线的本质问题在于,单轮 prompt 只能给模型一次“猜”的机会,而修 bug 本身是一个迭代收敛的过程。模型第一轮可能理解错了上下文,或者看漏了一处关键逻辑,它需要被“纠正”而不是被“重置”。传统的人写代码也是这么干的:修一次、跑一次、看到新报错、再修一次。对大模型来说,多轮对话里带着上一次的失败信息继续改进,显然比每次从零开始猜要合理得多。这篇论文正是抓准了这个点——不是换一个更大的模型,而是换一套和模型交流的姿势。1.3 论文解决的问题与阅读价值论文的核心问题可以一句话概括:如果给 ChatGPT 一个真实项目的 bug(包含失败测试、堆栈追踪、源码),通过多轮对话的方式持续反馈测试结果,它到底能修好多少?需要多少钱?回答是 162/337,48.1%,每个 bug 平均 0.42 美元。这个数字放在当时的意义是:它让“AI 自动修 bug”从实验室玩票变成了一个可以认真评估工程成本的方案。对于读者来说,这篇论文的价值不只在修复率,更在于它把整个流程工程化了:怎么裁剪上下文、怎么设计指令模板、怎么控制成本、怎么判定修复是否成功。即使你不做 APR,只看它怎么把大模型嵌进一个高频迭代流程中,也能带走不少东西。2. 方法拆解:把修复变成一场“不轻易散场”的对话2.1 完整工作流:失败测试+堆栈+源码,然后循环论文的修复工作流可以分成三个阶段:准备上下文 → 对话式修复 → 验证反馈。每个 bug 的输入是三样东西:能稳定复现 bug 的失败测试用例(failing test)、该测试运行时的堆栈追踪(stack trace)、以及 buggy 版本的源码文件。源码文件包含生产代码,不一定包含测试文件本身——测试和堆栈信息已经起到了“告诉模型问题在哪”的作用。对话式修复是核心。模型第一轮收到的是:[失败测试 + 堆栈 + 源码片段 + 修复指令],输出一个候选补丁。系统立刻编译并运行测试,如果失败,会拿到新的失败信息——可能是编译错误、断言错误、还是新堆栈。这些新信息会被转换为文本,追加到对话上下文中,模型进入第二轮。如此往复。这个流程和人类修 bug 几乎一样。唯一区别是,人会在脑子里做“抽象理解”,而模型要靠每一次具体的失败反馈来修正自己对 bug 的猜测。论文里把这个过程形容为“保持对话进行下去”(keep the conversation going),论述的逻辑是:只要迭代次数足够、反馈足够具体,模型就有机会逐步收敛到正确补丁。实测下来,很多 bug 不是第一轮修对的,而是第三轮、第五轮,甚至第七轮才修对,这恰恰说明了“多轮”的重要性。2.2 五种修复指令模板:为什么每轮换着来这里有一个非常工程化的设计:论文不是只用一条 prompt 反复问,而是准备了五种不同的修复指令模板。每种模板对模型的约束和提示方式不同,比如:模板 A(直接修复):只把失败测试和堆栈抛给模型,让它直接给出修复后的代码块。这种模式最适合定位明确、错误信息清晰的 bug。模板 B(要求解释):让模型先解释失败原因,再给出补丁。加了“要求解释”这个动作之后,模型需要先归纳问题,相当于强迫它做一步推理链,补丁质量通常会更好。模板 C(减少上下文):不提供完整堆栈,只给失败测试名和目标文件。当堆栈信息太长、语义混杂时,去掉噪音反而能让模型聚焦在源码逻辑上。模板 D(多数投票):一次让模型生成多个候选补丁,再从中投票选最优。适合模型第一轮已经给出好几个思路但无法确认的情况。模板 E(基线条目):仅把 buggy 源码给模型,不附带任何测试信息,作为对照。按论文的设计,每种指令都覆盖一类“对话状态”。比如前几轮用“要求解释”模板已经把问题原因诊断出来了,后面就可以切到“直接修复”模板;如果试了两轮补丁测试还是失败,就切“减少上下文”模板,过滤掉可能误导的堆栈信息。这个“指令选择”环节不是让用户手工决定,而是论文里训练了一个轻量的选择器,根据当前轮数、已尝试指令、失败次数等特征来预测下一轮该用哪种模板。这个设计给了一个重要启发:不要迷信某一条“完美提示词”。真正稳定的自动化流程,是要把 prompt 当成可选择的工具集,按状态动态切换。2.3 对话上下文怎么组织才不爆token多轮对话最容易翻车的其实是 token 预算。ChatGPT 的 API 有上下文长度限制,你把失败测试、堆栈、源码、历史对话一股脑塞进去,到第三轮上下文就炸了。论文的处理方式非常务实:第一,限定源码文件范围和代码行数。不是把整个项目传给模型,而是基于堆栈追踪定位到报错所在文件,再截取相关函数体。整个上下文把目标代码块限制在几十行以内,而不是整个文件几百行。第二,历史对话不保留完整原文。每一轮生成的补丁和失败测试输出可以压缩成摘要,比如“上一轮输出在第 12 行有 Compilation error”,而不是把上一轮的整个报错重新贴一遍。第三,最长轮数有硬上限。论文实验里大概设了 10 轮左右,超过这个数就直接放弃,避免成本失控。这里的工程判断值得记住:大模型上下文窗口看着越来越长,但你真的把它塞满了,一来成本剧增,二来模型注意力会被无关信息冲散。一个能稳定跑完 5 轮的对话,上下文的裁剪策略比模型本身的选择还重要。2.4 成本能压到 0.42 美元,靠的是这几点平均每个 bug 花 0.42 美元,放在当时(GPT-3.5-turbo 的 API 价格大约是每 1000 token 零点几美分)是一个“小钱办大事”的水平。这个成本能压下来,核心靠三件事。一是输入侧严格瘦身。严禁把整个仓库丢过去,只传与 bug 相关的函数代码与报错摘要,极大降低每轮消耗的 token 数。二是输出侧限制补丁长度。很多修复本质上就是改几行代码,论文对模型输出长度做了约束,避免模型输出一大堆无关解释、重构建议甚至“对不起我不能帮你修 bug”这种废话。三是大多数 bug 在 3 轮以内收敛。虽然设计了 10 轮上限,但大部分成功案例并没有跑满,成本自然低。值得说明的是,0.42 美元是总成本除以全部 337 个 bug 得到的平均值,不是每个成功案例都只花这个数。真实情况是:修成功的 bug 平均可能花 1 美元左右,失败的 bug 成本也不低,因为失败也可能跑满 10 轮。但这仍然是一个极有吸引力的数字,它意味着即使只有 48% 的修复率,这个方案的经济账也完全算得过来。3. 实验结果怎么看:162/337意味着什么3.1 337个bug是怎么筛出来的Defects4J 2.0 这个基准数据集里总共有一千个左右的真实 bug,每个 bug 都有对应的 buggy 版本、修复版本和触发测试。论文并没有直接拿全集跑,而是先做了一轮“可复现性过滤”。实际原因很朴素:测试要能在当前 JDK/构建环境下稳定复现 fail 状态。有些 bug 依赖特定版本的外部库、网络环境,或者测试本身就存在 flaky 现象,这些 bug 一开始就被排除掉了。过滤完之后剩 337 个 bug。这里有个细节值得注意:论文如果直接报“修复了 162/1000”会很不好看,选择报“337 个可复现 bug 中的 162 个”是合理的学术表达。这也提醒我们,看 APR 论文的修复率时,先看分母是怎么来的。分母是“全部 bug”还是“可复现 bug”,含金量差别很大。3.2 修复率与成本数据画像修复率 48.1%(162/337)是论文的主打数字。为了让你对这个数字有概念,我可以给几条参考线。传统基于模板的方法在 Defects4J 1.x 上的修复率多在 20%~30% 左右。此前基于深度学习(如 Recoder)的修复率在 10%~15% 区间。而基于 GPT 3.5 的单轮 prompt 修复率,一般也就是 20% 上下。论文用“对话式”思路把 GPT-3.5 的修复率推到接近 50%,提升幅度是非常可观的。成本画像上,论文给出了一个更细的拆解:整个实验的总 API 花费在 141 美元上下,337 个 bug 的平均成本是 0.42 美元。成功率 bug 的每例成本会更高,但整个方案的高性价比已经很清楚:如果拿一个中型项目来说,几百美元换来自动修复掉一半可复现缺陷,在人力成本面前几乎可以忽略不计。在修复时间上,论文没有高调宣传,但基于测试轮数估算,每个 bug 的平均修复时间在几分钟量级,相比开发人员打开 IDE 手动定位、反复跑测试再验证,也有数量级上的优势。3.3 消融对比:停掉“对话”效果差多少论文里最关键的一组消融实验是把“对话式迭代”这个核心机制去掉,只保留单轮 prompt(即一次性生成补丁)。结果是灾难性的:修复率明显掉到只有对话式流程的不到一半。这意味着论文 48% 的修复率中,至少一半以上的增量来自于“多轮反馈”,而不是“ChatGPT 本身很聪明”。另一个消融方向是去掉“指令选择器”,固定用一种指令模板跑到底。结果同样出现修复率下降,只是下降幅度没有去掉对话那么大。这说明动态切换指令模板的价值主要在处理那些“第一轮方向就没找对”的 bug:不同模板会引导模型从不同角度理解代码,等于给了它多条路试错。这个实验结果给我一个很强的信号:如果要在自己的项目里复现这套方案,对话轮数和反馈机制是优先级最高的组件,提示词本身反而是次要的。3.4 人工验证里的水分与“有效修复”有一个必须泼冷水的问题是:在 APR 领域,“测试通过”不等于“修复正确”。一个补丁可能只是让当前失败测试通过了,却在其他测试上产生了新的回归,或者从根本上没有修复原有缺陷,只是绕过了触发条件。论文对这类情况做了人工验证,结论是:相当一部分成功补丁确实是通过调整逻辑让测试通过,但最终被判定为“正确修复”的 bug 数量,少于报告的 162 个。这不是论文的缺陷,而是整个自动化修复领域的通病。测试是这个系统唯一的 oracle,而测试天然不完备。我在读这篇论文时最大的警惕点也是这里:哪怕修复率报 90%,只要没有经过完整的回归验证和人工代码审查,你都没法确定这些补丁上线后会不会埋雷。看这篇论文时,建议把“162”当作“候选修复上限”而不是“真实正确修复数”,这样不容易被结果带偏。4. 复现与落地:自己跑一遍需要避开哪些坑4.1 环境与模型参数快速配置如果你想把这套流程复现出来,起步配置并不复杂。模型方面直接用当时的 GPT-3.5-turbo API 即可,代码侧用 Python 写一个循环框架,核心只需要四个模块:上下文收集模块(解析失败测试与堆栈)、prompt 构建模块(按指令模板生成消息)、补丁应用与测试模块(将候选补丁写入文件并跑测试)、对话管理模块(维护多轮消息历史)。模型参数上,建议把 temperature 设到一个适中偏低的水平,比如 0.2 到 0.4,避免过度随机导致补丁变化太剧烈。max_tokens 视补丁长度而定,一般设置为 256 到 512 就够用,太大的输出空间会带来不稳定的成本。整个流程要设置一个最大轮数上限,比如 8 到 10 轮,同时记录每一轮消耗的 token 数,方便事后算成本。这里有一个很容易踩的坑:模型生成的补丁不一定能直接对应原文件的行号。你要在应用补丁前做一次简单的文本对齐,按函数名找到目标位置,再做替换。直接按行号硬替换,很容易遇到上下文不匹配而失败。4.2 提示词里最容易翻车的三个细节第一个细节是堆栈追踪不能无脑贴全。堆栈信息几十行,真正的报错点往往只在一个“from xxx in yyy”行。论文的实践是只保留包含项目源码路径的堆栈帧,过滤掉框架内部的调用,因为这些框架帧不仅浪费 token,还会把模型往无关的依赖问题上引。第二个细节是不要只给“当前测试失败”的信息,要给出“期望行为”。失败测试的断言往往只写了“assertEquals(42, result)”这类规则,模型需要结合测试上下文反推出这个 42 是怎么算出来的。如果你的对话里只有“测试失败”四个字而没有失败的具体值,模型就只能瞎猜,修复率会直线下降。把测试里断言的输入和期望值完整保留,这一步的价值比任何提示词技巧都大。第三个细节是源码输出窗口尽量只保留“可能出错的区域”,而不是整个文件。具体做法是检查堆栈追踪中报错的函数,截取该函数及其直接调用方,一并放入 prompt。如果你给的代码范围过大,模型经常会在错误的区域“脑补”修改,导致测试过了但功能全乱。4.3 API调用成本与稳定性工程真正线上跑这套流程时,最容易让你崩溃的不是修复效果,而是 API 的稳定性。实测常见的三个问题是:限流(rate limit)、超时(timeout)和偶尔的 500 错误。建议在代码层面加一个退避重试机制:遇到 429 错误,按指数退避等待几秒;遇到超时,重试前一请求但不要重复累加消息历史;每轮请求前检查 token 用量,如果接近上限,自动丢弃最早的提示词。另外一个省钱技巧是按 bug 缓存中间结果。同一个 bug 在调参过程中会被反复跑,如果每次重跑都花全量成本,实验成本会快速膨胀。可以在每一轮对话结束时,把该轮生成的补丁、测试结果、输入 prompt 存成一条记录,下次重跑时如果命中同一轮,直接复用结果。关于并发,我建议单线程或小并发跑。ChatGPT API 的并发限制是按账号算的,并发开太高触发限流后,重试成本反而比慢慢跑更高。4.4 把这套流程接进日常开发/CI的经验把论文方案搬到实际开发流程中,最大的改变不是技术而是流程:要走通“失败测试自动提交给对话修复 → 补丁生成 → 自动跑全量测试 → 给出候选 PR”的链路。我的建议是先圈定一个小的试点范围:只修单文件、可复现、无外部依赖的 bug。这样对话上下文干净、测试运行稳定、验证逻辑简单。等跑通之后再逐渐扩展到跨文件修复。另一个重要经验是:自动生成补丁上线前,必须要经过一次人工审查,至少看一眼“改动了几行、逻辑等价性如何”。把这个审查环节做成强制门禁,能拦住大部分“假修复”的代码进入主干。有一点被你复现后会体会很深:这套方案不要求开发人员懂任何提示词技巧,只要把失败测试交给它就行。真正难的是让开发团队信任一个自动补丁生成器。我建议团队先把它当成“建议系统”用,而不是“自动驾驶”。它生成的补丁可以被人工修改和讨论,跑几周积累几个成功的修复案例后,信任度自然就上来了。5. 批判与启发:它不是银弹,但给了一个新方向5.1 局限一:测试码当裁判,容易修出表面光APR 领域最深的老坑在论文里依旧存在:修复目标是“让当前测试通过”,而不是“让程序行为符合预期”。多轮对话机制并不会消除这个局限,它只是更高效地探索了绕过测试失败的不同路径。实测中很容易遇到这样的案例:一个 null 指针异常,模型没有增加判空处理,而是把冗余的 catch 块往上一提,异常被吞掉,测试过了,但程序在更严重的情况下会静默失败。这类表面修复在人工审查中大概率被打回来。所以当团队设定自动修复项目的指标时,建议把“候选修复率”和“人工验证通过率”分开统计,后者才是真正有价值的数据。5.2 局限二:大模型看不懂跨模块的“意会”bug另一个被论文数据掩盖的现状是:在 Defects4J 2.0 的可复现 bug 里,大部分是单文件改动。真实项目的 bug 经常牵扯多个模块:一个接口的数据结构变了,调用方的依赖逻辑全要改。这种跨文件的 bug,当前对话流程的上下文根本无法覆盖——你不可能为了修复一个 bug 把三个文件的 2000 行代码全丢进模型,抛进去之后模型也理不清。论文的 48% 修复率,在理想化的数据集上成立,放到真实系统里会打不小的折扣。但这不全是坏事,它说明“上下文收集与压缩”是 APR 未来最有希望的发力点:谁能把跨模块的关键线索精准提取出来,谁就能把大模型的修复能力延伸到真实项目里。5.3 启发:把“对话”思维用到代码评审、单测生成、故障排查上抛开 APR 这个具体领域,这篇论文真正无意中做对的事,是示范了一个“多轮反馈驱动大模型”的通用范式。代码评审也可以这么做:让模型先提意见,把团队的评审意见再喂回去,让它重新修订。单元测试生成同样适用:模型生成测试用例,跑一下覆盖率,把没覆盖到的分支作为反馈,继续生成补充用例。故障排查也一样:让模型连续观察系统日志的不同片段,逐步缩小根因范围。这个范式之所以有效,根源在于大模型处理复杂任务时不是一个稳定的函数。它需要外部世界的真实反馈来校准自己的推理。对话循环提供的正是这种校准机制。你把它用到任何一种“生成→验证→反馈→再生”的任务上,都能收到明显效果。5.4 关于成本与收益的模型思考每次看到“0.42 美元修一个 bug”这种数字,我习惯算一笔更大的账:一位开发人员处理一个真实 bug 的平均时间,从提交 issue 到定位、修复、提交、验证,保守估计在半天到一天。折合人力成本少说几百美元。如果自动修复能先处理掉一半“机械性可复现”的 bug,哪怕剩下的一半仍由人工处理,整体的开发产出提升也已经值回 API 费用。这里真正值得思考的不是“要不要用 AI 修 bug”,而是“如何把 AI 用在那些它确实擅长的地方——组合性修改、重复性逻辑错误、由单一变更引起的连锁失败”。把这些机械工作交给模型,让开发者腾出时间处理需要业务上下文和创新思维的 bug,效率账才真正算得过来。我个人读完这篇论文后,最大的实际动作是把对话式修复的流程移植到了自己的一个小工具上。用三个月时间跑了两个开源项目中的若干真实 issue,得到的最深体会是:大模型修 bug 的成功率,并不取决于你给我多好的模型,而取决于你能不能把它的每一次失败变成下一步的线索。这大概就是“保持对话进行下去”的真正含义。拿到一篇 APR 论文,与其纠结它 162/337 的数字能不能复现,不如先在自己手头最痛的那个 bug 上,跑满十轮对话再说。