Day 3: 面对真相——从"生成了 patch"到"真的修好了"

十天写一个编码 Agent,第三天的主题是:用官方评测验证 patch 质量,并扩展到全量 300 实例。

"生成了 patch"不等于"修好了 bug"

Day 2 结束时,我们的 Agent 在 20 个 Django 实例上达到了 95% 的 patch 生成率。数字很漂亮,但有一个根本问题没回答:这些 patch 真的修好了 bug 吗?

生成率衡量的是"Agent 有没有动手修改代码",通过率衡量的是"修改后测试是否通过"。两者之间可能差距巨大——Agent 可以自信地写出一个完全错误的修复。

Day 3 的第一个任务:用 SWE-bench 官方评测工具验证真相。

静态分析:先给自己打个预防针

在跑正式评测之前,我先做了一次静态对比——把 Agent 生成的 patch 和 ground truth 逐一比较:

类别数量比例
精确匹配 ground truth1155%
语义等价(不同写法但逻辑相同)420%
不确定15%
方法错误315%
无 patch15%

预估通过率:60-65%。

这个分析本身就很有价值——它揭示了几种典型的失败模式:

  1. 时机错误:django-10924 要求在 formfield() 中延迟调用 callable,Agent 却在 __init__ 中立即调用
  2. 逻辑差异:django-12113 需要条件分支,Agent 却做了变量替换
  3. 语义微差:django-14534 用 fallback 逻辑而非直接返回 None

这些错误的共性:Agent 理解了问题的表面症状,但没有深入理解代码的设计意图。

官方评测:Docker 隔离环境

SWE-bench 的评测方式很严格:

  1. 在 Docker 容器中 checkout 到 base commit
  2. 应用预测的 patch
  3. 注入测试 patch(添加新测试或修改已有测试)
  4. 运行 FAIL_TO_PASS 测试(修复后应通过的测试)
  5. 运行 PASS_TO_PASS 测试(确保没有回归)

只有当所有测试都通过时,才算 resolved。

20 实例小规模验证

先在之前的 20 个 Django 实例上跑:

指标值
Resolved16
Unresolved2
Error1
Empty patch1
Resolve rate16/20 = 80%

16/20 通过!比预估的 60-65% 要好。原因是一些"语义等价"的 patch 确实能通过测试——修复不一定要和 ground truth 一模一样。

扩展到 300 实例

20 个实例太少,而且全是 Django。SWE-bench Lite 有 300 个实例跨 12 个仓库,需要全量评测。

并行化改造

原来的 run.py 是串行的——每次只跑一个实例。300 个实例串行跑需要 25+ 小时。

改造的核心是两个点:

1. Git Worktree 隔离

多个 Agent 并行时,不能共用同一个 repo 目录(会互相 checkout 不同 commit 导致冲突)。解决方案是用 git worktree:

python
def setup_repo(instance, workdir):
    # 每个 repo 只 clone 一次
    base_dir = _ensure_clone(repo, workdir)
    # 每个实例创建独立的 worktree
    subprocess.run(["git", "worktree", "add", "--detach", str(wt_dir), base_commit], ...)

Worktree 共享 .git 目录的对象存储,比完整 clone 节省大量磁盘。

2. ThreadPoolExecutor 并发

python
with ThreadPoolExecutor(max_workers=8) as pool:
    futures = {pool.submit(run_instance, inst, llm, output): inst for inst in instances}
    for future in as_completed(futures):
        result = future.result()
        # 实时输出进度和 ETA

8 个 worker 对应 8 个 LLM slot,充分利用推理服务器的并行能力。

300 实例生成结果

8 并发跑完 300 实例的 patch 生成:

仓库生成/总数生成率
django/django103/11490%
sympy/sympy72/7794%
scikit-learn21/2391%
matplotlib20/2387%
pytest-dev13/1776%
sphinx-doc13/1681%
astropy6/6100%
requests6/6100%
flask3/3100%
其他11/1573%
总计268/30089%

100 实例均匀抽样评测

全量 300 实例的 Docker 评测太慢(镜像构建动辄数小时),于是按仓库比例均匀抽样了 99 个实例:

指标值
提交评测99
完成评测75
Resolved44
Unresolved31
Docker Error24
有效 Resolve rate44/75 = 58.7%

44 个实例被正式确认为 resolved——跨越 Django、SymPy、scikit-learn、sphinx、pylint、astropy 6 个不同仓库。

24 个 Docker error 主要是 matplotlib、seaborn、requests 等 repo 的镜像构建失败(环境依赖问题),不是 Agent 能力问题。

数字背后的意义

58.7% 意味着什么?

以 SWE-bench Lite 的标准来看,这个数字需要放在上下文中理解:

  • 我们用的是 Qwen3.5-35B-A3B,一个 MoE 模型,实际激活参数只有 3B
  • 跑在单张 RTX 5090 上,完全本地推理
  • Agent 框架从零搭建,只用了 3 天
  • 没有任何 retrieval、planning 等高级技术

作为参考,SWE-bench 排行榜上的顶级系统使用 Claude/GPT-4 级别模型,配合精心设计的 multi-agent 架构,也就在 40-60% 的范围。

跨仓库泛化

最让我满意的不是数字本身,而是 Agent 在 6 个不同仓库上都有 resolve。这说明:

  • 系统提示词的设计是通用的
  • 工具集(bash、read_file、edit_file、search_code)足够覆盖不同类型的 bug
  • Agent 的探索策略不依赖特定项目的领域知识

Error 不是 Agent 的错

24 个 Docker error 值得说明——这些是 SWE-bench 评测基础设施的问题(特定版本的 matplotlib/seaborn 等需要特殊的系统依赖),不是 Agent 生成了错误的 patch。如果这些能正常评测,resolve 数字可能会更高。

Day 3 的认知收获

1. 评测是最诚实的镜子

静态对比 patch 可以做定性分析,但只有跑通测试才能给出确定的答案。有些看起来"不同"的 patch 能通过测试(语义等价),有些看起来"差不多"的 patch 却失败了(细微逻辑差异)。

2. 基础设施投入回报巨大

Day 3 一大半时间花在了基础设施上:并行化、Docker 评测、镜像构建。这些不性感,但没有它们就无法在合理时间内完成 300 实例的评测。编码 Agent 的研发,基础设施和算法同样重要。

3. 小模型的天花板和潜力

3B 激活参数的模型能达到 58.7% 的 resolve rate,这说明:

  • 编码 Agent 的上限不完全由模型决定——框架设计、提示词、工具选择都是关键因素
  • 小模型在简单到中等难度的 bug 上表现不错——定位代码、理解修改模式、生成正确 patch 都能做到
  • 复杂的多文件交互 bug 是真实瓶颈——这需要更强的推理能力或更好的探索策略

三天总结

Day交付关键指标
Day 1基础框架1 实例验证通过
Day 2效率优化19/20 patch 生成 (95%)
Day 3全量评测44/75 resolved (58.7%)

从零到一个能在 SWE-bench 上跑出成绩的编码 Agent,3 天,~1000 行代码,一张 GPU。


这是「十天开发编码 Agent」系列的第三篇。代码已打 tag day3。

Linked from

Comments (0)

No comments yet

imfht
April 8, 2026