第 4 章 · 巧妙之处、边界与代码地图
前三章讲了主线。本章收拢「值得带走的精华」、诚实的局限,以及给人/agent 用的跳转表。
4.1 巧妙之处(可借鉴的技术)
4.1.1 防数据泄漏:把 base_commit 之后的历史从仓库里剪掉
如果容器里的仓库还能看到「未来」的提交和 tag,模型 agent 就可能偷看到答案。make_repo_script_list_py(swebench/harness/test_spec/python.py:264-317)在克隆后做了一串清理:
git remote remove origin——断开远程,模型拿不到新提交(:279)。- 按
base_commit的时间戳,删掉所有指向更晚提交的 tag(:280-282)。 git reflog expire+git gc --prune=now --aggressive抹掉悬空对象(:283-284)。- 校验:统计 base 时间戳之后的提交数,若不为 0 直接
exit 1(:285-288)。
妙在哪:把「不许偷看未来」做成了构建期硬校验,而不是口头约定。
4.1.2 clean diff:用一次空提交锚定基线
安装步骤可能顺手改动仓库文件,导致后面 git diff 把环境改动也算进去。解决办法是建仓脚本结尾打一次空提交(python.py:309-315):
# 真实源码节选
git config --global user.email setup@swebench.config
git config --global user.name SWE-bench
git commit --allow-empty -am SWE-bench
这是它在干嘛:把「环境就绪」固化成一个干净的 HEAD,之后 git diff 就只反映模型补丁带来的改动,基线一致、可复现。
4.1.3 内容哈希当缓存键(见第 2 章 2.3.1)
环境脚本/docker_specs 的 sha256 当镜像 tag(test_spec.py:89-104):脚本变→key 变→自动重建,脚本不变→命中复用。正确性来自内容寻址,省时又防陈旧缓存。
4.1.4 多级 patch fallback(见第 2 章 2.4)
GIT_APPLY_CMDS(run_evaluation.py:64-68)从严到宽三连:git apply → git apply --reject → patch --fuzz=5。容忍模型补丁的轻微行号偏移,同时保留「全失败即判失败」的底线。
4.1.5 新文件 vs 改文件,还原方式不同(一个真实 bug 的修法)
eval 脚本还原测试时,对已存在文件用 git checkout base_commit <file>、对新增文件用 rm -f(python.py:417-423,注释里点名 issue #518)。原因:若对没有文件参数地 git checkout base_commit,会把整个工作树都 reset 掉、连环 境改动一起抹了。get_modified_files/get_new_files(utils.py:339-362)靠 unidiff 判断补丁里源文件是不是 /dev/null 来区分。
4.2 inference 子系统:模型怎么拿到题、怎么交补丁
虽然评测不强制用本仓库跑模型,但 inference/ 提供了完整链路:
- 构造输入:
PROMPT_FUNCTIONS(swebench/inference/make_datasets/create_instance.py:296)把 issue + 仓库代码 + 「请输出一个能git apply的补丁」拼成 prompt(如prompt_style_2,:165-190)。 - 检索:仓库往往太大塞不进上下文,
bm25_retrieval.py用 Pyserini/Lucene 建索引(make_index,:196),按 BM25 召回最相关的文件再喂给模型。 - 跑模型:
run_api.py走 OpenAI/Anthropic API(MODEL_LIMITS里登记各模型上下文上限,:32-45),run_llama.py跑本地权重;都把模型回复里的补丁抽出来(extract_diff)写成 predictions。
4.3 多语言扩展点
SWE-bench 已不止 Python。支持的语言由 constants/ 下的 文件枚举:c / go / java / javascript / php / python / ruby / rust(MAP_REPO_TO_EXT,constants/__init__.py:158-167)。加一门语言/一个仓库,本质是补三处映射表:
| 要补什么 | 在哪 |
|---|---|
安装/测试配方 MAP_REPO_VERSION_TO_SPECS | constants/<lang>.py |
日志解析器 MAP_REPO_TO_PARSER | log_parsers/<lang>.py |
| 脚本/ Dockerfile 模板(如非 common 可复用) | test_spec/<lang>.py、dockerfiles/<lang>.py |
脚本生成默认走 *_common(test_spec/create_scripts.py)。专门实现的分布并不均匀:建仓/装环境脚本只有 Python 有专门分支,JS 的专门实现仅限 eval 脚本(make_eval_script_list_js,见 2.2),其余路径仍走 common——所以新语言常常先复用 common 路径。
4.4 边界与局限(诚实)
- 强依赖 Docker 与算力:README 建议 x86_64、≥120GB 磁盘、≥16GB 内存;ARM/Mac 需本地构建镜像(慢)。这不是轻量评测。
- 判分依赖日志格式:解析器是按框架/仓库手写的正则。测试框架升级、输出格式变了,解析可能失准(源码里多处
TODO,如grading.py:49、python.py:93-99的 Django 特例)。 - Multimodal test 不可本地评测:被刻意私有化以防刷分,只能走 sb-cli 云端提交(
run_evaluation.py:496-501)。 - 「resolved」不等于「写法和人一样好」:只要 F2P/P2P 满足即算解决,不评估补丁的可读性/最优性——它测的是「修对了吗」,不是「修得漂亮吗」。
- 数据可能随时间偏差:题来自历史 PR,新模型的训练数据可能已包含这些仓库的后续提交,存在潜在记忆/泄漏风险(这也是 4.1.1 那套构建期防护存在的原因,但只能防容器内偷看,防不了训练集污染)。
4.5 横向对比(同 shelf 视角)
SWE-bench 属于 evals-observability + coding-agents 交叉地带,与「跑模型的 agent」是上下游关系:
- vs 编码 agent(如 SWE-agent):agent 负责「生成补丁」,SWE-bench 负责「给补丁判分」。SWE-agent 正是为刷 SWE-bench 而生的兄弟项目(README News)。本仓库
inference/是一个最简 baseline 式的「生成器」。 - 判分哲学:相比「LLM 当裁判」或「文本相似度」的评测,SWE-bench 走的是**可执行裁判(真测试)**路线——客观、抗刷分,代价是工程复杂、跑得慢。
4.6 代码地图(导航索引)
| 主题 | 文件路径 | 关键符号 |
|---|---|---|
| 题目数据结构 | swebench/harness/constants/__init__.py | SWEbenchInstance, FAIL_TO_PASS, PASS_TO_PASS, TestStatus, ResolvedStatus |
| 评测顶层入口 | swebench/harness/run_evaluation.py | main, get_dataset_from_preds, run_instances, run_instance, GIT_APPLY_CMDS |
| 题→脚本编译 | swebench/harness/test_spec/test_spec.py | TestSpec, make_test_spec, env_image_key, instance_image_key |
| 脚本生成(按语言分派) | swebench/harness/test_spec/create_scripts.py | make_repo_script_list, make_env_script_list, make_eval_script_list |
| Python 脚本/防泄漏/clean diff | swebench/harness/test_spec/python.py | make_repo_script_list_py, make_eval_script_list_py, get_test_directives |
| 三层镜像构建 | swebench/harness/docker_build.py | build_base_images, build_env_images, build_instance_image, build_container, build_image |
| Dockerfile 模板 | swebench/harness/dockerfiles/python.py | _DOCKERFILE_BASE_PY, _DOCKERFILE_ENV_PY, _DOCKERFILE_INSTANCE_PY |
| 容器执行/超时/缓存清理 | swebench/harness/docker_utils.py | exec_run_with_timeout, should_remove, clean_images |
| 判分内核 | swebench/harness/grading.py | get_logs_eval, get_eval_tests_report, compute_fail_to_pass, get_resolution_status, get_eval_report |
| 日志解析器映射 | swebench/harness/log_parsers/__init__.py | MAP_REPO_TO_PARSER |
| pytest/Django 解析器 | swebench/harness/log_parsers/python.py | parse_log_pytest, parse_log_django |
| 最终汇总报告 | swebench/harness/reporting.py | make_run_report |
| 工具:预测加载/线程池/patch 分析 | swebench/harness/utils.py | get_predictions_from_file, run_threadpool, load_swebench_dataset, get_modified_files, get_new_files |
| 数据采集 | swebench/collect/build_dataset.py | create_instance, is_valid_pull, is_valid_instance, has_test_patch |
| 安装/测试配方表 | swebench/harness/constants/python.py | MAP_REPO_VERSION_TO_SPECS_PY, TEST_PYTEST, TEST_DJANGO |
| inference:输入/检索/跑模型 | swebench/inference/make_datasets/create_instance.py, bm25_retrieval.py, swebench/inference/run_api.py | PROMPT_FUNCTIONS, prompt_style_2, make_index, MODEL_LIMITS |