SWE-bench:用真实 Issue、补丁与测试评测编码 Agent
原始来源
Carlos E. Jimenez 等,ICLR 2024:SWE-bench: Can Language Models Resolve Real-World GitHub Issues?,本页按 arXiv v3 阅读。
本页是原创学习导读,不含原文复刻;论文版权与完整内容以原始来源为准。
学完后你应该能做到什么
读完本篇,你应该能够:
- 用问题描述 、代码库快照 、测试补丁 和参考修复 表述一个 SWE-bench 任务。
- 说明真实 Issue 和合并 Pull Request 怎样经过筛选、拆分与执行验证,变成可评测样本。
- 准确定义
FAIL_TO_PASS与PASS_TO_PASS,并解释为什么两组测试必须全部通过。 - 从 base commit 开始,追踪候选补丁的应用、测试执行、日志解析和最终判分。
- 区分“补丁与维护者答案不同”“补丁能应用”“测试通过”“真实工程上完全正确”四件不同的事。
- 解释仓库版本、依赖环境、评测 Harness 和数据污染怎样改变结果,避免脱离版本引用排行榜数字。
为什么短函数题不够评测编码 Agent
许多传统代码基准给一个函数签名和几条测试,答案只需几行。真实仓库维护通常复杂得多:Issue 使用项目术语描述症状,错误可能来自另一个文件,修复需要理解调用链、兼容旧行为、遵循项目风格并运行特定版本依赖。
因此,SWE-bench 把问题从“能不能续写一段看起来像代码的文本”改成:
给定历史 Issue 和问题发生时的代码库,生成一个可以应用的补丁;在可复现环境中运行真实测试,判断它是否修复目标行为且没有破坏选定的既有行为。
这一定义同时考察模型和围绕模型的系统:上下文检索、仓库导航、编辑接口、执行环境、错误反馈和重试策略都会影响最终解决率。SWE-bench 因而也成为后来编码 Agent 与 Harness 设计的重要压力测试。
一个任务到底包含什么
论文用四个核心对象描述任务:
| 符号 | 含义 | 从哪里来 | 评测时怎样使用 |
|---|---|---|---|
| Problem statement,问题描述 | 关联 Issue 的标题、正文和在 PR 首次提交前出现的评论 | 提供给模型,说明要改变什么 | |
| Codebase,代码库快照 | 原 PR 所基于的 base_commit | 检出该提交,让模型在历史现场修改 | |
| Test patch,测试补丁与相关测试 | 原 PR diff 中测试文件的改动 | 评测方应用,用来重现修复前后行为 | |
| Gold/reference patch,维护者参考修复 | 原 PR diff 中非测试部分 | 构造和验证任务,不要求候选补丁逐行模仿它 |
模型或 Agent 的输出记作候选补丁 。评分关心 在测试上的行为,而不是它与 的文本相似度。不同实现只要满足同一行为合同,也可能得到正确判定。
测试补丁不是给模型抄答案
基准的核心输入是 与 ; 和 属于评测构造与执行侧。论文把
test_patch描述为用于判定的未见测试补丁。现实评测还要明确 Agent 是否能运行仓库已有公开测试,不能把“可运行公开测试”与“看到了隐藏判分测试”混为一谈。
基准怎样从 GitHub 历史构建出来
arXiv v3 描述的完整 SWE-bench 集合包含 2,294 个任务,来自 12 个流行 Python 仓库。这个数字来自一条三阶段流水线,不是任意抓取 2,294 条 Issue。
阶段一:选择仓库并抓取 Pull Request
作者选择 12 个主要由 Python 构成的流行开源仓库,从大约 90,000 个 PR 开始。每个 PR 天然给出 base commit、变更 diff、相关 Issue 和时间信息。
阶段二:按属性筛选
候选 PR 必须已经合并,并且:
- 明确关联并解决一个 GitHub Issue。
- 修改测试文件,表明维护者为问题提供了可执行行为检查。
问题描述会聚合相关 Issue 的标题、正文和评论,但只使用早于 PR 首次提交的内容,降低把解决方案讨论泄漏进题面的风险。
阶段三:按执行结果筛选
对每个候选,构建可执行环境并回到 base commit:
- 安装当时版本的代码库。
- 应用测试补丁 ,运行相关测试,得到修复前日志
log_pre。 - 再应用参考修复 ,运行同一组测试,得到修复后日志
log_post。 - 保留能够安装、补丁可应用、测试可执行,并至少存在目标测试从失败转为通过的样本。
原 PR 的 diff 会按测试相关路径拆成 和 。这是可扩展的工程启发式,而不是对“测试代码”与“产品代码”语义的完美理解;数据质量仍依赖仓库结构、测试覆盖和解析规则。
flowchart TD A["约 90,000 个真实 PR"] --> B["关联并解决 Issue?"] B -->|"否"| R1["移除"] B -->|"是"| C["包含测试改动?"] C -->|"否"| R2["移除"] C -->|"是"| D["拆分 test patch T 与 reference patch δ"] D --> E["检出 base commit C 并安装对应环境"] E --> F["仅应用 T,运行得到 log_pre"] F --> G["再应用 δ,运行得到 log_post"] G --> H{"可执行且存在 fail → pass?"} H -->|"否"| R3["移除"] H -->|"是"| K["进入 2,294 个任务的完整集合"]
两组测试怎样定义“解决”
比较参考修复前后的测试状态,可以把相关测试分组。SWE-bench 的核心判分使用两组:
FAIL_TO_PASS:目标行为真的被修复了吗
这组测试在只有 base commit 与测试补丁时失败,在再应用参考修复后通过。它们刻画 Issue 希望改变的行为。候选补丁必须让所有这类测试通过,否则目标问题至少有一部分仍未解决。
PASS_TO_PASS:已有行为是否被破坏
这组测试在参考修复前通过,应用参考修复后仍通过。它们不是目标缺陷本身,而是选定的回归保护。候选补丁必须继续通过这些测试,避免用“删掉校验”“写死一个输入”之类方式修好新例子却破坏旧功能。
论文的判定非常严格:所有 FAIL_TO_PASS 和 PASS_TO_PASS 测试都必须在候选补丁上被找到且状态为通过;测试缺失或任何非通过状态都算失败。
| 候选结果 | FAIL_TO_PASS | PASS_TO_PASS | 判定 |
|---|---|---|---|
| 没修到问题 | 仍有失败 | 全通过 | 未解决 |
| 修了新例子但造成回归 | 全通过 | 至少一个失败 | 未解决 |
| 测试脚本崩溃或目标测试没运行 | 缺失/非通过 | 不确定 | 未解决 |
| 同时恢复目标和保留旧行为 | 全通过 | 全通过 | Resolved |
FAIL_TO_FAIL 或 PASS_TO_FAIL 不进入原论文的核心解题判分。这不代表它们没有诊断价值,只是基准选择了前两类定义目标合同和回归合同。
候选补丁如何被执行和判分
对每个 ,评测流水线大致执行:
- 清理工作区并检出任务的 base commit,恢复 。
- 激活该仓库版本对应的可执行环境并安装代码库。
- 应用测试补丁 。
- 应用候选补丁 ;论文实现会对部分格式问题尝试修复后重试应用。
- 运行由 决定的测试命令,记录日志。
- 用仓库专用解析器把日志转换为“测试 → 状态”的映射。
- 检查两组目标测试是否全部存在并通过,得到
Resolved或Not resolved。
def evaluate(instance, candidate_patch):
repo = checkout(instance.repo, instance.base_commit)
env = activate_environment(instance.version)
install(repo, env)
apply_patch(repo, instance.test_patch)
if not apply_or_repair_patch(repo, candidate_patch):
return "not_resolved"
log = run_repository_tests(repo, instance.test_patch)
status = parse_with_repo_specific_rules(log)
required = instance.FAIL_TO_PASS + instance.PASS_TO_PASS
if all(status.get(test) == "PASS" for test in required):
return "resolved"
return "not_resolved"注意:补丁能应用只是流水线中间状态,不代表行为正确;测试进程成功启动也不代表目标测试全通过。
走完一个简化案例
下面是教学用的虚构 pathkit 任务,用来展示判分语义,并非 SWE-bench 中的真实 Issue。
Issue 说:safe_join(root, child) 在 root 以斜杠结尾时错误拒绝合法子路径。base commit 中已有四个安全检查测试通过;PR 新增一个重现该问题的测试。
| 状态 | 新增目标测试 | 四个既有安全测试 |
|---|---|---|
| base commit + | 失败 | 全通过 |
| 再应用参考修复 | 通过 | 全通过 |
因此新增测试属于 FAIL_TO_PASS,四个旧测试属于 PASS_TO_PASS。
- 候选 A 直接删除所有路径边界检查。新增测试通过,但一个“禁止跳出 root”的旧测试失败,所以不能判为解决。
- 候选 B 先规范化末尾分隔符,再保留边界检查。两组测试都通过,因此可以判为解决,即使它的代码与维护者参考补丁不完全相同。
- 候选 C 修改测试配置,让新增测试根本没被收集。由于目标测试在日志映射中缺失,仍然算失败。
这个例子说明执行式评测比文本相似度更接近行为,但也只覆盖测试表达出来的合同。
仓库版本、环境与容器为什么是评分的一部分
同一补丁在不同 Python、依赖或系统库版本上可能得到不同结果。原论文不是简单地在“最新仓库”运行,而是按发布版本建立 conda 可执行环境,记录 Python、依赖和安装命令,在对应 base commit 上验证任务。
现代 Harness 可以进一步把环境封装到容器,但“用了 Docker”本身仍不等于可复现。至少要锁定:
- 数据集和任务实例版本。
- 仓库镜像与 base commit。
- Python、依赖锁和系统包。
- 容器镜像 digest 或确定的构建输入。
- 安装命令、测试命令和超时/资源限制。
- 测试补丁、日志解析器和评测 Harness 的版本。
- Agent 能看到的测试、网络、工具和重试预算。
因此两个“都在 SWE-bench 上”的结果,若数据子集、环境或 Harness 不同,就不是严格的同一实验。
证据该怎么看
论文 v3 的 2,294 个任务和 12 个 Python 仓库,证明作者确实把大规模真实 Issue–PR 历史转成了可执行评测集合。任务常要求跨函数、类和文件定位问题,远超自包含短函数生成。
论文的历史基线也显示当时难度很高:在论文指定的 BM25 检索与模型快照设置下,Claude 2 解决 1.96% 的任务。这个数字只用于理解 2023–2024 年的起点,不是当前排行榜结论。模型、Agent、数据集变体、清洗和 Harness 后来都可能改变;本篇不提供动态排名。
更稳妥的长期结论是:真实仓库修复的瓶颈不只在代码语法,还包括定位相关上下文、生成可应用补丁、管理执行环境和用反馈继续修改。
污染、版本和解释风险
1. 公开历史可能进入训练或检索
Issue、PR 和参考修复原本就在 GitHub。模型可能在预训练中见过,Agent 也可能通过联网搜索找到原 PR。时间切分或论文内“前后年份表现相近”只能提供有限信号,不能彻底证明没有污染。报告必须说明网络访问、检索范围和模型训练截止信息(若可得)。
2. 测试不等于完整规范
候选补丁通过两组测试,说明满足该任务的可执行判分合同,不保证性能、安全、代码风格、未覆盖平台或所有用户意图都正确。
3. 参考补丁也不是唯一答案
来自维护者实际合并的方案,是构造测试转移和理解任务的重要依据,但另一个更小或结构不同的补丁也可能正确。反过来,机械检索并复刻 可能得分,却不能证明 Agent 具备可泛化的软件工程能力。
4. 变体不能混报
完整集、Lite、Verified 或其他后续变体具有不同筛选与难度。比较时必须写明名称、版本、实例数、模型与 Agent 配置、采样预算、公开测试权限和 Harness。
常见失败模式与调试顺序
| 失败现象 | 常见根因 | 先检查什么 |
|---|---|---|
| 候选 patch 无法应用 | diff 上下文、路径或行号错误 | base commit 是否正确,补丁格式是否匹配 |
| 本地通过、评测失败 | 依赖、系统包、测试集或 Harness 不同 | 对齐环境镜像、测试补丁和解析器版本 |
FAIL_TO_PASS 全过但仍未解决 | PASS_TO_PASS 回归或目标测试缺失 | 查看两组完整状态,而不是只看新测试 |
| 模型读了大量代码却找错文件 | 上下文检索噪声、项目术语难以定位 | 检查检索召回与排序,使用执行反馈缩小范围 |
| 分数异常高但过程不可解释 | 可能检索到历史 PR 或测试泄漏 | 审计网络、搜索、训练污染和可见文件 |
| 同名榜单结果无法复现 | 数据或 Harness 版本未记录 | 要求完整评测清单与输出日志 |
历史贡献与今天的工程解释
论文的历史贡献是把真实 GitHub Issue、对应历史代码快照、维护者补丁和测试转移组织成大规模执行式编码评测,让“是否修好”由仓库行为而不是文本相似度决定。
今天的工程解释是:编码 Agent 的能力必须通过真实执行路径验收,任务定义、代码版本、环境、工具接口和反馈 Harness 都是评测对象的一部分。SWE-bench 分数衡量特定合同下的仓库修复能力,不等于完整软件工程能力,也不替代产品环境的安全和端到端验收。
它与下一篇 SWE-agent 的关系也很清楚:SWE-bench 定义“题目与判分”,SWE-agent 进一步研究“Agent 应该通过怎样的计算机接口观察和修改仓库”。
自测:确认你真的掌握了机制
- 、、、 和 分别是什么?哪些给模型解决问题,哪些主要留在评测侧?
- 为什么只要求
FAIL_TO_PASS通过会奖励危险的修复? - 一个候选补丁与参考补丁逐行不同,什么条件下仍应判为解决?
- 原论文怎样从修复前后日志构造两组测试?缺失目标测试为什么算失败?
- 比较两个 SWE-bench 结果时,至少要核对哪五类版本或配置?
参考答案与判断要点
- 是问题描述, 是 base commit 的代码, 是测试补丁, 是参考修复, 是候选补丁。模型主要依据 和 生成候选; 与 用于构造和执行评测。
- 候选可以删除校验、写死新样例或破坏旧接口来通过目标测试;
PASS_TO_PASS用于阻止选定的回归。- 只要补丁可应用,所有
FAIL_TO_PASS与PASS_TO_PASS都被执行且通过,就满足原判分合同,不需要文本匹配参考补丁。- 先在 base commit + 测试补丁上运行得到修复前状态,再应用参考修复得到修复后状态;fail→pass 和 pass→pass 分组。缺失意味着无法证明该合同被检查,按失败处理。
- 至少核对数据集变体/版本、实例集、仓库和环境、模型与 Agent 配置、工具/网络/测试可见性、采样预算以及评测 Harness。