获取开发者新闻通讯
产品更新、操作指南、社区聚焦等。每月发送到您的收件箱。
我们最新的模型,升级版 Claude 3.5 Sonnet,在 SWE-bench Verified(一个软件工程评估)上达到了 49%,超过了之前最先进模型的 45%。本文解释了我们围绕该模型构建的“代理”,旨在帮助开发者从 Claude 3.5 Sonnet 中获得最佳性能。
SWE-bench 是一个 AI 评估基准,用于评估模型完成真实世界软件工程任务的能力。具体来说,它测试模型如何解决来自流行开源 Python 仓库的 GitHub 问题。对于基准中的每个任务,AI 模型会获得一个设置好的 Python 环境以及问题解决前的仓库检出(本地工作副本)。然后,模型需要理解、修改和测试代码,再提交其提出的解决方案。
每个解决方案都会根据关闭原始 GitHub 问题的拉取请求中的真实单元测试进行评分。这测试了 AI 模型是否能够实现与 PR 原始人类作者相同的功能。
SWE-bench 不仅仅孤立地评估 AI 模型,而是评估整个“代理”系统。在此上下文中,“代理”指的是 AI 模型及其周围的软件脚手架的组合。该脚手架负责生成输入模型的提示、解析模型的输出以采取行动,以及管理交互循环,其中模型先前行动的结果被纳入其下一个提示。即使使用相同的底层 AI 模型,代理在 SWE-bench 上的性能也可能因脚手架的不同而有显著差异。
还有许多其他用于大型语言模型编码能力的基准,但 SWE-bench 因以下几个原因而越来越受欢迎:
请注意,原始 SWE-bench 数据集包含一些在没有 GitHub 问题之外的额外上下文的情况下无法解决的任务(例如,关于要返回的特定错误消息)。SWE-bench-Verified 是 SWE-bench 的一个包含 500 个问题的子集,已经过人工审查以确保它们可解决,因此提供了编码代理性能的最清晰衡量标准。这是我们在本文中提到的基准。
我们为更新版 Claude 3.5 Sonnet 优化创建代理脚手架时的设计理念是尽可能将控制权交给语言模型本身,并保持脚手架最小化。该代理有一个提示、一个用于执行 bash 命令的 Bash 工具,以及一个用于查看和编辑文件和目录的 Edit 工具。我们持续采样,直到模型决定完成,或超过其 200k 上下文长度。这个脚手架允许模型自行判断如何解决问题,而不是被硬编码到特定模式或工作流程中。
提示概述了模型建议的方法,但对于此任务来说并不过长或过于详细。模型可以自由选择如何逐步推进,而不是有严格和离散的转换。如果您不关心 token 数量,明确鼓励模型生成长响应可能会有所帮助。
以下代码显示了我们的代理脚手架中的提示:
<uploaded_files>
{location}
</uploaded_files>
我已经在目录 {location}(不是 /tmp/inputs)中上传了一个 Python 代码仓库。请考虑以下 PR 描述:
<pr_description>
{pr_description}
</pr_description>
你能帮我实现仓库中必要的更改,以满足 <pr_description> 中指定的要求吗?
我已经处理了 <pr_description> 中描述的所有测试文件的更改。这意味着你不需要以任何方式修改测试逻辑或任何测试!
你的任务是对 {location} 目录中的非测试文件进行最小更改,以确保 <pr_description> 得到满足。
按照以下步骤解决该问题:
1. 作为第一步,探索仓库以熟悉其结构可能是个好主意。
2. 创建一个脚本来复现错误,并使用 BashTool 通过 `python <filename.py>` 执行它,以确认错误
3. 编辑仓库的源代码以解决该问题
4. 重新运行你的复现脚本并确认错误已修复!
5. 考虑边界情况,并确保你的修复也能处理它们
你的思考应该彻底,所以即使很长也没关系。
模型的第一个工具执行 Bash 命令。其模式很简单,只接收要在环境中运行的命令。然而,工具的描述承载了更多分量。它包含给模型的更详细指令,包括转义输入、无互联网访问,以及如何在后台运行命令。
接下来,我们展示 Bash 工具的规范:
{
"name": "bash",
"description": "在 bash shell 中运行命令\n
* 调用此工具时,\"command\" 参数的内容不需要进行 XML 转义。\n
* 你无法通过此工具访问互联网。\n
* 你可以通过 apt 和 pip 访问常见 linux 和 python 软件包的镜像。\n
* 状态在命令调用以及与用户的讨论之间是持久的。\n
* 要查看文件的特定行范围,例如第 10-25 行,请尝试 'sed -n 10,25p /path/to/the/file'。\n
* 请避免可能产生大量输出的命令。\n
* 请在后台运行长时间运行的命令,例如 'sleep 10 &' 或在后台启动服务器。",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "要运行的 bash 命令。"
}
},
"required": ["command"]
}
}
该模型的第二个工具(编辑工具)要复杂得多,包含了模型查看、创建和编辑文件所需的一切。同样,我们的工具描述中包含了供模型使用的、关于如何使用该工具的详细信息。
我们在各种智能体任务中,为这些工具的描述和规范投入了大量精力。我们对它们进行了测试,以发现模型可能误解规范的任何方式,或使用这些工具可能存在的陷阱,然后编辑描述以预先防范这些问题。我们认为,应当像为人类设计工具界面时投入大量注意力那样,为模型设计工具界面投入多得多的注意力。
以下代码展示了我们编辑工具的描述:
{
"name": "str_replace_editor",
"description": "用于查看、创建和编辑文件的自定义编辑工具\n
* 状态在命令调用和与用户的讨论之间持久保留\n
* 如果 `path` 是文件,`view` 会显示应用 `cat -n` 后的结果。如果 `path` 是目录,`view` 会列出最多 2 层深度的非隐藏文件和目录\n
* 如果指定的 `path` 已作为文件存在,则无法使用 `create` 命令\n
* 如果 `command` 生成较长的输出,它将被截断并标记为 `<response clipped>` \n
* `undo_edit` 命令将还原对 `path` 处文件所做的最后一次编辑\n
\n
使用 `str_replace` 命令的注意事项:\n
* `old_str` 参数应准确匹配原始文件中的一行或多行连续内容。注意空白字符!\n
* 如果 `old_str` 参数在文件中不唯一,则不会执行替换。请确保在 `old_str` 中包含足够的上下文以使其唯一\n
* `new_str` 参数应包含用于替换 `old_str` 的已编辑行",
...
我们改进性能的一种方式是让工具“防错”。例如,有时在智能体移出根目录后,模型可能会弄错相对文件路径。为防止这种情况,我们直接让该工具始终要求使用绝对路径。
我们尝试了多种为现有文件指定编辑的不同策略,其中字符串替换的可靠性最高,即模型指定 old_str,将其替换为给定文件中的 new_str。只有当 old_str 恰好匹配一次时,才会执行替换。如果匹配次数多于或少于一次,模型会看到相应的错误消息,以便重试。
我们的编辑工具规范如下所示:
...
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"enum": ["view", "create", "str_replace", "insert", "undo_edit"],
"description": "要运行的命令。允许的选项有:`view`、`create`、`str_replace`、`insert`、`undo_edit`。"
},
"file_text": {
"description": "`create` 命令的必需参数,包含要创建的文件的內容。",
"type": "string"
},
"insert_line": {
"description": "`insert` 命令的必需参数。`new_str` 将插入到 `path` 的第 `insert_line` 行之后。",
"type": "integer"
},
"new_str": {
"description": "`str_replace` 命令的必需参数,包含新字符串。`insert` 命令的必需参数,包含要插入的字符串。",
"type": "string"
},
"old_str": {
"description": "`str_replace` 命令的必需参数,包含 `path` 中要替换的字符串。",
"type": "string"
},
"path": {
"description": "文件或目录的绝对路径,例如 `/repo/file.py` 或 `/repo`。",
"type": "string"
},
"view_range": {
"description": "当 `path` 指向文件时,`view` 命令的可选参数。如果未提供,则显示整个文件。如果提供,文件将按指定的行号范围显示,例如 [11, 12] 将显示第 11 行和第 12 行。索引从 1 开始。设置 `[start_line, -1]` 将显示从 `start_line` 到文件末尾的所有行。",
"items": {
"type": "integer"
},
"type": "array"
}
},
"required": ["command", "path"]
}
}
总体而言,升级后的 Claude 3.5 Sonnet 在推理、编码和数学能力上均优于我们之前的模型,以及先前的最先进模型。它还展示了改进的代理能力:工具和脚手架有助于将这些改进的能力发挥到最佳效果。
| 模型 | Claude 3.5 Sonnet(新) | 先前的最先进 | Claude 3.5 Sonnet(旧) | Claude 3 Opus |
|---|---|---|---|---|
| SWE-bench 验证分数 | 49% | 45% | 33% | 22% |
为了运行基准测试,我们使用了 SWE-Agent 框架作为我们代理代码的基础。在下面的日志中,我们将代理的文本输出、工具调用和工具响应分别呈现为 THOUGHT、ACTION 和 OBSERVATION,尽管我们并未限制模型必须遵循固定的顺序。
下面的代码块将展示 Sonnet 3.5 解决一个 SWE-bench 问题的典型情况。
在第一个代码块中,您可以看到给模型的初始提示的一部分,其中 {pr_description} 被替换为来自 SWE-bench 任务的真实值。重要的是,该任务包含了重现问题的步骤,这将为模型提供一个有价值的调查起点。
<uploaded_files>
/repo
</uploaded_files>
我在目录 /repo(不是 /tmp/inputs)中上传了一个 Python 代码仓库。请考虑以下 PR 描述:
<pr_description>
linear_model.RidgeClassifierCV 的参数 store_cv_values 问题
#### 描述
sklearn.linear_model.RidgeClassifierCV 上的参数 store_cv_values 错误
#### 复现步骤/代码
import numpy as np
from sklearn import linear_model as lm
#测试数据库
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)
#### 预期结果
预期得到通常的岭回归模型输出,并将交叉验证预测保留为属性。
#### 实际结果
TypeError: __init__() got an unexpected keyword argument 'store_cv_values'
lm.RidgeClassifierCV 实际上没有参数 store_cv_values,尽管某些属性依赖于它。
模型作出响应,首先使用 Edit Tool 查看仓库结构。你可以在 THOUGHT 和 ACTION 下看到模型的文本输出和工具调用参数,并在 OBSERVATION 下看到工具的部分输出:
THOUGHT:
我会帮你实现这些更改,以满足 PR 描述。让我们按照你列出的步骤进行。
1. 首先,让我们探索仓库结构:
ACTION (str_replace_editor):
command:
view
path:
/repo
OBSERVATION:
以下是 /repo 中最多 2 层深度的文件和目录,不包括隐藏项:
/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
...
现在模型对仓库结构有了更好的理解,它使用 Edit 工具创建一个新脚本,用于复现该问题并测试其修复方案:
THOUGHT:
根据仓库结构,我们需要修改 `sklearn/linear_model` 目录中的 Ridge Classifier。
让我们创建一个脚本来复现该错误:
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
然后,模型使用 Bash 工具执行它编写的脚本,并成功复现了任务中的问题:
THOUGHT:
让我们验证一下能否复现该错误:
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'
从这里开始,模型使用编辑工具更改仓库中的源代码,并重新运行其脚本以验证更改是否解决了该问题:
THOUGHT:
该错误可以复现。查看代码,我可以看到问题在于,虽然 `RidgeClassifierCV` 类继承自 `_BaseRidgeCV`,但它未能将构造函数中的 `store_cv_values` 参数传递给基类构造函数。
让我们修复这个问题:
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
在这个特定示例中,模型运行了 12 步后才决定可以提交。随后,任务的测试成功运行,验证了模型的解决方案解决了问题。有些任务在模型提交解决方案前进行了超过 100 轮;在其他任务中,模型不断尝试,直到上下文用尽。
通过审查更新后的 Claude 3.5 Sonnet 与旧模型的尝试对比,更新后的 3.5 Sonnet 更频繁地自我纠正。它还显示出尝试多种不同解决方案的能力,而不是反复犯同样的错误。
SWE-bench Verified 是一个强大的评估工具,但运行起来也比简单的单轮评估更复杂。以下是我们在使用它时面临的一些挑战——其他 AI 开发者也可能遇到这些挑战。
升级后的 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 在代理编码方面表现出色做出了贡献。
产品更新、操作指南、社区亮点等。每月发送到您的收件箱。
