Claude 3.5 Sonnet 在 SWE-bench Verified 上的性能突破

核心成果概述

Anthropic 2025年1月发布的升级版 Claude 3.5 Sonnet 在 SWE-bench Verified 软件工程评估基准测试中取得了 49% 的成绩,超越了此前最先进模型的 45% 的表现。本文详细解释了围绕该模型构建的"智能体"(agent)系统,旨在帮助开发者从 Claude 3.5 Sonnet 中获得最佳性能。


什么是 SWE-bench

SWE-bench 是一个 AI 评估基准,用于评估模型完成真实世界软件工程任务的能力。具体来说,它测试模型如何解决来自流行开源 Python 仓库的 GitHub 问题。对于基准测试中的每个任务,AI 模型会获得一个已配置好的 Python 环境,以及该问题解决之前的仓库检出(本地工作副本)。然后,模型需要理解、修改和测试代码,最后提交其提议的解决方案。

每个解决方案都会根据关闭原始 GitHub 问题的拉取请求中的真实单元测试进行评分。这测试了 AI 模型是否能够实现与原始 PR 人类作者相同的功能。

SWE-bench 不仅仅是孤立地评估 AI 模型,而是评估整个"智能体"系统。在这个上下文中,"智能体"指的是 AI 模型与其周围软件脚手架的组合。这个脚手架负责生成输入模型的提示词、解析模型的输出以采取行动,以及管理交互循环(将模型先前行动的结果纳入其下一个提示词中)。即使使用相同的底层 AI 模型,智能体在 SWE-bench 上的性能也会因这个脚手架而有显著差异。

SWE-bench 评估流程

graph TD
    A[GitHub Issue] --> B[AI 模型接收任务]
    B --> C[Python 环境 + 仓库检出]
    C --> D[模型理解问题]
    D --> E[修改代码]
    E --> F[测试代码]
    F --> G{通过测试?}
    G -->|否| D
    G -->|是| H[提交解决方案]
    H --> I[运行真实单元测试]
    I --> J[评分]

SWE-bench 的独特优势

虽然有许多其他用于评估大型语言模型编码能力的基准测试,但 SWE-bench 因以下几个原因而越来越受欢迎:

  1. 真实工程任务:它使用来自实际项目的真实工程任务,而不是竞赛或面试式问题;

  2. 尚未饱和:仍有充足的改进空间。还没有模型在 SWE-bench Verified 上超过 50% 的完成率(尽管截至2025年1月时,升级版 Claude 3.5 Sonnet 达到了 49%);

  3. 整体评估:它衡量的是整个"智能体",而不是孤立的模型。开源开发者和初创公司在优化脚手架方面取得了巨大成功,大幅提升了相同模型的性能。

SWE-bench 三大优势

graph TD
    Root((SWE-bench 优势))
    Root --> A[真实任务]
    Root --> B[未饱和]
    Root --> C[整体评估]
    
    A --> A1[实际项目]
    A --> A2[GitHub Issues]
    
    B --> B1[最高 49%]
    B --> B2[改进空间大]
    
    C --> C1[模型+脚手架]
    C --> C2[完整系统]

SWE-bench Verified 数据集说明

需要注意的是,原始 SWE-bench 数据集包含一些在没有 GitHub 问题之外的额外上下文(例如关于要返回的特定错误消息)的情况下无法解决的任务。SWE-bench Verified 是 SWE-bench 的一个包含 500 个问题的子集,经过人工审查以确保它们是可解决的,因此提供了编码智能体性能最清晰的衡量标准。本文将主要参考这个基准测试。


实现最先进性能的方法

工具使用智能体架构

在为升级版 Claude 3.5 Sonnet 优化智能体脚手架时,我们的设计理念是尽可能多地将控制权交给语言模型本身,并保持脚手架最小化。该智能体包含:

  • 一个提示词
  • 一个用于执行 bash 命令的 Bash 工具
  • 一个用于查看和编辑文件和目录的编辑工具

我们持续采样,直到模型决定完成任务,或超过其 200k 上下文长度限制。这种脚手架允许模型根据自己的判断来处理问题,而不是被硬编码到特定的模式或工作流程中。

提示词概述了模型的建议方法,但对于这项任务来说,它并不太长或过于详细。模型可以自由选择如何从一个步骤过渡到另一个步骤,而不是有严格和离散的转换。如果你对令牌(token)不敏感,明确鼓励模型产生长响应会有所帮助。

智能体架构设计

graph TD
    Agent((智能体系统))
    
    Agent --> Model[AI 模型]
    Agent --> Scaffold[脚手架]
    
    Model --> M1[Claude 3.5 Sonnet]
    Model --> M2[200k 上下文窗口]
    Model --> M3[自主决策能力]
    
    Scaffold --> S1[提示词]
    Scaffold --> S2[Bash 工具]
    Scaffold --> S3[编辑工具]
    
    S1 --> P1[建议方法]
    S1 --> P2[灵活步骤]
    
    S2 --> B1[执行命令]
    S2 --> B2[环境管理]
    
    S3 --> E1[查看文件]
    S3 --> E2[编辑文件]
    S3 --> E3[字符串替换]

智能体提示词

以下代码展示了我们智能体脚手架的提示词:

<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.

建议的问题解决步骤

graph LR
    A[1. 探索仓库结构] --> B[2. 创建重现脚本]
    B --> C[3. 确认错误]
    C --> D[4. 修改源代码]
    D --> E[5. 重新测试]
    E --> F{修复成功?}
    F -->|否| D
    F -->|是| G[6. 检查边界情况]
    G --> H[完成]

Bash 工具规范

模型的第一个工具执行 Bash 命令。架构很简单,只需要在环境中运行的命令。然而,工具的描述更重要。它包含了对模型的更详细说明,包括转义输入、缺乏互联网访问,以及如何在后台运行命令。

以下是 Bash 工具的规范:

{
   "name": "bash",
   "description": "Run commands in a bash shelln
* 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"]
   }
}

Bash 工具关键说明

特性说明
XML 转义命令参数不需要 XML 转义
网络访问 无互联网访问
包管理 可通过 apt 和 pip 安装常用包
状态持久化 跨命令调用保持状态
输出限制避免产生大量输出的命令
后台运行支持后台运行长时命令

编辑工具规范

模型的第二个工具(编辑工具)要复杂得多,包含了模型查看、创建和编辑文件所需的一切。同样,我们的工具描述包含了关于如何使用该工具的详细信息。

我们在各种智能体任务中对这些工具的描述和规范投入了大量精力。我们对它们进行了测试,以发现模型可能误解规范的任何方式,或使用工具的可能陷阱,然后编辑描述以预防这些问题。我们认为,应该像对设计人类工具界面投入大量关注一样,对为模型设计工具界面投入更多关注。

以下代码展示了编辑工具的描述:

{
   "name": "str_replace_editor",
   "description": "Custom editing tool for viewing, creating and editing filesn
* State is persistent across command calls and discussions with the usern
* 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 deepn
* The `create` command cannot be used if the specified `path` already exists as a filen
* 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
n
Notes 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 uniquen
* 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"]
   }
}

编辑工具命令对比

命令用途必需参数说明
view查看文件/目录path文件显示带行号,目录显示2级深度
create创建新文件path, file_text文件已存在时不可用
str_replace替换字符串path, old_str, new_strold_str 必须唯一匹配
insert插入内容path, insert_line, new_str在指定行后插入
undo_edit撤销编辑path撤销最后一次编辑

工具改进:错误防护

我们改进性能的一个方法是让我们的工具"防错"。例如,有时模型在智能体移出根目录后可能会搞乱相对文件路径。为了防止这种情况,我们简单地让工具始终要求使用绝对路径

我们尝试了几种不同的策略来指定对现有文件的编辑,并发现字符串替换的可靠性最高,其中模型在给定文件中指定要用 new_str 替换的 old_str。只有当 old_str 恰好有一个匹配时,才会进行替换。如果匹配数量更多或更少,模型会看到适当的错误消息以便重试。

防错设计原则

问题解决方案效果
相对路径混乱强制使用绝对路径消除路径歧义
多处匹配要求唯一匹配精确定位修改
匹配失败显示错误消息允许重试
状态丢失跨调用持久化保持上下文

性能结果

总体而言,升级版 Claude 3.5 Sonnet 展示了比我们之前的模型和之前最先进的模型更高的推理、编码和数学能力。它还展示了改进的智能体能力:工具和脚手架有助于将这些改进的能力发挥到最佳用途。

性能对比表

模型SWE-bench Verified 得分
Claude 3.5 Sonnet (升级版)49%
之前的 SOTA45%
Claude 3.5 Sonnet (旧版)33%
Claude 3 Opus22%

所有模型均使用此智能体脚手架在 SWE-bench Verified 上的得分

性能提升路径

graph LR
    A[Claude 3 Opus<br/>22%] -->|+11%| B[Sonnet 旧版<br/>33%]
    B -->|+12%| C[之前 SOTA<br/>45%]
    C -->|+4%| D[Sonnet 新版<br/>49%]
    D -.->|目标| E[50% 里程碑]
    
    style D fill:#90EE90
    style E fill:#FFD700

智能体行为示例

为了运行基准测试,我们使用 SWE-Agent 框架作为智能体代码的基础。在下面的日志中,我们将智能体的文本输出、工具调用和工具响应呈现为 THOUGHT(思考)、ACTION(行动)和 OBSERVATION(观察),尽管我们并没有将模型限制在固定的顺序中。

以下代码块将演示 Sonnet 3.5 解决 SWE-bench 问题的典型案例。

步骤 1:初始提示和问题描述

在第一个代码块中,你可以看到给模型的部分初始提示,其中 {pr_description} 被填充为来自 SWE-bench 任务的实际值。重要的是,此任务包含重现问题的步骤,这将为模型提供宝贵的调查起点。

<uploaded_files>
/repo
</uploaded_files>
I've uploaded a python code repository in the directory /repo (not in /tmp/inputs). Consider the following PR description:

<pr_description>
linear_model.RidgeClassifierCV's Parameter store_cv_values issue

#### Description
Parameter store_cv_values error on sklearn.linear_model.RidgeClassifierCV

#### Steps/Code to Reproduce
import numpy as np
from sklearn import linear_model as lm

#test database
n = 100
x = np.random.randn(n, 30)
y = np.random.normal(size = n)

rr = lm.RidgeClassifierCV(alphas = np.arange(0.1, 1000, 0.1), normalize = True, 
                                         store_cv_values = True).fit(x, y)

#### Expected Results
Expected to get the usual ridge regression model output, keeping the cross validation predictions as attribute.

#### Actual Results
TypeError: __init__() got an unexpected keyword argument 'store_cv_values'

lm.RidgeClassifierCV actually has no parameter store_cv_values, even though some attributes depends on it.
</pr_description>

步骤 2:探索仓库结构

模型首先使用编辑工具查看仓库结构:

THOUGHT:
I'll help you implement the changes to satisfy the PR description. Let's follow the steps you outlined.

1. First, let's explore the repo structure:

ACTION (str_replace_editor):
command: view
path: /repo

OBSERVATION:
Here's the files and directories up to 2 levels deep in /repo, excluding hidden items:
/repo
/repo/PULL_REQUEST_TEMPLATE.md
/repo/ISSUE_TEMPLATE.md
/repo/sklearn
/repo/sklearn/decomposition
/repo/sklearn/kernel_ridge.py
/repo/sklearn/model_selection
/repo/sklearn/covariance
/repo/sklearn/cluster
/repo/sklearn/base.py
/repo/sklearn/dummy.py
/repo/sklearn/naive_bayes.py
...

步骤 3:创建重现脚本

模型使用编辑工具创建一个新脚本,用于重现问题并测试其修复:

THOUGHT:
Based on the repository structure, we'll need to modify the Ridge Classifier in the `sklearn/linear_model` directory.

Let's create a script to reproduce the error:

ACTION (str_replace_editor):
command: create
path: /repo/reproduce_error.py
file_text:
import numpy as np
from sklearn import linear_model as lm

#test database
n = 100
x = np.random.randn(n, 30)
y = np.random.normal(size = n)

rr = lm.RidgeClassifierCV(alphas=np.arange(0.1, 1000, 0.1), normalize=True, 
                         store_cv_values=True).fit(x, y)

OBSERVATION:
File created successfully at: /repo/reproduce_error.py

步骤 4:执行脚本确认错误

模型使用 Bash 工具执行脚本,成功重现问题:

THOUGHT:
Let's verify that we can reproduce the error:

ACTION (bash):
command: python3 /repo/reproduce_error.py

OBSERVATION:
Traceback (most recent call last):
  File "/repo/reproduce_error.py", line 10, in <module>
    store_cv_values=True).fit(x, y)
TypeError: __init__() got an unexpected keyword argument 'store_cv_values'

步骤 5:修复源代码

模型使用编辑工具更改仓库中的源代码:

THOUGHT:
The error is reproducible. Looking at the code, I can see that the issue is that while the `RidgeClassifierCV` class inherits from `_BaseRidgeCV`, it fails to pass the `store_cv_values` parameter from its constructor through to the base class constructor.

Let's fix this:

ACTION (str_replace_editor):
command: str_replace
path: /repo/sklearn/linear_model/ridge.py
old_str:
    def __init__(self, alphas=(0.1, 1.0, 10.0), fit_intercept=True,
                 normalize=False, scoring=None, cv=None, class_weight=None):
        super(RidgeClassifierCV, self).__init__(
            alphas=alphas, fit_intercept=fit_intercept, normalize=normalize,
            scoring=scoring, cv=cv)
        self.class_weight = class_weight

new_str:
    def __init__(self, alphas=(0.1, 1.0, 10.0), fit_intercept=True,
                 normalize=False, scoring=None, cv=None, class_weight=None,
                 store_cv_values=False):
        super(RidgeClassifierCV, self).__init__(
            alphas=alphas, fit_intercept=fit_intercept, normalize=normalize,
            scoring=scoring, cv=cv, store_cv_values=store_cv_values)
        self.class_weight = class_weight

问题解决工作流

sequenceDiagram
    participant Model as Claude 模型
    participant Edit as 编辑工具
    participant Bash as Bash 工具
    participant Test as 测试系统

    Model->>Edit: 查看仓库结构
    Edit-->>Model: 返回目录树
    Model->>Edit: 创建重现脚本
    Edit-->>Model: 脚本创建成功
    Model->>Bash: 执行重现脚本
    Bash-->>Model: 返回错误信息
    Model->>Model: 分析问题根因
    Model->>Edit: 修改源代码 (str_replace)
    Edit-->>Model: 修改成功
    Model->>Bash: 重新运行测试
    Bash-->>Model: 测试通过
    Model->>Test: 提交解决方案

任务完成

在这个特定示例中,模型工作了 12 个步骤后决定准备提交。然后任务的测试成功运行,验证了模型的解决方案解决了问题。一些任务在模型提交解决方案之前花费了超过 100 个回合;在其他任务中,模型一直尝试直到耗尽上下文。

从审查升级版 Claude 3.5 Sonnet 与旧模型的尝试中,我们发现升级版 3.5 Sonnet 更频繁地自我纠正。它还显示出尝试多种不同解决方案的能力,而不是一遍又一遍地犯同样的错误。


面临的挑战

SWE-bench Verified 是一个强大的评估,但它运行起来也比简单的单轮评估更复杂。以下是我们在使用它时面临的一些挑战——其他 AI 开发者也可能会遇到这些挑战。

1. 持续时间和高令牌成本

上面的示例来自一个在 12 个步骤中成功完成的案例。然而,许多成功的运行需要数百个回合才能解决,并且使用了超过 10 万个令牌。升级版 Claude 3.5 Sonnet 非常坚韧:如果有足够的时间,它通常可以找到解决问题的方法,但这可能很昂贵。

2. 评分问题

在检查失败的任务时,我们发现了一些模型行为正确但存在环境设置问题,或安装补丁被应用两次的问题的情况。解决这些系统问题对于准确了解 AI 智能体的性能至关重要。

3. 隐藏测试

由于模型无法看到它被评分的测试,它经常"认为"自己已经成功,但任务实际上是失败的。其中一些失败是因为模型在错误的抽象级别上解决了问题(打补丁而不是更深层次的重构)。其他失败感觉不太公平:它们解决了问题,但与原始任务的单元测试不匹配。

4. 多模态能力未充分利用

尽管升级版 Claude 3.5 Sonnet 具有出色的视觉和多模态能力,但我们没有实现让它查看保存到文件系统或作为 URL 引用的文件的方法。这使得调试某些任务(特别是来自 Matplotlib 的任务)特别困难,并且也容易导致模型产生幻觉。这里对于开发者来说肯定有唾手可得的成果可以改进——而且 SWE-bench 已经推出了一个专注于多模态任务的新评估。我们期待看到开发者在不久的将来使用 Claude 在这个评估上取得更高的分数。

挑战与应对策略

挑战具体表现影响潜在解决方案
高成本100+ 回合,10万+ tokens资源密集优化提示词,减少无效尝试
评分复杂性环境问题,补丁重复误判性能标准化环境,改进测试流程
隐藏测试模型无法看到评分标准误判成功提供测试提示,改进反馈
多模态受限无法查看图片/可视化调试困难集成视觉能力,支持文件预览

总结与展望

升级版 Claude 3.5 Sonnet 在 SWE-bench Verified 上取得了 49% 的成绩,超越了之前最先进的 45%,仅使用了一个简单的提示词和两个通用工具。我们相信,使用新的 Claude 3.5 Sonnet 进行构建的开发者将很快找到新的、更好的方法来提高 SWE-bench 分数,超过我们在这里最初展示的水平。


致谢

Erik Schluntz 优化了 SWE-bench 智能体并撰写了这篇博客文章。Simon Biggs、Dawn Drain 和 Eric Christiansen 帮助实现了基准测试。Shauna Kravec、Dawn Drain、Felipe Rosso、Nova DasSarma、Ven Chandrasekaran 以及许多其他人为训练 Claude 3.5 Sonnet 在智能体编码方面表现出色做出了贡献。


总结

核心要点

  1. 性能里程碑:Claude 3.5 Sonnet 达到 49%,距离 50% 里程碑仅一步之遥

  2. 架构创新:最小化脚手架 + 最大化模型自主权的设计理念

  3. 工具设计:两个精心设计的通用工具(Bash + 编辑)胜过复杂系统

  4. 设计哲学

    • 让 AI 自主决策工作流程,而非硬编码固定模式
    • 绝对路径、精确匹配等设计减少错误
    • 像设计人机界面一样精心设计 AI-工具界面
  5. 智能体能力

    • 能够尝试多种方案,不重复犯错
    • 可以进行 100+ 回合的长期问题解决
    • 在实际工程任务上表现出色

原文作者: Erik Schluntz (Anthropic Engineering Team)

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com