DeepK:面向数据库内核的自动程序修复框架全链路解析 一篇论文标题背后往往藏着整整几年的坑。DeepK 这个项目名字我盯了很久一个自动程序修复框架挂在 OceanBase 校企联合研究的背景下乍看像是一个“大模型修 bug”的常规课题但真正深入进去才发现它要解决的问题远比“用 ChatGPT 改代码”复杂得多——尤其在数据库内核这种对正确性近乎偏执的领域。这篇文章我想以参与者的视角把这套框架从设计动机、技术拆解、实验设计到论文写作的完整链路讲透把我踩过的坑、试错过的方法、以及最终沉淀下来的经验一并交代清楚。适合正在做 AI4SE人工智能辅助软件工程方向的研究生、对自动化测试和缺陷修复感兴趣的后端开发者以及所有想在校企合作项目中真正落地技术成果的人阅读。1. 这项研究到底在解决什么问题自动程序修复不是新概念学术界搞了十几年但真正能在工业级系统里跑起来的产品少之又少。DeepK 想做的是把这条“最后一公里”走通而 OceanBase 分布式数据库内核就成了最好的试金石。为什么这么说得从 APR 领域本身的困境和数据库场景的特殊性两方面看。1.1 自动程序修复的三座大山自动程序修复英文叫 Automated Program Repair缩写 APR。它的目标很朴素给定一个有 bug 的代码仓库自动定位缺陷位置并生成补丁让测试从失败变通过。这个领域有大量前人工作从早期基于模板的修复到后来基于符号执行的修复再到近几年大模型主导的生成式修复范式换了几轮但始终有几个绕不过去的坎。第一座大山是缺陷定位。你让一个大模型直接读整个仓库它基本抓瞎。代码量动辄几十万行真正的 bug 可能就藏在某个边界条件里而模型的注意力机制很难在没有先验的情况下定位到准确位置。传统方案用测试覆盖率和可疑度排序来找候选行但数据库内核这种并发密集、状态耦合严重的代码单测覆盖率高并不代表逻辑正确定位准确率往往惨不忍睹。第二座大山是补丁生成。即使定位到了可疑代码生成一个能通过全部测试的补丁仍然极其困难。大模型擅长模仿代码风格但它并不理解数据库事务的 ACID 语义不知道什么情况下锁可以被释放什么情况下 redo log 必须落盘。它生成的补丁经常是“语法上无懈可击语义上一塌糊涂”测试一跑就挂。第三座大山是验证成本。修复是否成功的唯一标准是测试但数据库的测试套件重得要命跑一轮完整回归可能需要几十分钟。如果生成的补丁在验证阶段都要排队整个修复循环的效率就被拖垮了。这也是很多 APR 框架停留在论文里的原因——实验室的玩具项目跑得飞快一上真实系统就露馅。DeepK 的设计初衷就是把这“三座大山”按顺序一一拆掉用检索和历史缺陷知识缩小定位范围用场景化的提示词约束生成方向用分层的验证策略把回归成本降到可控水平。1.2 为什么偏偏选中数据库内核OceanBase 这类分布式数据库的内核代码可能是对 APR 系统最苛刻的试炼场但也是最能证明技术价值的场景。首先是代码质量的敏感性。数据库不允许“差不多能用”的补丁。一个并发控制的修复如果存在微小的竞态窗口在低负载测试中可能完全看不出来但一上生产环境、高并发场景下就会触发脏读或死锁。这意味着补丁的验证不能只看单测还要做并发压力测试、故障注入测试、性能回归测试——一套完整的质量门禁。其次是领域的强规则性。数据库内核里有大量不可破坏的语义约定事务要么提交要么回滚索引必须与数据一致WAL 日志必须先于数据页落盘。这些规则不像通用代码里的函数契约那样写在注释里而是散落在代码的各个角落靠开发者多年的经验维系。对 APR 模型来说它必须“学会”这些隐性规则否则生成的补丁就是在跟整个系统的根基对着干。再有一点是可复现性。数据库的 bug 往往由特定的执行序列触发输入是一个复杂的 SQL 查询计划加上精心构造的数据分布输出是错误的结果或崩溃。这种可复现但难理解的缺陷恰恰是评估 APR 系统能力的理想样本。你可以在不泄露测试用例的前提下构造出“已知 bug、未知根因”的评估集然后看修复系统能在多长时间内给出正确补丁。校企联合研究在这里的价值就体现出来了学校团队有算法和模型训练的经验企业团队掌握真实缺陷库和完整的测试与验证基础设施。两边一结合DeepK 才有机会在一个真实、严格、且业务价值清晰的场景里完成闭环验证。1.3 DeepK 项目的定位与目标简单说DeepK 是一个面向数据库内核场景的自动程序修复框架。它不追求“无所不能”的通用修复能力而是聚焦在“已知缺陷描述、代码仓储全量可见、测试门禁可执行”这一假设下把修复这件事做得尽可能稳、尽可能快。具体目标有三条在给定缺陷报告或失败测试的前提下自动定位到可疑代码范围并把候选范围缩得足够小通常控制在一个函数到几个相邻函数之间。基于大模型生成候选补丁并通过多轮指令反馈让补丁质量逼近人工水平。这里不要求一次生成完美补丁但要求迭代收敛。建立一套可落地的验证流水线能够在合理时间通常几十分钟内完成对候选补丁的回归验证输出修复成功的证据链。这三条目标听起来并不浮夸但每一条落到实处都有大量细节要抠。接下来的章节我会逐层展开。2. DeepK 框架的整体设计思路DeepK 的架构并不是一开始就定下来的。我们最早也尝试过端到端的方式把整个仓库丢给大模型让它输出一个完整的 diff。结果并不意外模型完全 hold 不住输出质量远低于可用水平。后来逐步收敛到“检索增强 生成反馈 分层验证”三阶段架构。2.1 不是从零训练模型而是“检索生成验证”很多 APR 论文喜欢微调一个专门的修复模型但 DeepK 一开始就确定了方向不做从零训练也不做大规模微调。原因很实际数据库内核的缺陷样本太稀缺了整个企业级缺陷库里能拿出来做标注的也就几千个样本拿这点数据微调百亿参数模型性价比极低。我们的方案是“检索增强生成”模式。系统维护一个历史缺陷知识库里面包含四类信息缺陷现象描述、可疑函数列表、最终修复补丁、修复后的验证记录。当遇到一个新缺陷时先通过文本相似度和代码结构相似度检索出最接近的历史案例然后把历史案例作为参考片段拼进大模型的上下文。这里有一个非常关键的经验直接给模型看“修复后的代码”作为示例往往不如给模型看“修复前的 Bug 代码 修复后的 diff 缺陷描述”效果更好。因为模型的注意力机制能从“变化”中更快学习到规律而单纯的正确代码示例只会让它模仿风格却学不会“为什么要这样改”。我们在实验中发现这种带缺陷上下文的三元组示例修复成功率比单纯正确代码提升约 23%。生成之后的验证也分两层。第一层是静态验证用编译器和基础 lint 工具检查补丁是否引入语法错误、类型错误、未使用变量等问题。这一层能过滤掉 60% 以上的垃圾补丁而且成本极低每条补丁不到一秒。第二层是动态验证执行与缺陷相关的定向测试集重点验证修复是否真正消除了问题。动态验证通过后再进入完整的回归套件。分层验证的价值不止是省钱更重要的是给生成环节提供了更细粒度的反馈信号。如果一条补丁在静态层被拒绝我们可以快速告诉模型是类型错误还是未定义符号如果动态测试失败我们可以把失败断言和堆栈信息回传给模型让它基于失败原因做第二轮修复。这个“失败信息闭环”是 DeepK 迭代效率远高于一次性生成方案的核心原因。2.2 知识注入让模型读懂数据库的“脾气”通用大模型对数据库代码的理解非常粗浅。它知道begin_transaction和commit这两个词的字面意思但不知道在一个多副本同步提交的协议里commit 之前必须等待多数派返回 ack也不知道在内存表转储到磁盘的过程中必须先冻结写入再拷贝数据。这种领域性的“脾气”不写进提示词里模型永远学不会。DeepK 的做法是构建了一个三层知识注入体系。第一层是通用代码规范包括当前项目的编码风格、异常处理约定、日志格式要求。这些信息通过项目级提示词塞进系统上下文保证生成的补丁在风格上与原有代码融为一体避免出现“代码能跑但一看就是外来户”的情况。第二层是数据库领域约束。我们手工整理了一批“不可 violate 的语义规则”比如所有需要跨节点协调的写操作必须走两阶段提交协议所有索引的更新必须与数据行更新在同一事务内任何 malloc 得到的内存在错误返回路径上必须释放。这批规则以约束清单的方式随每个补丁请求发给模型并明确要求“如果修复涉及这些模块必须遵守对应规则”。第三层是即时检索到的相似缺陷知识。也就是前面提到的问题历史案例这一层不是静态的每次请求都会动态检索因此能保证即使是新增的缺陷类型也能在知识库中找到最接近的参考。三层知识注入的比例也非常讲究。我们试过把知识库里的所有信息一股脑全塞进去结果上下文字数膨胀到几万 token模型的输出质量急剧下降同时单次请求的延迟和成本都翻了好几倍。后来我们用了一个简单的小技巧动态摘要。系统先按关键词粗筛出前 20 个候选案例再用一个轻量模型对每个案例生成 100 字以内的摘要最终只把摘要和其中与当前缺陷最相似的三条完整案例放进上下文。这样上下文的控制量基本维持在 6000 token 左右生成质量也稳定得多。2.3 修复验证闭环测试用例不是万能的讲一个我们在项目中反复碰壁的点测试用例并不是万能的。数据库的缺陷往往只在特定并发时序下触发而现有测试用例可能根本没有覆盖到这条路径。你让模型修一个 bug确实通过了所有现有测试但缺陷依旧存在只是因为测试没能复现出来。这个问题在早期直接击穿了 DeepK 的验证机制。后来我们引入了两个补充手段。第一个是变异验证。系统会拿修复前的缺陷版本对可疑代码附近的表达式做变异生成一组“仍然有 bug”的变体以及一组“已经修复”的对照组。然后把待验证补丁分别应用在这两组代码上观察测试结果。如果补丁对变形变体的测试表现与对照组一致即变异体依旧失败、修复版通过说明补丁真正命中缺陷的语义如果补丁让变异体也通过了测试那很可能说明它只是在“绕过测试”而不是“修复缺陷”。第二个是对偶测试生成。我们会用大模型为修复后的代码生成一组新的边界测试用例重点覆盖与缺陷相关的模块边界。这些新生成的用例会被回灌到原缺陷版本上如果能复现失败说明新用例确实触碰到了缺陷路径然后再把新用例加入验证集对候选补丁做二次验证。这一手能有效防止模型“刷测试”——只修了报告里的那一条失败用例却把整个语义改歪了搞出一堆新的隐蔽问题。用这套闭环DeepK 的补丁通过率有了质的提升。最终统计中修复后的补丁通过完整回归测试的比例达到 81%而早期没有闭环验证时的通过率只有 46%。差距非常明显。3. 数据库场景下绕不开的实操细节框架层面的架构讲完了下面进入真正见血的部分——那些论文摘要里不会写、但实操中每个都能让你折腾一周的细节。这里我挑了三个最有代表性的缺陷样本怎么构造、模型生成参数怎么调、回归验证怎么承受得住。3.1 故障注入与崩溃数据构造做 APR 研究的先决条件是有足够多的缺陷样本。但真实数据库项目的 bug 不像 Web 应用那样随手一抓一大把而且很多缺陷一旦被修复原始触发条件就永久失效了。所以校企联合项目中我们花了不少精力在样本构造和复现上。最有效的来源是历史缺陷库。企业版本的数据库项目经过常年的 Fuzz 测试和用户反馈积累了数千个已修复缺陷的完整记录包括触发 SQL、崩溃堆栈、修复前后的代码快照。这些数据是 DeepK 训练知识库和评估集的金矿。但原始记录往往是冗余且混乱的需要人工清洗——把触发条件抽成最小可复现用例把崩溃堆栈裁剪到与根因最相关的 30 行以内。另一个重要手段是主动注入缺陷。我们从生产代码里挑选出一些修复过的关键函数把修复补丁逆向删除或者故意改错人为制造新的缺陷样本。这种做法有一个先天优势我们知道正确答案就是原始修复后的代码可以自动评估模型补丁的正确性。但它的缺点也很明显注入的缺陷在模式上可能与真实 bug 分布有偏差所以我们在评估集设计时把真实缺陷和注入缺陷严格分开统计防止注入样本的高修复率掩盖真实场景的低可用性。缺陷数据构造完成后标准化格式很重要。DeepK 内部采用统一的缺陷元数据格式包含五个字段repo_version仓库版本、trigger_case最小触发用例、stack_trace异常堆栈、buggy_fn_list可疑函数列表、golden_patch正确修复补丁仅供训练和评估使用。有了这种标准化格式后续的检索、评估、比对才有统一基准。3.2 修复补丁生成的关键参数大模型生成补丁看起来就是“写提示词、调 API”但里面的参数设置能直接决定修复率的天花板。这里分享几个我们调参摸索下来的关键参数。温度系数temperature最敏感。我们测过 0.1 到 1.2 的完整区间发现 0.3 附近效果最稳。温度太低模型输出保守经常生成“啥也没改”的空补丁温度太高输出天马行空频繁出现并发语义完全错误的改动。0.3 这个值能让模型在“基于示例做局部修改”和“探索性修复”之间达到相对好的平衡。需要说明的是这个结论基于数十轮实验的结果具体项目仍建议自己做小规模基线测试。top_p 我们固定在 0.9候选数量 n 设置为 8。也就是说每次请求会并行生成 8 条候选补丁然后统一进入验证流水线按批处理。n 值不是越大越好因为验证阶段是瓶颈一次性生成太多候选会导致后续验证排队整体修复耗时反而拉长。8 这个值是在吞吐量和验证吞吐之间取的一个合理平衡。还有一个容易忽略的参数是 max_tokens。数据库代码里一个函数动辄几百行如果补丁涉及重构输出长度会很大。我们最初把 max_tokens 设置为 1024结果很多有修复思路的补丁被硬生生截断静态验证直接报语法错误。后来调整到 2048截断率下降了一半还要多。但如果设到 4096模型开始频繁“凑字数”输出里出现大量无意义的空行和重复注释反而干扰验证。所以 2048 是个比较合适的值。3.3 回归验证与性能评估怎么做验证流水线的核心是管理有限的计算资源。数据库中一次完整回归可能要跑半小时以上如果每次修复候选都跑完整回归那整个框架的效率会低到没法用。DeepK 采用三级验证策略。第一级是静态检查包括在容器内编译补丁后的项目。数据库内核的编译通常需要几分钟这已经是流水线里最重的单步操作。我们做了增量编译优化只编译受补丁影响的模块能把编译时间压缩到 40% 左右但如果补丁修改了头文件或数据结构的定义就必须全量编译了。第二级是定向测试集也就是与缺陷模块强相关的测试。定向测试集的选择很有讲究不能只看测试文件路径是不是与被改动代码相邻还得看测试内部引用了哪些接口。我们的做法是建立模块与测试用例的映射关系表每个可疑函数关联到一组已知覆盖它的测试用例定向测试就从这份关联表里取。这一步执行时间通常在 1-3 分钟。第三级是完整回归加性能快照。只有前两级全部通过后才会触发完整回归。完整回归除了验证功能正确还同时跑性能快照对比——数据库内核绝不能因为一个修复就把关键查询性能拖累超过 5%。我们设定了一个自动化脚本解析性能测试结果把超阈值的补丁直接标红并打回修复循环附上性能回退的指标差值和采样日志。整个验证流水线的耗时结构大致是静态检查 5 分钟定向测试 2 分钟完整回归 30 分钟。如果一条候选补丁如果能连续通过三级验证最终人工审查基本都是一次过。这个流水线听起来琐碎但它是 DeepK 能走向论文实验和实际交付的共同底座。4. 校企联合研究的组织与推进经验校企联合研究项目最大的风险往往不在技术上而在两边团队的预期管理和协作模式上。这里想聊聊我们用真金白银换来的几条组织经验。4.1 需求从哪来成果到哪去校企联合研究最容易犯的错误是企业把学校团队当外包用而学校团队把企业当数据提款机。DeepK 能走通靠的是建立了清晰的“需求源”和“成果归口”机制。需求源方面企业团队每周整理一批来自生产环境的真实缺陷案例这些案例不直接给学校团队用作训练数据而是先经过一轮脱敏和技术拆解把问题抽象成学术研究命题。比如“某并发事务调度场景下出现丢失更新”的案例抽象出的研究问题可能是“如何在分布式事务调度中识别丢失更新模式的缺陷特征”。这样一来学校团队的研究方向始终跟真实痛点绑定而不是闭门造车。成果归口方面学校的算法原型会被企业团队做两层评估。第一层是算法效果评估在固定基准集上对比 DeepK 与基线方法的表现第二层是工程可行性评估看算法输出的补丁是否满足代码规范、是否能通过完整 CI 流水线、是否有性能隐患。只有两层评估都通过的原型才会被纳入企业内部的工具链试点。这个机制保证了学术成果和工程落地的转化路径清晰可见。4.2 论文实验设计的三个坑做 AI4SE 方向的论文实验设计直接决定论文能否中稿。DeepK 的实验设计前前后后改了三版踩过的坑我挑三个最典型的分享。第一个坑是基线方法选择失衡。我们最初对比的基线只选了经典的模板修复方法和一个通用的代码生成模型被审稿人指出“缺少与同类 AI 修复框架的对比”。后来我们重新收集了两个开源的 APR 工具在相同的缺陷集上跑了一遍基准测试。对比结果其实对我们有利——DeepK 在数据库场景的修复率比通用方案高出很多——但如果没有补齐对比实验再好的结果都难以让人信服。第二个坑是评估集污染。我们在早期实验中不小心把部分缺陷样本的修复补丁也放进了训练数据导致模型在评估集上表现得异常优秀。及时发现这个问题后我们重建了评估集保证训练集与评估集完全隔离同一仓库版本的缺陷样本绝不交叉。这一点对 APR 研究来说尤其致命因为大模型有极强的记忆能力一旦见过答案评估结果就基本失去意义。第三个坑是消融实验不够细。我们早期版本的消融只做了“有知识注入 vs 无知识注入”两组说服力有限。后来拆成四个维度分别做消融缺陷定位模块开与关、知识库检索开与关、对偶测试生成开与关、静态检查反馈开与关。每一组消融都跑完整评估流程并记录耗时和修复率的差异。这样审稿人就能清晰看到每个模块的真实贡献度而非堆一个端到端结果。4.3 团队协作与代码交接心得校企联合项目里人员的流动性非常高。研究生会毕业企业工程师会转岗代码和技术决策如果在单一角色手里项目就会变得极其脆弱。DeepK 项目的代码交接和知识沉淀是我们后期维护的重点。我们坚持两个规则。第一所有关键设计决策必须写成设计文档哪怕只是半页 A4 纸的简短说明也要记录当时为什么选这个方案、放弃那个方案。技术债可以还设计决策丢失才是真正的灾难。第二代码仓库严格执行提交信息规范每个 merge request 必须注明关联的缺陷编号、实验批次和验证结论。这样新加入的成员可以凭 commit 历史在半小时内还原一个模块的完整演进脉络。另一个很实用的技巧是建立“每周轮值”机制。每周让一名团队成员负责处理另一个模块的 issue 和代码评审即使前期效率不高也必须坚持。经过两三个月团队里每个人都对系统全貌有了基本认知再遇到核心人员离开项目也能保持惯性推进而不至于失控停摆。5. 常见问题与排查技巧实录研究过程中积累了大量调试经验这里挑几个出现频率高、又不太容易在网上找到答案的问题按场景整理成速查表。5.1 模型输出乱改 API 怎么办这是最常遇到的问题。模型生成补丁时会对项目内部的 API 做“想象性重构”比如把某个get_table_meta(handle)改成get_table_meta(handle, true)但函数本身根本没有第二个参数。这种补丁在静态检查阶段就会被拒但被拒次数过多会拖累修复循环效率。应对策略有两个层面。技术层面我们在提示词中显式加入了一份本次修复允许调用的 API 签名清单只列出与可疑代码相邻模块的接口。模型看到明确的白名单后乱改 API 的现象大幅减少。管理层面如果同一类 API 错误反复出现我们会把这批错误案例收集起来在知识库中标记为“高危误用模式”在后续检索中优先推送给模型作为反面示例。经验上展示三个以上反面示例的效果优于单纯的正向约束。5.2 验证用例泄漏导致误报“修复成功”的假象比修复失败更可怕。有一段时间DeepK 在评估集上的修复率高达 65%但人工复核后发现超过三分之一的“成功修复”存在验证用例泄漏——模型生成补丁时间接接触到了测试代码信息生成的补丁对特定用例做了过拟合。排查过程很痛苦。最终我们加入了一个简单的防泄漏机制把测试文件路径和测试用例内容从模型上下文中完全屏蔽只允许提供“测试失败堆栈 失败的断言描述 相关业务模块的说明”。这样模型只能在语义层面理解问题而不能直接参考具体断言。改动之后评估集修复率降到了 41%但人工复核通过率几乎翻倍。这也是一个重要的经验指标的真实性比指标数值本身更重要。5.3 跨版本代码漂移问题数据库项目版本迭代极快今年写的缺陷知识库明年可能因为某次大规模重构而失效。代码漂移会让检索模块召回一堆过时代码片段反而干扰模型判断。我们的处理方式是给知识库中的每个缺陷样本加上“适用版本范围”字段检索时先做版本过滤。当发现某条历史案例在当前版本上无法正常编译或测试时自动降权处理。另外一个补充手段是持续跟踪代码变更日志。每当仓库有涉及关键模块的重构提交脚本会自动生成一份变更摘要并推送到知识库管理后台由人工决定哪些历史案例需要淘汰、哪些需要重新标注。这样知识库始终与代码演进保持同步检索准确率才能长期稳定在较高水平。6. 后续还能往哪些方向扩展最后说说我对 DeepK 后续发展的观察。基于我在项目中的体验这个框架的潜力不在“修更多 bug”而是在“修复数据的反哺”上。每次修复闭环产生的缺陷样本、失败用例、补丁对比本身就是高质量的训练语料。如果把一定量的修复历史回灌到模型的持续学习中理论上可以形成“越修越准”的正循环。这不是遥远的设想而是我们在内部已经跑通的实验形态只是还未到公开发布的程度。另一个值得探索的方向是把 DeepK 从“修复已报告的缺陷”延伸到“主动探测潜在缺陷”。配合变异测试和模糊测试让框架在常规测试通过后继续尝试寻找隐蔽边界把 APR 从被动响应变成主动防御。这个过程需要大量的算力和精细的验证设计但方向已经很明确。真实操作系统摩擦过才知道自动修复真正稀缺的不是模型能力而是对业务场景的理解深度和验证工程的控制力。如果这篇文章能帮准备入局 AI4SE 的读者少踩几个坑或者给校企合作中的研究者提供一点协作思路那这些经验就没白写。