PatchHolmes: Agentic Patch Retrieval via Listwise Selection
Guanqun Yang, Yingming Zhou, Jiangrui Zheng, Shudong Hao, Xueqing Liu
cs.IR, cs.SE
2026-09-30
PatchHolmes 用四工具 agent 通读 top-100 候选再精选修复 commit,Recall@1 达 59.95%,超 pointwise 基线 25 个点,token 成本约为其 1/7。
每个公开披露的漏洞(CVE)都该有一条映射:上游仓库里哪个 commit 修的它。这条 patch link 是安全通告、CVSS 评分、SBOM 扫描的共同输入,但 GitHub Advisory Database 和 NVD 里 60% 到 63% 的 CVE 缺这条链接。人工补不动,NVD 自己还背着元数据积压,Apache Rave 一个漏洞的补丁拖到披露十年后才被找到。
任务难在三处:仓库大,49% 的 CVE 对应超过 5000 个 commit 的仓库;diff 长,GitHubAD 语料平均约 15000 个 token,远超 BERT 类编码器 512 token 的窗口;词表几乎不重叠,CVE 描述写「buffer overflow」,修复 commit 写「out-of-bounds read」,词面匹配直接失效。
已有两代方案各有卡点。PatchFinder、SPFinder 这条传统线是开环的,静态特征打一次分就定排名,不能细看候选或回头重来。最新的 Favia 换成 LLM 判断,结构仍是 pointwise:对 10 个候选各问一次「是不是修复」,10 次调用互相看不见。多个候选都答「是」时,排名只能靠候选顺序。论文举了 CVE-2015-9251(jQuery 跨域 Ajax 引发的 XSS):Favia 给 10 个候选全部打上置信度 5 的「是」,选中排第一的重构 commit,真正的修复排第九。
PatchHolmes(AACL-IJCNLP 2026 接收)分两段。
Phase 1 管召回,两条检索路并行,RRF(k=60)融合成 top-100:
Phase 2 管精度,四工具 agent 走 survey-inspect-drill-submit 流程:listcandidates 一次返回全部 100 个候选的一行清单(排名、commit id、message 首行、文件改动统计),是唯一能整看列表的调用;readcommit 读单个 commit 的 message、文件清单和 8000 字符预算内的 diff;readfilediff 取单文件 diff,预算翻倍到 16000 字符,用于钻取被截断的文件;submitanswer 提交唯一答案附理由。agent 跑在 OpenHands SDK 上,上限 15 轮,典型读 3 到 10 个候选就交卷。
核心动作是 listwise:先看全清单再决定读谁,候选之间互相比,不走各自独立打分的老路。工具刻意只留四个,任务不改代码、候选集已固定,最小充分集就够。
GitHubAD 的 809-CVE 子集,Qwen3-235B 骨干:
| 方法 | Recall@1 | Recall@10 |
| PatchHolmes | 59.95% | 77.26% |
| Favia(pointwise LLM) | 34.61% | 72.31% |
| IRCoT(retrieve 后接 CoT) | 28.55% | 28.55% |
| SPFinder(最强非 agentic) | 27.32% | 70.36% |
McNemar 精确检验 p = 2.4e-44,bootstrap 置信区间不重叠。看形状:Favia 的 Recall@10 只落后 4.95 个点,输的是 rank-1 精度,正是独立打分的结构性弱点。
归因实验把检索和选择拆开。固定用同一套 top-100:直接取排名第一是 32.63%,加 agent 做到 59.95%,循环单独贡献 27.32 个点。换骨干的影响小得多,Qwen3 家族内 235B 与 30B 只差 0.86%,gpt-oss-120B 拿 56.98%、gpt-oss-20B 拿 49.81%,都远高于 32.63% 的无 agent 下限。收益来自循环,不来自模型规模。
工具消融是一架梯子:无 agent 32.63%;加 listcandidates 和 submitanswer 到 48.21%,单步最大(+15.58 个点),是 listwise 视图最直接的证据;补 readcommit 到 57.48%;再补 readfilediff 到 59.95%。
跨语料:同一个 agent 一行不改,搬到 PatchFindertop10(1252 个 CVE,候选由 PatchFinder 预筛),Recall@1 从 PatchFinder 自家排序的 24.28% 提到 39.86%。这份数据有硬上限,675 个 CVE 的候选池里没有 gold,单选方法封顶 46.10%,PatchHolmes 恢复了到上限距离的 71.40%。
成本按 CVE 计:Qwen3-235B 平均 96118 个输入 token、8 次工具调用、读 5 个 commit、86 秒,约 0.007 美元;8401 个 CVE 全量一遍约 58 美元,Favia 同口径约 400 美元。token 省 7 倍,Recall@1 高 25.34 个点。换 Qwen3-Coder-30B,单 CVE 39 秒,精度只让 0.86 个点。
对漏洞管理、SBOM、安全通告流水线,系统可以直接落地:冻结开源模型、本地 Git 仓库、不微调、不调外部搜索 API,单 CVE 不到一美分。对六成缺链接的存量提供了成本可承受的补法;不过评测只在补丁已被找到的子集上做过,外推到最难样本要打折。
对做 agent 系统的人,结论更通用:固定候选集上循环值 27 个点,同家族约 7 倍规模的骨干切换只值 0.86 个点,瓶颈在交互界面设计,不在挑哪个模型。listwise 优于 pointwise,大概率也适用于其他从固定候选池选一个的任务,比如缺陷定位、重复 issue 归并。赢 IRCoT 的分量有限,它为开放网页多跳 QA 设计,512 token 的 e5 检索器在这里把 diff 截没了;有分量的对照是 Favia 和固定候选集消融。
论文自己披露三条:评测只能在补丁已被找到的 37% 到 40% 的 CVE 上做,最难的样本在所有 benchmark 里缺席,35 个真实 CVE 的补充实验(60.0%)与主实验一致,但范围限制仍在;7.28% 的 CVE 有多个修复 commit,只有被记录的才算对,选了未记录但同样有效的修复会被记错;gpt-oss 骨干的提交率只有 70% 和 54%(Qwen3-235B 为 90.5%),推理模型常撞 15 轮上限,未提交就退回 Phase 1 排序。
读下来另有疑点。主实验是为省成本从 8401 个 CVE 抽的 809 子集,9 项分布检验过了 6 项,commit 数和 diff token 的尾部没对齐,难例恰恰堆在尾部。Recall@1 不到 60%,十个 CVE 仍有四个选错,离无人值守还有距离。系统的天花板还取决于 gold 是否落进 Phase 1 的 top-100,这个召回率论文没有报告。