论文精读 05|SWE-bench:用真实仓库与测试验证软件 Agent

论文档案

项目内容
论文SWE-bench: Can Language Models Resolve Real-World GitHub Issues?
作者Carlos E. Jimenez 等
发表ICLR 2024
原文OpenReview / arXiv
代码与数据princeton-nlp/SWE-bench
本文定位把代码 Agent 的成功条件从“生成像补丁的文本”提升为“真实仓库测试通过”

为什么它改变了软件 Agent 研究

早期代码生成常给模型一个函数签名和短描述,再用隐藏单元测试评估。真实软件维护完全不同:issue 用自然语言描述,相关实现分散在数十万行代码中,修复要理解项目惯例,还不能破坏已有行为。

SWE-bench 把一个任务定义为:

GitHub issue + 指定仓库 base commit
              │
              ▼
模型读取仓库并生成 patch
              │
              ▼
在可重建环境中应用 patch
              │
              ▼
运行 fail-to-pass 测试 + regression tests
              │
              ▼
全部通过才记为 resolved

关键跃迁是 evaluator 不再询问模型“你修好了吗”,也不靠另一个 LLM 看补丁像不像,而是执行真实测试并检查环境终态。

数据集怎样从 GitHub 历史构造

三阶段筛选

作者从约 9 万个 pull request 出发,经过三类约束:

  1. 仓库与 PR 抓取:选择活跃、可安装的 Python 项目,关联 issue 与合并 PR;
  2. 测试关联:要求 PR 不只是修改代码,还增加或修改测试,并能找到至少一个能证明 issue 被修复的测试;
  3. 执行过滤:在 base commit 上安装依赖、运行测试,确认测试在旧代码失败、应用 gold patch 后通过。

最终测试集包含 2,294 个 issue,来自 12 个流行 Python 仓库。训练集另从 37 个互不重叠仓库构造约 19,000 个实例;论文用于微调时进一步筛到 10,000 个上下文不超过 30k token 的实例。

任务为何难

论文统计一个 issue 平均约 195.1 个词,代码库平均约 43.8 万行;参考补丁平均修改 1.7 个文件、3 个函数、32.8 行。目标改动并不大,真正困难的是从巨型仓库中找到它。

每个任务中位数有 51 个额外回归测试,约 40% 的实例有两个或更多 fail-to-pass 测试。也就是说,“让一个测试变绿”不代表没有引入回归。

评估协议:Resolved 到底意味着什么

对于预测 patch,harness 需要完成:

  • 在正确 base commit 上重建环境;
  • 应用 patch,记录是否能干净应用;
  • 运行 fail-to-pass tests,确认目标 bug 被修复;
  • 运行 pass-to-pass tests,确认已有行为没有回归;
  • 处理超时、依赖失败与基础设施错误;
  • 汇总为单个 % Resolved 指标。

因此至少要区分四个结果:

状态含义
patch 无法应用输出连语法/上下文条件都没满足
patch 可应用、测试未通过格式正确但功能错误
目标测试通过、回归失败修了局部问题却破坏其他行为
全部测试通过按基准定义 resolved

论文中可应用 patch 的比例远高于 resolved rate,直接证明“生成 diff”与“解决 issue”是两个层次。

基线系统如何工作

原论文还没有今天常见的长程 coding agent。基线主要是一次性生成补丁:

  1. 用 BM25 从仓库检索与 issue 相关的文件;
  2. 在 13k、27k 或 50k token 预算内拼接代码;
  3. 把 issue 和代码交给模型;
  4. 让模型一次输出 patch;
  5. 应用并执行测试。

还设置 Oracle retrieval:直接给模型参考补丁涉及的文件,用于估计定位瓶颈。论文也训练 SWE-Llama 7B/13B,对 Llama 系列注意力层做 LoRA 微调。

关键结果的正确读法

论文初版结果非常低:Claude 2 在 BM25 检索设置下约 1.96% resolved,ChatGPT 约 0.17%,SWE-Llama 13B 约 0.70%。后续论文版本表中还包含 Claude 3 Opus 约 3.79%、GPT-4 Turbo 约 1.31%。

Oracle 文件显著提升但远未解决问题:Claude 2 约 4.8%,在进一步压缩无关代码的 oracle-collapsed 设置中约 5.93%。这说明:

  • 定位是大瓶颈;
  • 即使知道正确文件,理解 issue、设计改动和验证仍很难;
  • 更长上下文不等于更高成功率。

BM25 在 27k token 设置下约有一半任务没有检索到任何参考补丁文件。另一方面,扩大到 50k 也可能因噪声而降低性能。问题不只是“窗口够不够长”,而是上下文选择是否精确。

数据集背后的 Harness 设计

环境快照是一等资产

任务不能只保存 issue 文本,必须绑定仓库 commit、依赖、操作系统、测试命令和超时。任何一项漂移都会让同一 patch 得到不同结果。

成功判定必须独立

Agent 可以请求提交,但无权决定成功。Harness 接管 patch、在隔离环境运行测试,并将结构化结果反馈给 Agent 或基准报告。

观察要支持定位,而不是倾倒仓库

一次把完整代码库送进上下文既昂贵又嘈杂。适合 Agent 的接口应提供文件搜索、符号搜索、局部查看、测试运行和增量 diff。SWE-agent 后来正是沿这个方向改造 ACI。

测试是证据,也不是绝对真理

测试覆盖可能不足,参考补丁也未必唯一。某个预测 patch 通过现有测试,不代表生产质量完全正确;失败也可能来自环境构建问题。评测报告要保留测试明细与基础设施错误,而不只是一个二元标签。

数据泄露与时效性

SWE-bench 来自公开 GitHub 历史,模型可能在预训练中见过 issue、PR 或补丁。论文检查 issue 时间和性能关系,但无法完全排除污染。随着基准流行,公开测试集更容易进入后续训练数据。

复现或做新比较时应:

  • 使用精确、可审计的模型版本;
  • 报告 cutoff 与任务创建时间;
  • 优先加入更新、更难或私有保留集;
  • 检查输出是否复制 reference patch 的异常特征;
  • 不把排行榜增量自动解释为泛化能力提升。

一个现代复现协议

完整 2,294 题成本很高,可先做分层样本:

  1. 按仓库、issue 长度、patch 文件数和测试数分层抽取 50–100 题;
  2. 为每题构建固定容器镜像和依赖缓存;
  3. 记录模型、harness commit、prompt、工具 schema、预算和随机种子;
  4. 每题至少运行 3 次,报告 resolved、apply rate、测试错误和成本;
  5. 保存每步搜索、打开文件、编辑、测试与最终 diff;
  6. 失败归类为定位、理解、实现、编辑、验证、预算或环境;
  7. 把基础设施失败从模型失败中单独剥离。

若比较两个 harness,必须使用相同模型、任务样本、预算、容器和重试策略。否则所谓 Harness 增益可能只是更多 token 或更多尝试。

论文局限

  • 仅覆盖 12 个 Python 仓库,不能代表所有语言与开发流程;
  • PR 与测试筛选偏向有良好测试文化的项目;
  • issue 可能含图片、链接或上下文,纯文本抽取会丢信息;
  • 依赖和测试环境复杂,基础设施噪声不可忽略;
  • 公开历史带来训练污染风险;
  • % Resolved 不衡量代码可维护性、安全性和潜在未测回归;
  • 原论文的一次性生成基线不代表现代交互式 coding agent 上限。

SWE-bench 最重要的结论不是“模型只能解决 1% 的问题”,而是建立了一个此后可持续改进的执行式契约:给定真实仓库状态,Agent 必须产生可应用、可测试且不回归的改变。这个契约后来成为 coding harness 的核心验收面。

延伸阅读