SWE-bench:用真实 Issue、补丁与测试评测编码 Agent

原始来源

Carlos E. Jimenez 等,ICLR 2024:SWE-bench: Can Language Models Resolve Real-World GitHub Issues?,本页按 arXiv v3 阅读。

本页是原创学习导读,不含原文复刻;论文版权与完整内容以原始来源为准。

学完后你应该能做到什么

读完本篇,你应该能够:

  1. 用问题描述 、代码库快照 、测试补丁 和参考修复 表述一个 SWE-bench 任务。
  2. 说明真实 Issue 和合并 Pull Request 怎样经过筛选、拆分与执行验证,变成可评测样本。
  3. 准确定义 FAIL_TO_PASS 与 PASS_TO_PASS,并解释为什么两组测试必须全部通过。
  4. 从 base commit 开始,追踪候选补丁的应用、测试执行、日志解析和最终判分。
  5. 区分“补丁与维护者答案不同”“补丁能应用”“测试通过”“真实工程上完全正确”四件不同的事。
  6. 解释仓库版本、依赖环境、评测 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 必须已经合并,并且:

  1. 明确关联并解决一个 GitHub Issue。
  2. 修改测试文件,表明维护者为问题提供了可执行行为检查。

问题描述会聚合相关 Issue 的标题、正文和评论,但只使用早于 PR 首次提交的内容,降低把解决方案讨论泄漏进题面的风险。

阶段三:按执行结果筛选

对每个候选,构建可执行环境并回到 base commit:

  1. 安装当时版本的代码库。
  2. 应用测试补丁 ,运行相关测试,得到修复前日志 log_pre。
  3. 再应用参考修复 ,运行同一组测试,得到修复后日志 log_post。
  4. 保留能够安装、补丁可应用、测试可执行,并至少存在目标测试从失败转为通过的样本。

原 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_PASSPASS_TO_PASS判定
没修到问题仍有失败全通过未解决
修了新例子但造成回归全通过至少一个失败未解决
测试脚本崩溃或目标测试没运行缺失/非通过不确定未解决
同时恢复目标和保留旧行为全通过全通过Resolved

FAIL_TO_FAIL 或 PASS_TO_FAIL 不进入原论文的核心解题判分。这不代表它们没有诊断价值,只是基准选择了前两类定义目标合同和回归合同。

候选补丁如何被执行和判分

对每个 ,评测流水线大致执行:

  1. 清理工作区并检出任务的 base commit,恢复 。
  2. 激活该仓库版本对应的可执行环境并安装代码库。
  3. 应用测试补丁 。
  4. 应用候选补丁 ;论文实现会对部分格式问题尝试修复后重试应用。
  5. 运行由 决定的测试命令,记录日志。
  6. 用仓库专用解析器把日志转换为“测试 → 状态”的映射。
  7. 检查两组目标测试是否全部存在并通过,得到 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 应该通过怎样的计算机接口观察和修改仓库”。

自测:确认你真的掌握了机制

  1. 、、、 和 分别是什么?哪些给模型解决问题,哪些主要留在评测侧?
  2. 为什么只要求 FAIL_TO_PASS 通过会奖励危险的修复?
  3. 一个候选补丁与参考补丁逐行不同,什么条件下仍应判为解决?
  4. 原论文怎样从修复前后日志构造两组测试?缺失目标测试为什么算失败?
  5. 比较两个 SWE-bench 结果时,至少要核对哪五类版本或配置?

上一篇:MCP 发布文章 · 下一篇:SWE-agent · 返回 里程碑原典目录