SWE-bench — 架构与原理
30 秒导读: SWE-bench 是一个评测基准。它从知名开源仓库(Django、sympy、scikit-learn…)的真实 PR 里造题:每道题给模型一个仓库快照和一段 issue 描述,模型要生成一个
git补丁。评判方式不是看文本像不像标准答案,而是把补丁打进一个 Docker 容器、真的跑那个仓库的测试套件,看「该被修好的测试」是否全部转绿、且「原本就绿的测试」没被弄坏。
本项目较大(5 个子系统、三层镜像模型、多语言判分),按「由浅入深」拆成 1 个索引 + 4 章。先读本页建立全景,再按需下钻。
1. 这是什么(零基础也能懂)
一句话定义: SWE-bench 是「让大模型修真实软件 bug,并用真实测试自动判分」的评测基准 + 评测工具。
它想回答的问题: 论文标题就是问句——Can Language Models Resolve Real-World GitHub Issues?(语言模型能解决真实的 GitHub issue 吗?)。
给谁用:
- 模型 / agent 作者:想知道「我的编码 agent 在真实仓库上到底能修好多少 bug」。
- 研究者:想要一个不靠人工、可复现、难以靠背答案刷分的硬指标。
为什么「真实测试判分」很关键: 写代码这件事天然有「事实裁判」——单元测试。一个补丁对不对,不用人去读,跑测试就知道。SWE-bench 把这个裁判工程化了。
一道题(task instance)由什么组成:
| 字段 | 白话 |
|---|---|
repo + base_commit | 哪个仓库、在哪个提交点(给模型看的代码快照) |
problem_statement | issue 正文(要解决的问题描述) |
patch | 人类工程师当年合并的「标准答案」补丁(gold patch) |
test_patch | 当年随 PR 一起加的 测试改动(裁判用的测试) |
FAIL_TO_PASS | 修好之后「应当由失败转成通过」的测试列表 |
PASS_TO_PASS | 修改前后都「应当保持通过」的测试列表(防止改坏) |
字段的完整定义见 swebench/harness/constants/__init__.py:24-37(SWEbenchInstance)。
用起来什么样: 装好 Docker 后,一条命令就能拿「标准答案」验证整条评测链是否通:
python -m swebench.harness.run_evaluation \
--predictions_path gold \
--max_workers 1 \
--instance_ids sympy__sympy-20590 \
--run_id validate-gold
--predictions_path gold 表示「用 gold 补丁当作模型预测」——既能自检环境,也能确认这道题本身确实可解(README「Set Up」段)。
一句话直觉/类比: 把它想成给 AI 出的「真实工单 + 自动验收」。HR 不看你简历写得漂不漂亮,而是把你的代码改动直接部署进测试流水线,红变绿就算过、把别人弄红就算砸。
2. 顶层全景(它大概怎么转)
SWE-bench 仓库其实是两件事的合体:(A) 怎么把真实 PR 变成题目(数据侧),(B) 怎么给一份模型答案判分(评测侧)。评测侧是日常最常用、也是「SWE-bench」这个名字真正指代的价值核心。
2.1 五个子系统
| 子系统 | 干什么 | 在哪个目录 |
|---|---|---|
harness/ | 评测核心:把一道题变成可跑脚本、建 Docker 镜 像、打补丁、跑测试、判分 | swebench/harness/ |
collect/ | 数据采集:爬 GitHub PR → 过滤 → 造出 task instance | swebench/collect/ |
versioning/ | 给每个 instance 标注它属于仓库的哪个版本(决定用哪套安装/测试配方) | swebench/versioning/ |
inference/ | 跑模型生成补丁(OpenAI/Anthropic API 或本地 Llama),以及构造模型输入文本 | swebench/inference/ |
resources/ | 静态资源 | swebench/resources/ |
下面几章聚焦 harness/(评测)与 collect/(造题),这两块是理解 SWE-bench 的主干。
2.2 评测主线一张图
「怎么读:从上到下是一次评测的时间顺序;每道题(instance)独立走一遍,多道题在线程池里并行。」
predictions.json 数据集 (HuggingFace)
(模型给每道题的补丁) (题面 + gold + 测试)
│ │
└──────────────┬──────────────────────┘
▼
① 配对 + 过滤待跑题目
get_dataset_from_preds()
│
▼
② 一道题 → 一份 TestSpec
make_test_spec()
(生成 3 段 bash:建仓 / 装环境 / 跑测试)
│
▼
③ 三层 Docker 镜像
base ──► env ──► instance
(越往下越专用、越可缓存复用)
│
▼
④ 起容器,把模型补丁打进去
git apply / patch (多级 fallback)
│
▼
⑤ 跑 eval.sh:先打测试补丁,再跑测试
输出夹在 >>>>> Start/End 标记之间
│
▼
⑥ 解析日志 → status map → 判分
get_eval_report() → resolved?
│
▼
⑦ 汇总成 run report (resolved 多少道)
各步对应的真实符号:
| 步骤 | 函数 / 符号 | 文件 |
|---|---|---|
| ① 配对过滤 | get_dataset_from_preds | swebench/harness/run_evaluation.py:374 |
| ② 造 TestSpec | make_test_spec | swebench/harness/test_spec/test_spec.py:174 |
| ③ 建镜像 | build_env_images / build_instance_image | swebench/harness/docker_build.py:270,399 |
| ④⑤ 打补丁+跑测试 | run_instance | swebench/harness/run_evaluation.py:71 |
| ⑥ 判分 | get_eval_report | swebench/harness/grading.py:235 |
| ⑦ 汇总 | make_run_report | swebench/harness/reporting.py:17 |
2.3 主线走一遍(高层,不进代码)
- 你给一个
predictions.json:每条是{instance_id, model_name_or_path, model_patch}。 - 工具按
instance_id把你的补丁和数据集里的题面配上,跳过没补丁/已跑过/空补丁的(步 ①)。 - 每道题被编译成一份
TestSpec——本质是三段 bash 脚本:怎么克隆并 reset 到base_commit、怎么装依赖、怎么跑测试(步 ②)。 - 用这些脚本叠出三层 Docker 镜像,最专用的 instance 镜像里已经躺着「打好环境、停在 base_commit」的仓库(步 ③)。
- 起容器 → 把模型补丁打进去 → 跑
eval.sh,脚本里会再打测试补丁、然后跑指定测试(步 ④⑤)。 - 测试输出被夹在
>>>>> Start Test Output/>>>>> End Test Output之间,解析成「每个测试 → PASSED/FAILED/…」的 status map,再和FAIL_TO_PASS/PASS_TO_PASS比对,得出这道题resolved与否(步 ⑥)。 - 全部跑完,汇总出一份报告:总共多少道、resolved 多少道(步 ⑦)。
3. 阅读地图(建议顺序)
- 01-data-model.md — 先搞清「一道题长什么样」:
SWEbenchInstance、F2P/P2P 的含义、gold 补丁、以及数据怎么从真实 PR 里采出来(collect/)。这是后面一切的地基。 - 02-harness-pipeline.md — 评测主流程:
TestSpec如何把一道题编译成 bash、三层 Docker 镜像为什么这么分、补丁怎么打、测试怎么跑。 - 03-grading.md — 判分内核:日志解析、F2P/P2P 比对、
resolved三档判定(这是「分数」真正诞生的地方)。 - 04-internals-and-edges.md — 巧妙之处(防数据泄漏、clean diff、缓存键、多级 patch fallback)、边界与局限、多语言扩展、代码地图。
如果你只想知道「分数怎么来的」→ 直接读第 3 章。如果你要复现/调试一次跑批 → 读第 2 章。如果你要造自己的数据集 → 读第 1 章后半。