Claude 拿下 SWE-bench:把控制权交给模型,脚手架越薄越好
升级版 Claude 3.5 Sonnet 在软件工程评估 SWE-bench Verified 上拿到 49%,超过此前 SOTA 的 45%。这篇讲我们围绕模型搭的那个「agent」,目的是帮开发者把 Claude 3.5 Sonnet 的性能榨到最好。
SWE-bench 评估模型完成真实软件工程任务的能力:解决热门开源 Python 仓库里的 GitHub issue。每个任务给模型一个配好的 Python 环境和「issue 解决之前」的仓库工作副本,模型要理解、修改、测试代码,再提交方案。每份方案拿关闭该 issue 的那个 PR 里的真实单元测试来评分——即测「AI 有没有实现和原作者 PR 同样的功能」。
SWE-bench 不只评孤立的模型,而是评整个「agent」系统。这里「agent」= AI 模型 + 围绕它的软件脚手架(scaffolding)——脚手架负责生成喂给模型的 prompt、解析模型输出去执行动作、管理「把上一步结果并入下一步 prompt」的交互循环。同一个模型,脚手架不同,SWE-bench 表现能差很多。
它流行有几个原因:用真实工程任务而非竞赛/面试题;还没饱和(撰文时还没有模型越过 50%,升级版 Sonnet 在 49%);评的是整个 agent——开源开发者和创业公司通过优化脚手架,在同一模型上大幅提升了表现。
注:我们说的是 SWE-bench Verified——原始 SWE-bench 里有些题缺了 issue 之外的必要上下文、根本无解,Verified 是经人工审核确认可解的 500 题子集,最能清晰衡量编码 agent 表现。
⭐ 设计哲学:把控制权交给模型,脚手架保持最小
我们为升级版 3.5 Sonnet 优化脚手架时的核心哲学是:尽量把控制权交给语言模型自己,脚手架越薄越好。这个 agent 只有:一段 prompt、一个执行 bash 命令的 Bash 工具、一个查看/编辑文件和目录的 Edit 工具。我们持续采样,直到模型自己决定结束、或超出 200k 上下文。这让模型用自己的判断去推进问题,而不是被硬编码进某个固定流程。
prompt 给出一个建议的思路,但不冗长、不过度细化。模型可以自由地从一步走到下一步,而非被强制做离散转换。提示词大意是:仓库已上传到某目录,给出 PR 描述;测试文件已替你改好、你不用动任何测试逻辑;只对非测试文件做最小改动满足 PR 要求。建议步骤:先探索仓库结构 → 写脚本复现错误并用 BashTool 跑确认 → 改源码 → 重跑复现脚本确认修好 → 想清楚边界情况。最后还补一句「你的思考可以很长,没关系」。
工具的「描述」比「schema」更重要
第一个工具跑 bash,schema 极简(只接受一个 command)。但工具的描述承载了更多分量——里面有给模型的详细说明:输入无需 XML 转义、本工具不能联网、可通过 apt/pip 访问常用包镜像、状态在多次调用间持久、想看某文件某几行可用 sed -n、避免产生海量输出、长任务放后台跑。
第二个工具 Edit 工具复杂得多,包含查看、创建、编辑文件所需的一切。我们在大量 agentic 任务上反复打磨工具的描述和 spec:测出模型可能误解 spec 的地方、踩坑的可能,再改描述预先堵掉这些问题。
我们认为,给模型设计工具接口,应该像给人设计工具接口一样被认真对待——后者投入了大量心力,前者也该如此。
「防呆」你的工具
一个提升表现的办法是给工具「防呆(error-proof)」。例如 agent 移出根目录后有时会把相对路径搞错——我们干脆强制工具只接受绝对路径。
指定「如何编辑已有文件」时,我们试了好几种策略,字符串替换(string replacement)可靠性最高:模型给出 old_str 去替换成 new_str,只有当 old_str 在文件里恰好唯一匹配一次时才会替换;匹配数不对就给模型一条恰当的报错让它重试。Edit 工具的 spec 里,command 可选 view / create / str_replace / insert / undo_edit,并要求 path 是绝对路径等。
结果
升级版 3.5 Sonnet 在推理、编码、数学上都强于此前模型和上一代 SOTA,agentic 能力也更好——工具和脚手架帮它把这些能力用到了刀刃上。
| 模型 | SWE-bench Verified 分数 |
|---|---|
| Claude 3.5 Sonnet(新) | 49% |
| 此前 SOTA | 45% |
| Claude 3.5 Sonnet(旧) | 33% |
| Claude 3 Opus | 22% |
(均使用同一套 agent 脚手架。)
一个典型解题过程
跑基准时我们以 SWE-Agent 框架为基础。日志里我们把模型的文字输出、工具调用、工具返回分别渲染为 THOUGHT、ACTION、OBSERVATION(虽然我们并不强制固定顺序)。一个典型例子(sklearn 的 RidgeClassifierCV 缺少 store_cv_values 参数报 TypeError):
- 模型先用 Edit 工具 view 仓库结构(THOUGHT/ACTION/OBSERVATION);
- 用 Edit 工具 create 一个复现脚本
reproduce_error.py; - 用 Bash 工具跑脚本,成功复现
TypeError; - 看懂根因(
RidgeClassifierCV继承自_BaseRidgeCV,却没把store_cv_values透传给父类构造器),用 str_replace 改源码,在__init__和super().__init__()里补上参数; - 重跑脚本确认修好。
这例里模型干了 12 步就提交,测试通过。有些任务模型要超过 100 轮才提交,有些则一直试到耗尽上下文。对比旧模型,升级版 3.5 Sonnet 更常自我纠正,也更会换几种不同解法,而不是一头撞死在同一个错误上。
挑战
SWE-bench Verified 很强,但比单轮 eval 更难跑。我们遇到的(其他 AI 开发者大概也会遇到):
| 挑战 | 说明 |
|---|---|
| 耗时与高 token 成本 | 上面是 12 步搞定的例子,但很多成功跑要几百轮、超 10 万 token。升级版 3.5 Sonnet 很有韧性,给足时间常能绕过问题——但那可能很贵 |
| 打分 | 排查失败时发现有些是环境配置问题或安装 patch 被打了两遍,而非模型真错。解决这些系统性问题对「拿到准确的能力画像」至关重要 |
| 隐藏测试 | 模型看不到用来评它的测试,常以为自己成功了而其实失败——有的是解在了错误的抽象层(贴创可贴而非深层重构),有的则解了问题但不匹配原任务的单元测试,多少有点不公平 |
| 多模态 | 尽管 3.5 Sonnet 视觉能力出色,我们没实现让它查看文件系统里的文件或 URL 引用的图,这让某些任务(尤其 Matplotlib 类)调试格外难、也易引发幻觉。这里有明显的「低垂果实」待开发者改进 |
总结
升级版 Claude 3.5 Sonnet 用一段简单 prompt + 两个通用工具,就在 SWE-bench Verified 上拿到 49%、刷新 SOTA。这背后的直觉是:模型越强,越该把判断权交还给它、把脚手架做薄——你的功夫应该花在把工具接口打磨到位、把工具防呆,而不是把解题流程硬编码死。我们相信,用新 Sonnet 构建的开发者会很快找到比我们这里展示的更好的办法,把分数推得更高。