跳至内容

以每秒 1000 个 Token 的速度编辑文件

作者: Aman Sanger归入研究


像 GPT-4o 这类前沿模型在进行大幅度编辑时表现不佳,常见问题包括偷懒、不准确和高延迟。

这是编码代理显而易见的一个短板。
要精确地编辑上百行代码,往往需要多次模型调用,有时甚至会把代理困在无限循环中。即便是小而独立的改动也常常问题不断。

更糟的是,现有模型在进行大幅度编辑时速度很慢,打断程序员的工作流。

我们针对全文件代码编辑任务的一项重要变体进行了专项训练,名为快速应用(fast apply)

复杂的代码修改可以分为两个阶段:规划应用

在 Cursor 中,规划阶段以与强大的前沿模型进行聊天的界面呈现。
将更改应用到当前文件应当简单且即时完成。

A toy example of a change we want to 'apply'. It cannot easily be copy/pasted since it sketches out the change over a full class

我们的快速应用模型在性能上超越了 GPT-4 和 GPT-4o,并在准确度/延迟曲线上进一步推动了帕累托前沿。
借助一种专为代码编辑定制的推测式解码变体(称为 speculative edits),我们在 70B 模型上实现了约 ~1000 tokens** 的生成速度(约 3500 字符/秒)。

这意味着相比使用 Llama-3-70b 的标准推理,速度提升约 13 倍;相比我们此前基于 GPT-4 的“投机式编辑”(speculative edits)部署,速度提升约 9 倍。

默认情况下,我们会让语言模型在当前文件对话历史当前代码块的基础上生成完整重写的文件

在这篇文章中,我们将介绍我们如何训练并评估全新模型。
我们将解释为何选择重写文件而非使用 diff,以及推测性编辑如何带来极致的速度提升。

评估基于提示的重写

我们构建了一个评估集,包含约 450 个对不超过 400 行的文件所进行的全文件级编辑,然后使用 Claude-3 Opus 作为评审器来评估多个经提示的模型的表现。

在数十个精心挑选的示例中,基于 Opus 的评分与我们的评级一致性高于 GPT-4-Turbo 或 GPT-4o。

The Priompt component for grading guidelines

这些评分很可能偏向 Claude 模型的输出。
但这些评分也与我们对这些模型的定性评估一致。

Surpisingly, claude-3-sonnet outperforms gpt-4-turbo. And gpt-4o performs similarly to gpt-4-turbo.

我们推断 Claude 的出色表现主要源于后训练阶段的影响。
Claude 模型常在助手消息中输出数千行代码(LOC),而 GPT-4 则会省略代码,并以 ... 或注释标注缺失区域。

GPT-4 的表现不佳也可能是由于无关的改动所致。
GPT-4 会删除被注释掉的代码和不必要的空行。
它往往会“修复/清理”与当前任务无关的代码。

速度测量

我们将速度衡量为:

速度=重写字符数重写耗时(秒)\text{速度} = \frac{\text{重写字符数}}{\text{重写耗时(秒)}}

其优势包括:

  1. 在不同分词器间标准化速度
  2. 在不同提示/生成的 token 长度下,提供一个我们真正关心的单一数值指标(而非同时统计 TTFT 与生成速度,字符/秒)
  3. 由于延迟包含 TTFT,这可以为生成速度提供一个较紧的下界。对大多数分词器和文本而言,单个 token 约等于 3–4 个字符,因此将字符/秒除以 4 可得到每秒 token 数的下界。
Top and to the right is better. opus, sonnet, gpt-4o, and haiku are on the pareto frontier.

当我们将 gpt-4-turbo 的推测式编辑带来的加速考虑在内时:

With speculative edits, gpt-4-turbo performs similarly to gpt-4o. Speculative edits are not yet possible with gpt-4o, otherwise gpt-4o-spec would be the speed frontrunner.

差异模型

为什么我们让模型重写整个文件,而不是只提供差异(diff)建议?

我们发现语言模型在处理 diff 格式的修改时表现不佳,可能有以下几个原因:

用更少的 Token 思考 - 输出的 token 越多,模型就有越多的前向传递来找到正确的解答。
Diff 会促使模型用更少的 token 来思考。

Diff 属于分布外数据 -
在预训练阶段,尤其是后训练阶段,模型更常见的是完整的代码文件,而非代码 diff。

输出行号 - 如果某个 tokenizer 将一串数字(如 123)当作单个 token 处理,就必须在某一个(通常是第一个)输出 token 上确定 diff 的正确行号
此外,模型在数行号方面一向不擅长。

Aider的 diff 格式启发,我们消除了行号问题。
模型不采用标准的 diff 格式,而是将 diff hunk 作为搜索/替换块提出:

diff
@@ ... @@
function binarySearch(arr, x) {
- let low = 0, high = arr.length - 1;
- while (low <= high) {
- let mid = Math.floor((low + high) / 2);
- if (arr[mid] === x) {
- return mid;
- }
- low += 1;
- }
- return -1;
+ let low = 0, high = arr.length - 1;
+ while (low <= high) {
+ let mid = Math.floor((low + high) / 2);
+ if (arr[mid] === x) {
+ return mid;
+ } else if (arr[mid] < x) {
+ low = mid + 1;
+ } else {
+ high = mid - 1;
+ }
+ }
+ return -1;
}

我们将所有以 - 或 开头的行替换为以 + 或 开头的行。
这些 diff 包含冗余的 -+ ,以便差异解析系统在出现轻微模型失误时依然稳健。

除 Claude Opus 外,大多数模型都无法输出准确的 diff。

claude-3-opus-diff is Claude Opus prompted with a modified version of Aider's diff format. It beats gpt-4-turbo-spec on speed and accuracy. gpt-4o-diff is omitted from the graph as it achieves a median speed of 2476 char/s, but performs far worse than claude-3-haiku (4.18 avg eval score).

训练

claude-3-opus-diff 的 apply 模型比 gpt-4-turbo-spec 更快也更准确,但仍然太慢

Anthropic 的任何模型目前都无法内置“speculative edits”,因此我们需要训练并部署一个高性能的自定义模型。

合成数据

我们一开始提供少量“快速应用”提示,并大量提供 cmd‑K 提示。

An example [cmd-k](/features#cmd-k) prompt and the corresponding generation. Cmd-k lets you instruct the model to make an edit in a selected region.

cmd‑k 命令框与我们在快速应用中所需的数据大致相似。
它包含一条编辑指令和当前文件中的代码选区。

针对每条编辑指令,我们先让 GPT-4 基于当前文件生成一条对话响应,然后再让一个语言模型“应用”这些更改。
我们使用一小批来自“真实”应用输入的数据来合成更多更高质量的应用样本。
最后,将这些数据集按 80/20 的比例合并,得到我们的微调数据。

Our data pipeline for generating the fast-apply finetuning dataset. The fully synthetic data is lower quality. For instance, in the example above, the selection range (which we throw away) is crucial for an accurate edit.

模型训练

我们训练 Deepseek Coder Instruct 和 Llama 3 系列模型。为改进我们的微调数据集,我们将:

  1. 我们对小文件进行降采样,因为它们在我们的训练集中占比过高(<100 行代码)。
  2. 我们对每个文件名对应的训练样本数量进行降采样。
  3. 我们会对产生空操作(no-op)的数据点进行降采样。

我们发现,我们的最佳模型(llama-3-70b-ft)的表现几乎与 claude-3-opus-diff 持平,并优于 gpt-4-turbo 和 gpt-4o。

Llama-3-70b-ft outperforms gpt-4-turbo-spec in performance.

这三个微调模型在评测中都优于 gpt-4-turbo,但我们确实能明显感受到微调后的 deepseek-33b 与 llama-3-70b 之间的差异。
Llama-3-70b 的使用感受优于其他微调模型和 gpt-4-turbo。
其他微调模型的实际效用不够,往往还不如 GPT-4。

预估式编辑

我们最大的成果来自自研的推测式解码算法,称为“speculative edits”。
它等同于整文件重写,但速度最高可提升至 9 倍。

在代码编辑中,我们在任意时刻对草稿 token 有很强的先验,因此可以不用草稿模型,而是用确定性算法来推测后续 token。

我们与Fireworks合作,部署了我们的快速应用模型,并提供了对推测式编辑的强力支持。
他们拥有出色的推理引擎,并为我们的自定义推测逻辑构建了完善的 API 支持。

这意味着对 Llama-3 而言,“推测式编辑”带来的收益高于 GPT-4,从而比下一个最快的模型实现约 4–5 倍的加速。

Speculative edits give our finetuned model a **massive** speedup.

未来规划

长上下文训练 - 我们正在开展长上下文训练,可重写最长达 2500 行的文件。
对 RoPE 位置 ID 进行朴素的线性扩展效果欠佳——社区当前对 Llama 3 70B 的长上下文微调同样表现不佳。

知识蒸馏 - 我们还计划将当前模型的“快速应用”(fast apply)能力蒸馏到更小的模型中,尤其是 llama-3-8b。
对于更大的文件,更小模型所带来的更低延迟将更加关键。

更高的准确率——基于新近发布模型数据开展某种形式的 on-policy 强化学习(RL),有望带来额外的性能提升。

除了在聊天中的实用性外,fast-apply 也是构建更复杂代码生成系统的关键模块。
随着模型在推理/规划方面的能力增强,低时延的 apply 将带来越来越多的收益!

如果你还没试过,建议在 Cursor 里体验一下这个功能!这是一个小小的
例子,能体现产品的打磨和深度。

这篇博文大致代表了我们在 Anysphere 开展的应用研究。
我们构建面向具体应用的推理加速(例如“speculative edits”)、训练并评估面向任务的模型,并将它们打造成对用户有价值的功能发布。

我们正在招聘研究工程师和软件工程师!在这里了解更多关于 Anysphere 的信息。

归类于: 研究

作者: Aman Sanger