SWE-smith — 架构与原理
30 秒导读: SWE-smith 是一个「软件工程训练数据工厂」。给它一个 GitHub 仓库,它能在这个仓库里自动制造海量 bug(改坏代码)、自动验证哪些 bug 真的弄坏了测试、再为每个 bug 反写一段 GitHub issue——最终产出 SWE-bench 风格的任务,用来训练能修 bug 的 AI agent(SWE-agent)。论文 NeurIPS 2025 Spotlight,公开数据集有 5.2 万个任务。
1. 这是什么(零基础也能 懂)
一句话定义: SWE-smith 是一套把「正常仓库」批量加工成「带 bug 的编程练习题」的工具链。
它解决谁的什么问题。 要训练一个会修 bug 的 AI agent,你需要大量「这里有个 bug + 这是它的 issue 描述 + 这是会失败的测试」的样本。这种数据真实世界里很稀缺:真 PR 数量有限、收集成本高、还很难复现环境。SWE-smith 反过来想:既然真 bug 难找,那就自己造——拿一个能跑测试的健康仓库,故意把代码改坏,只要它弄挂了原本通过的测试,就是一道合格的练习题。
它能做什么:
- 把任意 GitHub 仓库变成可复现的执行环境(Docker 镜像)。
- 在该仓库里用 4 种策略无限量生成 bug patch。
- 自动验证:跑测试,只保留「让 1+ 个原本通过的测试变红」的 bug。
- 为每个保留下来的 bug 用 LLM 反写一段 GitHub issue 文本。
- 打包成 SWE-bench 风格数据集,直接喂给 SWE-agent 训练。
用起来什么样。 加载已发布的数据集,每条任务都能拿到一个初始化好的 Docker 容器:
# 示意,改编自 README
from swesmith.profiles import registry
from datasets import load_dataset
ds = load_dataset("SWE-bench/SWE-smith", split="train") # 5.2 万条任务
for task in ds:
rp = registry.get_from_inst(task) # 拿到这条任务所属仓库的「档案」(RepoProfile)
container = rp.get_container(task) # 返回一个已 checkout 到 bug 分支的 Docker 容器
# 接下来:让 agent 在容器里读 issue、改代码、跑测试……
一句话直觉/类比。 把它想成**「考试出题机」:健康仓库是「标准答案版试卷」,SWE-smith 把答案涂掉几处(造 bug),然后亲自做一遍**(跑测试),凡是涂改后真的导致「原本能做对的题现在做错了」的,就收进题库,并附上一段题面(issue)。AI agent 的任务就是「把试卷改回能全做对」。
2. 顶层全景(它大概怎么转)
整条流水线是一条单向传送带,五个工位首尾相接。怎么读下面这张图:从上到下是数据流向,每个工位吃上一个工位的产物。
健康仓库 (GitHub repo @ 某 commit)
│
▼
┌─────────────────────┐
│ ① 建环境 build_repo │ 把仓库 + 依赖装进 Docker 镜像,镜像里测试能跑通
└─────────────────────┘
│ 可复现的执行环境
▼
┌─────────────────────┐
│ ② 造 bug bug_gen │ 4 种策略改坏代码 → 一堆 .diff 补丁(此刻还不知道好坏)
└─────────────────────┘
│ 候选 bug patches
▼
┌─────────────────────┐
│ ③ 验证 harness │ 在容器里跑测试:对比改前/改后,挑出「弄挂 1+ 测试」的
└─────────────────────┘
│ 有效 bug(带 FAIL_TO_PASS / PASS_TO_PASS 清单)
▼
┌─────────────────────┐
│ ④ 出题面 issue_gen │ LLM 看着 bug + 失败测试,反写一段 GitHub issue
└─────────────────────┘
│
▼
SWE-bench 风格数据集 → ⑤ 训练 SWE-agent (train/)
各工位一句话职责:
| 工位 | 干什么 | 关键模块 |
|---|---|---|
| ① 建环境 | 仓库+依赖打成 Docker 镜像,测试可跑 | swesmith/build_repo/、profiles/*.build_image |
| ② 造 bug | 4 种策略生成候选 bug patch | swesmith/bug_gen/ |
| ③ 验证 | 跑测试、对比 pass/fail、筛有效 bug、入库 | swesmith/harness/valid.py、grading.py、gather.py |
| ④ 出题面 | LLM 给每个 bug 反写 issue 文本 | swesmith/issue_gen/generate.py |
| ⑤ 训练 | 用产出的轨迹/数据微调模型 | swesmith/train/ |
| 贯穿全局 | 仓库「档案」:装/测/解析日志的规格 | swesmith/profiles/ |
主线走一遍(高层,不进代码): 给定 owner/repo@commit → 先 create_mirror 在 swesmith 组织下建一份冻结快照、build_image 装环境 → bug_gen 抽出仓库里所有函数/类,逐个改坏,落一堆 bug__*.diff → collect_patches 把补丁汇总成一个 JSON → harness.valid 在容器里逐个 apply、跑测试、和「未改前」对比,写出每个 bug 的 FAIL_TO_PASS 清单 → harness.gather 把验证通过的 bug 推成 mirror 仓库上的一个个分支,拼成数据集 → issue_gen 给每条反写 issue → 数据集发到 HuggingFace,供训练。
两个核心数据结构(贯穿全程,先记住):
CodeEntity(swesmith/constants.py:88)——一个被抽出来的「函数/类」,带文件路径、起止行、AST 节点、以及一组代码属性标签(有没有循环、有没有二元运算……)。造 bug 就是改它。RepoProfile(swesmith/profiles/base.py:80)——一个仓库的「档案」:怎么 clone、怎么装、用什么命令跑测试、怎么解析测试日志。整条流水线都靠它把语言差异抹平。
3. 阅读地图(建议顺序)
这是个多子系统项目,按下面顺序读最顺:
- 01-bug-generation.md — 项目的灵魂:4 种造 bug 策略。先读这个,你就懂「数据从哪来」。重点是 procedural(基于 LibCST/tree-sitter 的确定性变异)和 LLM-rewrite(挖空让模型重写)。
- 02-validation-harness.md — 造出来的 bug 怎么被「筛真」:跑测试 + diff pass/fail 状态。理解
FAIL_TO_PASS这个核心信号。 - 03-profiles-and-entities.md — 支撑层:
RepoProfile单例注册表 + tree-sitter/AST 实体抽取,怎么让一套代码吃下 11 种语言。 - 04-issue-gen-and-cleverness.md — 收尾:LLM 反写 issue,以及全项目的巧妙之处、边界、横向对比、完整代码地图。
如果你只想快速判 断「这项目跟我的任务相关吗」:看完本页的 §1、§2 即可。要动手改造 bug 生成 → 跳 01;要复现/调验证 → 跳 02。