以每秒 1000 个 Token 的速度编辑文件
像 GPT-4o 这类前沿模型在进行大幅度编辑时表现不佳,常见问题包括偷懒、不准确和高延迟。
这是编码代理显而易见的一个短板。
要精确地编辑上百行代码,往往需要多次模型调用,有时甚至会把代理困在无限循环中。即便是小而独立的改动也常常问题不断。
更糟的是,现有模型在进行大幅度编辑时速度很慢,打断程序员的工作流。
我们针对全文件代码编辑任务的一项重要变体进行了专项训练,名为快速应用(fast apply)。
复杂的代码修改可以分为两个阶段:规划和应用。
在 Cursor 中,规划阶段以与强大的前沿模型进行聊天的界面呈现。
将更改应用到当前文件应当简单且即时完成。

我们的快速应用模型在性能上超越了 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。

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

我们推断 Claude 的出色表现主要源于后训练阶段的影响。
Claude 模型常在助手消息中输出数千行代码(LOC),而 GPT-4 则会省略代码,并以 ... 或注释标注缺失区域。
GPT-4 的表现不佳也可能是由于无关的改动所致。
GPT-4 会删除被注释掉的代码和不必要的空行。
它往往会“修复/清理”与当前任务无关的代码。
速度测量
我们将速度衡量为:
其优势包括:
- 在不同分词器间标准化速度
- 在不同提示/生成的 token 长度下,提供一个我们真正关心的单一数值指标(而非同时统计 TTFT 与生成速度,字符/秒)
- 由于延迟包含 TTFT,这可以为生成速度提供一个较紧的下界。对大多数分词器和文本而言,单个 token 约等于 3–4 个字符,因此将字符/秒除以 4 可得到每秒 token 数的下界。

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

差异模型
为什么我们让模型重写整个文件,而不是只提供差异(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 的 apply 模型比 gpt-4-turbo-spec 更快也更准确,但仍然太慢。
Anthropic 的任何模型目前都无法内置“speculative edits”,因此我们需要训练并部署一个高性能的自定义模型。
合成数据
我们一开始提供少量“快速应用”提示,并大量提供 cmd‑K 提示。

cmd‑k 命令框与我们在快速应用中所需的数据大致相似。
它包含一条编辑指令和当前文件中的代码选区。
针对每条编辑指令,我们先让 GPT-4 基于当前文件生成一条对话响应,然后再让一个语言模型“应用”这些更改。
我们使用一小批来自“真实”应用输入的数据来合成更多更高质量的应用样本。
最后,将这些数据集按 80/20 的比例合并,得到我们的微调数据。

模型训练
我们训练 Deepseek Coder Instruct 和 Llama 3 系列模型。为改进我们的微调数据集,我们将:
- 我们对小文件进行降采样,因为它们在我们的训练集中占比过高(<100 行代码)。
- 我们对每个文件名对应的训练样本数量进行降采样。
- 我们会对产生空操作(no-op)的数据点进行降采样。

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

这三个微调模型在评测中都优于 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 倍的加速。

未来规划
长上下文训练 - 我们正在开展长上下文训练,可重写最长达 2500 行的文件。
对 RoPE 位置 ID 进行朴素的线性扩展效果欠佳——社区当前对 Llama 3 70B 的长上下文微调同样表现不佳。
知识蒸馏 - 我们还计划将当前模型的“快速应用”(fast apply)能力蒸馏到更小的模型中,尤其是 llama-3-8b。
对于更大的文件,更小模型所带来的更低延迟将更加关键。
更高的准确率——基于新近发布模型数据开展某种形式的 on-policy 强化学习(RL),有望带来额外的性能提升。
除了在聊天中的实用性外,fast-apply 也是构建更复杂代码生成系统的关键模块。
随着模型在推理/规划方面的能力增强,低时延的 apply 将带来越来越多的收益!
如果你还没试过,建议在 Cursor 里体验一下这个功能!这是一个小小的
例子,能体现产品的打磨和深度。
这篇博文大致代表了我们在 Anysphere 开展的应用研究。
我们构建面向具体应用的推理加速(例如“speculative edits”)、训练并评估面向任务的模型,并将它们打造成对用户有价值的功能发布。
我们正在招聘研究工程师和软件工程师!在这里了解更多关于 Anysphere 的信息。