数据截至 (上游 commit 7a21e0577295)
第 1 章 · 一道题长什么样(数据模型与采集)
本章讲两件事:(1) 一道 SWE-bench 题目(task instance)在内存里是什么结构、各字段什么意思;(2) 这些题目是怎么从真实 GitHub PR 里被「采」出来的。理解了数据模型,后面的评测和判分才有意义。
1.1 核心数据结构:SWEbenchInstance
一道题就是一个带固定字段的字典。类型定义在 swebench/types.py:9-21:
# 真实源码片段(已省略 hints_text / created_at 两个字段,见下方说明)
class SWEbenchInstance(TypedDict):
repo: str # "django/django"
instance_id: str # "django__django-12345"
base_commit: str # 模型看到的 代码快照(PR 之前的状态)
patch: str # gold 补丁:人类合并的代码改动
test_patch: str # 测试补丁:随该 PR 一起加的测试
problem_statement: str # issue 正文
FAIL_TO_PASS: str # 该由红转绿的测试(JSON 字符串)
PASS_TO_PASS: str # 必须保持绿的测试(JSON 字符串)
version: str # 仓库版本,决定用哪套安装/测试配方
environment_setup_commit: str # 建环境时用的提交点
这段是它在干嘛:定义评测里到处传递的「题目」对象。为聚焦判分主线,上面省略了源码里另有的两个字段——hints_text(issue 下的提示性评论)与 created_at(PR 创建时间),它们在评测主流程里基本不参与判分。注意 FAIL_TO_PASS / PASS_TO_PASS 在数据集里常以 JSON 字符串形式存放,用到时再 json.loads(见 make_test_spec,swebench/harness/utils.py:268-269)。
1.2 一个真实 PR 怎么拆成「答案 + 裁判」
这是 SWE-bench 最核心的 造题直觉:一个修了 bug 的 PR,天然包含「代码改动」和「测试改动」两部分。SWE-bench 把它们分开用。
「怎么读:左边是模型能看到的,右边是评测时才用、对模型隐藏的。」
一个真实的、已合并、且关联了 issue 的 PR
│
├── issue 正文 ───────────────► problem_statement (给模型看)
│
├── 代码改动 (非测试文件) ─────► patch = gold 答案 (评测时对模型隐藏)
│
└── 测试改动 (测试文件) ───────► test_patch = 裁判 (评测时才打进去)
关键设计:模型只拿到题面和仓库的 base_commit 快照,拿不到 gold patch,也拿不到 test_patch。模型得自己写补丁;评测时系统才把 test_patch 打进去、跑测试来验收。
1.3 F2P 与 P2P:判分的两把尺子
这两个列表是整个评测的灵魂。直觉很简单:
FAIL_TO_PASS(F2P):这些测试在 bug 没修时是失败的,修好后应当通过。它们衡量「问题到底有没有被解决」(Resolution)。PASS_TO_PASS(P2P):这些测试本来就通过,改完后还得通过。它们衡量「有没有把别的东西改坏」(Maintenance)。
常量名定义在 swebench/harness/constants/__init__.py:30-33。比对逻辑见第 3 章。
一句话:F2P 管「修好了吗」,P2P 管「没搞砸吧」。两个都满足才算真正解决。
1.4 数据集的几个版本
README 与 load_swebench_dataset(swebench/harness/utils.py:137)里能看到主要变体:
| 数据集 | 规模/特点 |
|---|---|
| SWE-bench (full) | 全量测试集 |
| SWE-bench Lite | 子集,跑得快、成本低 |
| SWE-bench Verified | 500 道,真实工程师确认「确实可解」(与 OpenAI Preparedness 合作) |
| SWE-bench Multimodal | 含视觉/前端场景;test 划分的评测被刻意私有化,只能走云端 sb-cli 提交 |
Multimodal 的 test 划分在本地评测里被直接拦截——见 swebench/harness/run_evaluation.py:496-501,目的是防止有人针对私有测试集刷分。
1.5 数据是怎么采出来的(collect 子系统)
造题侧的主流程在 swebench/collect/build_dataset.py。它读一个 PR 的 JSONL 文件,逐个判断能不能成题。
1.5.1 一个 PR 必须满足的硬条件
is_valid_pull(swebench/collect/build_dataset.py:51-64)要求:
# 真实源码节选
def is_valid_pull(pull: dict) -> bool:
if pull["merged_at"] is None: # 必须真的被合并了
return False
if "resolved_issues" not in pull or len(pull["resolved_issues"]) < 1:
return False # 必须关联了至少一个 issue
return True
这是它在干嘛:只有「被合并 + 关联 issue」的 PR 才有资格——前者保证有真实可信的答案,后者保证有清晰的问题描述当题面。
1.5.2 成题还要过两道质量关
is_valid_instance(:67-80):patch和problem_statement都非空。has_test_patch(:83-94):必须带测试改动——没有测试就没法当裁判,直接淘汰。
create_instance(:21-48)把通过的 PR 组装成上面 1.1 的字典;instance_id 由 owner/repo-PR号 转写而来(把 / 换成 __)。
1.5.3 「测试文件 vs 代码文件」怎么分
造题和判分都要区分哪些是测试。判分侧用 NON_TEST_EXTS(harness/constants/__init__.py:58 起,如 .json/.txt/.md/.yml)剔掉非代码文件;从 test_patch 抽测试文件路径的逻辑(旧版 harness 的 get_test_directives)已随 2026 年中重构移到数据侧(task/、collect/),评测时不再现场抽取。
1.6 小结
- 一道题 = 一个真实 PR,拆成「gold 答案补丁」+「测试裁判补丁」+「题面」。
- 模型只看题面和 base 快照;答案和测试对它隐藏。
- F2P/P2P 是两把判分尺子:解决了吗 + 没改坏吗。
- 采集严格过滤:合并过、关联 issue、有非空答 案、有测试。
下一章:这道题如何被编译成可在 Docker 里跑的脚本。