Claude 3.5 Sonnet 在 SWE-bench Verified 上再创高分
官方原文: https://www.anthropic.com/engineering/swe-bench-sonnet
发布日期: 2025年1月6日
作者: Erik Schluntz(优化了 SWE-bench Agent 并撰写了博文)。贡献者包括 Simon Biggs、Dawn Drain、Eric Christiansen、Shauna Kravec、Felipe Rosso、Nova DasSarma、Ven Chandrasekaran 以及其他参与训练的人员。
SWE-bench 是一个 AI 评估基准,用于衡量模型完成真实软件工程任务的能力。
升级后的 Claude 3.5 Sonnet 在 SWE-bench Verified 上取得 49%,超过此前最先进模型的 45%。本文详细介绍围绕模型搭建的 Agent scaffold,以及开发者如何借此发挥模型能力。
什么是 SWE-bench?
SWE-bench 测试模型解决来自热门开源 Python 仓库的 GitHub issue 的能力。对于每个任务,AI 会收到一个配置好的 Python 环境和一个在 issue 被解决之前的仓库检出版本。模型必须理解、修改和测试代码,然后提交解决方案。
每个解决方案都会根据关闭原始 GitHub issue 的 Pull Request 中的真实单元测试进行评分,测试 AI 是否实现了与原始人类 PR 作者相同的功能。
SWE-bench 评测的是完整的 Agent 系统,即 AI 模型与其外围 software scaffold 的组合。scaffold 负责生成 prompt、解析模型输出并执行动作、管理交互循环。即便底层模型相同,不同 scaffold 也可能带来显著的性能差异。
SWE-bench 流行的原因包括:
- 来自真实项目的实际工程任务,而非竞赛或面试式问题
- 尚未饱和——没有模型在 SWE-bench Verified 上超过 50%
- 它评测完整 Agent 而非孤立模型,允许开源开发者和初创团队优化 scaffold
原始 SWE-bench 数据集包含一些缺少必要上下文的不可解任务。SWE-bench Verified 是一个由人类审查可解性的 500 题子集,提供了最清晰的编码 Agent 性能衡量标准。
达到最先进水平
工具使用 Agent
设计理念是“尽可能把控制权交给语言模型”,同时让 scaffold 保持最小。Agent 只有一个 prompt、一个执行 bash 命令的 Bash Tool,以及一个查看、编辑文件和目录的 Edit Tool。采样会持续到模型决定完成,或超过 200k Context Window。这让模型用自身判断推进,而非被硬编码进固定模式或流程。
提示词概述了一个建议的方法,但不会过长或过于详细。模型可以自由选择如何在步骤之间移动。如果对 Token 不敏感,明确鼓励模型产生长回复可能会有帮助。
Agent 提示词:
<uploaded_files>
{location}
</uploaded_files>
I've uploaded a python code repository in the directory {location} (not in /tmp/inputs). Consider the following PR description:
<pr_description>
{pr_description}
</pr_description>
Can you help me implement the necessary changes to the repository so that the requirements specified in the <pr_description> are met?
I've already taken care of all changes to any of the test files described in the <pr_description>. This means you DON'T have to modify the testing logic or any of the tests in any way!
Your task is to make the minimal changes to non-tests files in the {location} directory to ensure the <pr_description> is satisfied.
Follow these steps to resolve the issue:
1. As a first step, it might be a good idea to explore the repo to familiarize yourself with its structure.
2. Create a script to reproduce the error and execute it with `python <filename.py>` using the BashTool, to confirm the error
3. Edit the sourcecode of the repo to resolve the issue
4. Rerun your reproduce script and confirm that the error is fixed!
5. Think about edgecases and make sure your fix handles them as well
Your thinking should be thorough and so it's fine if it's very long.Bash Tool 规范:
{
"name": "bash",
"description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* You do have access to a mirror of common linux and python packages via apt and pip.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The bash command to run."
}
},
"required": ["command"]
}
}Edit Tool 描述:
{
"name": "str_replace_editor",
"description": "Custom editing tool for viewing, creating and editing files\n* State is persistent across command calls and discussions with the user\n* If `path` is a file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep\n* The `create` command cannot be used if the specified `path` already exists as a file\n* If a `command` generates a long output, it will be truncated and marked with `<response clipped>` \n* The `undo_edit` command will revert the last edit made to the file at `path`\n\nNotes for using the `str_replace` command:\n* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!\n* If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique\n* The `new_str` parameter should contain the edited lines that should replace the `old_str`",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"enum": ["view", "create", "str_replace", "insert", "undo_edit"],
"description": "The commands to run. Allowed options are: `view`, `create`, `str_replace`, `insert`, `undo_edit`."
},
"file_text": {
"description": "Required parameter of `create` command, with the content of the file to be created.",
"type": "string"
},
"insert_line": {
"description": "Required parameter of `insert` command. The `new_str` will be inserted AFTER the line `insert_line` of `path`.",
"type": "integer"
},
"new_str": {
"description": "Required parameter of `str_replace` command containing the new string. Required parameter of `insert` command containing the string to insert.",
"type": "string"
},
"old_str": {
"description": "Required parameter of `str_replace` command containing the string in `path` to replace.",
"type": "string"
},
"path": {
"description": "Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.",
"type": "string"
},
"view_range": {
"description": "Optional parameter of `view` command when `path` points to a file. If none is given, the full file is shown. If provided, the file will be shown in the indicated line number range, e.g. [11, 12] will show lines 11 and 12. Indexing at 1 to start. Setting `[start_line, -1]` shows all lines from `start_line` to the end of the file.",
"items": { "type": "integer" },
"type": "array"
}
},
"required": ["command", "path"]
}
}一项性能改进来自让工具更不易出错。例如模型有时离开根目录后会误处理相对路径,因此工具改为始终要求使用绝对路径。
测试了几种指定编辑的策略,其中字符串替换达到了最高可靠性。模型指定 old_str 替换为 new_str,只有在恰好有一个匹配时才会进行替换。如果匹配数多于或少于一个,会显示错误消息以供重试。
结果
| 模型 | SWE-bench Verified 分数 |
|---|---|
| Claude 3.5 Sonnet (new) | 49% |
| 之前最先进 | 45% |
| Claude 3.5 Sonnet (old) | 33% |
| Claude 3 Opus | 22% |
所有分数使用相同的 Agent 脚手架。升级后的 Claude 3.5 Sonnet 展示了比之前模型和之前最先进水平更高的推理、编码和数学能力,以及改进的 Agent 能力。
Agent 行为示例
该基准使用 SWE-Agent 框架作为基础。Agent 文本输出、工具调用和工具响应被渲染为 THOUGHT、ACTION 和 OBSERVATION,尽管模型不受固定顺序的约束。
演练:RidgeClassifierCV Issue
初始提示词({pr_description} 填充了一个关于 scikit-learn 的 RidgeClassifierCV 中 store_cv_values 参数错误的真实 SWE-bench 任务):
任务描述了在向 RidgeClassifierCV 传递 store_cv_values=True 时出现 TypeError,即使某些属性依赖于它。
步骤 1 — 探索仓库结构: 模型使用 Edit Tool 的 view 命令查看 /repo,发现了仓库布局,包括 sklearn/linear_model、sklearn/base.py 和其他目录。
步骤 2 — 创建重现脚本: 模型创建了 /repo/reproduce_error.py,包含 PR 描述中的重现代码。
步骤 3 — 运行重现脚本: Bash Tool 执行了脚本,确认了 TypeError: __init__() got an unexpected keyword argument 'store_cv_values'。
步骤 4 — 修复源代码: 模型发现 RidgeClassifierCV 继承自 _BaseRidgeCV,但未能将 store_cv_values 从其构造函数传递给基类。使用 Edit Tool 的 str_replace 命令修改 /repo/sklearn/linear_model/ridge.py,将 __init__ 方法改为接受 store_cv_values=False 作为参数,并将其传递给 super().__init__() 调用。
这个特定示例在 12 步内完成。有些任务需要超过 100 个回合才提交;在其他任务中,模型不断尝试直到上下文耗尽。
与旧模型相比,更新后的 3.5 Sonnet 更常自行纠正,也更能尝试多种方案,而不是反复犯同一种错误。
挑战
SWE-bench Verified 功能强大,但比简单的单轮评估更复杂:
持续时间长且 Token 成本高。 许多成功运行需要数百个回合和超过 100k Token。更新后的 Claude 3.5 Sonnet 非常坚韧,只要有足够的时间通常能找到解决问题的方法,但这可能很昂贵。
评分。 检查失败的任务发现,模型行为正确但环境设置问题或安装补丁被应用两次导致失败。解决这些系统问题对于准确评估性能至关重要。
隐藏测试。 由于模型无法看到评分测试,它经常认为自己成功了而任务实际失败。一些失败源于在错误的抽象层次上解决问题。其他情况发生在模型解决了问题但与原始单元测试不匹配时。
多模态。 尽管更新后的 Claude 3.5 Sonnet 具有出色的视觉能力,但没有实现允许它查看保存到文件系统或作为 URL 引用的文件。这使得调试某些任务(特别是与 Matplotlib 相关的任务)变得困难且容易产生幻觉。SWE-bench 已经启动了多模态评估,团队期待开发者使用 Claude 实现更高的分数。
总结
升级后的 Claude 3.5 Sonnet 借助简单 prompt 与两个通用工具(Bash 和 Edit),在 SWE-bench Verified 上取得 49%,超过此前 45% 的最佳水平。团队相信,使用新模型的开发者很快会找到进一步提高 SWE-bench 成绩的方法。
致谢
Erik Schluntz 优化了 SWE-bench Agent 并撰写了博文。Simon Biggs、Dawn Drain 和 Eric Christiansen 帮助实现了基准测试。Shauna Kravec、Dawn Drain、Felipe Rosso、Nova DasSarma、Ven Chandrasekaran 以及许多其他人为训练 Claude 3.5 Sonnet 进行 Agent 式编码做出了贡献。