使用 Claude 3.5 Sonnet 在 SWE-bench Verified 上刷新纪录
发布日期: 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 脚手架,帮助开发者最大化性能。
什么是 SWE-bench?
SWE-bench 测试模型解决来自热门开源 Python 仓库的 GitHub issue 的能力。对于每个任务,AI 会收到一个配置好的 Python 环境和一个在 issue 被解决之前的仓库检出版本。模型必须理解、修改和测试代码,然后提交解决方案。
每个解决方案都会根据关闭原始 GitHub issue 的 Pull Request 中的真实单元测试进行评分,测试 AI 是否实现了与原始人类 PR 作者相同的功能。
SWE-bench 评估的是整个 "Agent" 系统——AI 模型及其周围软件脚手架的组合。这个脚手架生成提示词、解析模型输出以采取行动,并管理交互循环。即使使用相同的底层模型,性能也可能因脚手架的不同而有显著差异。
SWE-bench 流行的原因包括:
- 来自真实项目的实际工程任务,而非竞赛或面试式问题
- 尚未饱和——没有模型在 SWE-bench Verified 上超过 50%
- 评估整个 Agent 而非孤立的模型,允许开源开发者和初创公司优化脚手架
原始 SWE-bench 数据集包含一些缺少必要上下文的不可解任务。SWE-bench Verified 是一个由人类审查可解性的 500 题子集,提供了最清晰的编码 Agent 性能衡量标准。
达到最先进水平
工具使用 Agent
设计理念是"尽可能将控制权交给语言模型本身",并保持脚手架最小化。Agent 有一个提示词、一个用于执行 bash 命令的 Bash Tool 和一个用于查看和编辑文件及目录的 Edit Tool。采样持续到模型决定完成或超过 200k 上下文长度。这种方法让模型使用自己的判断,而不是被硬编码到特定模式或工作流程中。
提示词概述了一个建议的方法,但不会过长或过于详细。模型可以自由选择如何在步骤之间移动。如果对 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 在 SWE-bench Verified 上达到了 49%,击败了之前 45% 的最先进水平,使用的是简单的提示词和两个通用工具(Bash 和 Edit)。团队表示有信心,使用新模型构建的开发者将很快找到更好的方法来提高 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 式编码做出了贡献。