Skip to content

Cursor 案例

如果说 Claude Code 代表了终端系 agent 的 harness 路线——通用工具、显式文件操作、把智能尽量留给模型——那么 Cursor 代表了另一条同样成功的路线:把 harness 深埋进编辑器本身。它不满足于"模型 + 通用工具",而是围绕 IDE 这一宿主环境,自建了索引管道、专用模型和编辑协议。

Cursor 值得单独解剖的原因在于:它的团队公开了异常多的 harness 工程细节——从代码库索引的 Merkle 树同步,到为什么不用 diff 格式改代码,再到 Tab 模型与 agent 模型的分工。这些官方博客和文档让我们能清晰地看到一个道理:当宿主环境从终端换成 IDE,harness 的设计空间整体平移了,但问题一个都没少。

设计空间:IDE 系 vs 终端系

先建立全局对比。两个产品的底层 agent loop 是同一个物种(思考 → 工具调用 → 观察 → 再思考,见 Agent Loop),差异全在 harness 层:

维度终端系(Claude Code)IDE 系(Cursor)
宿主环境shell,模型自己探索一切VS Code fork,编辑器状态唾手可得
上下文来源显式工具调用(Read/Grep/Glob)隐式编辑器状态 + 索引检索 + 显式工具调用
交互节拍委托一段任务,异步围观人在回路内,编辑、补全、agent 三种粒度并存
代码编辑落地模型直接生成新文件内容,字符串精确匹配规划模型 + 专用 apply 模型的两段式管线
大规模代码库靠 agent 自主探索(grep/find/read)预先建 embedding 索引,检索喂给模型
回滚机制git + 用户审查 diff检查点(checkpoint)快照,一键还原

这张表里没有谁是"更先进"的一边,而是两个不同的赌注:终端系赌模型的探索能力,harness 保持薄而通用;IDE 系赌环境信号的价值,harness 变厚,专门化程度更高。Cursor 官方文档对 agent 的构成描述得很坦白——"说明(系统提示词和规则)、工具、模型"三部分,且"针对每个前沿模型专门优化说明和工具"(Agent 文档)。这是一句典型的 harness 厂商自白:模型是可替换的,harness 是自家产品。

为什么 Cursor 选择 fork VS Code 而不是做插件

这个决定本身就是一次 harness 架构决策。IDE 插件运行在宿主的扩展沙箱里,能读到的编辑器状态、能控制的 UI 渲染都受插件 API 约束;而 Tab 的幽灵文本、跨文件跳转门户、diff 逐块审批、检查点时间线——这些交互都需要深度改造编辑器本体。Fork 的代价是背上整个编辑器的维护负担,收益是 harness 获得了对"上下文采集面"和"人机交互面"的完全控制。这与 Claude Code 选择终端恰好互为镜像:一个把宿主做厚,一个把宿主做到近乎为零。

一个值得记住的视角

IDE 系 harness 的本质优势不是"有个图形界面",而是编辑器是一个持续产生高价值结构化信号的进程:用户此刻在看哪个文件、光标停在哪、刚才改了什么、哪些文件标红报错。这些信号在终端里要么不存在,要么要靠模型花工具调用去重建。Cursor 的整个 harness 设计,就是围绕"如何把这些信号低成本地喂给模型"展开的。

上下文供给:代码库索引是一道基础设施题

终端系 agent 面对一个十万文件的 monorepo,靠的是"边干边看"——grep 关键词、读目录、顺着 import 爬。这条路能用,但每一步都在烧轮次。Cursor 的选择是事前把代码库变成可检索的索引,这是 上下文工程里"离线预处理"路线的工业级样本。

根据 Cursor 官方博客《Secure Codebase Indexing》,这套索引管道长这样:

text
┌─────────────────── 客户端(本地) ───────────────────┐
│                                                      │
│  扫描工作区 ──> 按语法切块(syntactic chunks)          │
│       │                                              │
│       ▼                                              │
│  逐文件计算 SHA-256 ──> 构建 Merkle 树                │
│  (目录哈希 = 子节点哈希的哈希)                        │
│       │                                              │
│       ▼ 只上传哈希,不传代码                            │
└───────┼──────────────────────────────────────────────┘

┌─────────────────── 服务端 ───────────────────────────┐
│  对比客户端与服务端的 Merkle 树:                       │
│    · 根哈希相同 → 无需任何操作                         │
│    · 只走哈希不同的分支 → 精确找出变更文件              │
│       │                                              │
│       ▼                                              │
│  变更文件切块 ──> 计算 embedding ──> 写入向量库          │
│  (未变更的块命中内容缓存,不重算)                      │
│       │                                              │
│       ▼                                              │
│  查询时:embedding 检索相关代码块,回传客户端解密        │
│  (文件名混淆、代码块加密、服务端不留明文源码)          │
└──────────────────────────────────────────────────────┘

几个工程细节值得展开:

Merkle 树解决的是"同步"而不是"检索"。 五万个文件的工作区,仅文件名加 SHA-256 哈希就约 3.2 MB——如果没有树结构,每次同步都要搬运这份全量清单;有了树,根哈希相同即整体跳过,只有哈希分叉的子树需要下钻,变更检测的成本从 O(全量) 降到 O(变更数 × 树深)。这是 git 用了二十年的老技术,被原样搬到了 embedding 索引的增量更新上。

切块与缓存决定维护成本。 文件变更后按语法结构切块,embedding 按块内容缓存——大多数编辑只改动少数块,未变的块直接命中缓存。索引因此"更新快、维护轻",这是它能作为常驻基础设施而不是一次性批处理的前提。

团队级索引复用。 同一组织内,同一代码库的不同克隆平均有 92% 的相似度(Cursor 官方数据)。新成员加入时,客户端从 Merkle 树派生一个 simhash(相似性哈希)上传,服务端在同团队的 simhash 向量库里找超过阈值的已有索引,直接复制作为初始索引——把最大仓库上"数小时"的首次索引时间压到"数秒"。

值不值得做?官方给了数字。 Cursor 的内部评测称语义检索平均提升回答准确率 12.5%,且产出的代码改动更可能被保留进代码库。语义检索是他们认定的"agent 性能的最大驱动因素之一"。

索引不是免费的

这套管道的代价是隐私面:代码要切块、上传、在服务端算 embedding。Cursor 的应对是文件名混淆、代码块加密、客户端解密(见其代码库索引文档),并支持 .cursorignore。但架构上它终究是"代码离开本机"的方案——这正是终端系 agent 用"本地工具调用、按需读取"天然回避掉的权衡。选哪条路,取决于你对代码出域的容忍度。

索引管语义检索,精确查找另有其道:Cursor 还内置了自研的 Instant Grep 搜索引擎(官方宣称在大代码库上快过 ripgrep),agent 引用具体符号时自动走精确匹配。语义检索负责"发现",精确匹配负责"定位",两条通道互补。此外 agent 还能派出一个 Explore 子代理——在独立上下文窗口里用更快的模型并行做大量搜索,只把结论带回主对话,避免原始文件内容污染主上下文(这正是子代理作为上下文隔离手段的标准用法)。

自动检索之上还有一层用户手动投喂@ 符号体系让用户显式指定上下文——@Files 钉死具体文件、@Code 引用代码符号、@Docs 注入第三方库文档、@Web 触发联网搜索、@Git 拉取提交历史。这不是检索系统的冗余,而是对它的纠偏机制:embedding 检索是召回导向的("可能相关"),而用户经常确切知道答案在哪。把人变成上下文供给的最后一环,是 IDE 系 harness 对"检索必然有噪声"这一现实的务实回应——同样的问题在终端系里则表现为用户直接在对话里贴路径和文件内容。

IDE 独有的上下文:编辑器状态即 prompt

索引解决的是"整个代码库"的供给,IDE 系还有第二张牌:编辑器这个进程本身就是一台上下文生产机。Cursor 的 agent 与 Tab 能读到的信号包括:

  • 打开的文件与最近浏览记录——用户的注意力分布,是"哪些代码相关"的强先验;
  • 光标位置与选区——任务的焦点坐标,Cursor 这个产品名就来自此;
  • 编辑历史——用户刚做了什么,直接暗示下一步要做什么;
  • 诊断信息(linter errors)——语言服务器算好的类型错误、未定义符号,零成本的结构化反馈;
  • 终端历史与命令输出——构建和测试的即时结果。

注意这份清单的性质:它们不是模型调用工具"查"来的,而是 harness 顺手采集的。语言服务器本来就在跑、编辑历史本来就在记——harness 做的只是把这些进程内状态序列化进 prompt。这与终端系 agent 形成了鲜明对照:Claude Code 想知道有没有类型错误,得让模型跑一次 tsc 再读输出;Cursor 里语言服务器早就把诊断算好了,成本是一次序列化。这是"厚 harness"路线最坚实的论据:环境已经在免费生产高价值信号,不采就是浪费。

诊断信息还有第二层价值:它是编辑后的即时验证信号。agent 改完一个文件,语言服务器几乎同步重新检查——新引入的类型错误可以立刻回灌给模型,形成一个延迟以百毫秒计的"编辑 → 反馈"微循环,不需要专门跑一次构建。在 harness 的语境里,这相当于把验证环节从"显式工具调用"降本成了"环境自带回执",agent loop 里"观察"这一步的质量因此天然更高。

Tab 与 Agent:两种延迟预算,一个目标

Cursor 最出圈的功能不是 agent,而是 Tab——灰色幽灵文本式的编辑预测。从 harness 视角看,Tab 和 Agent 的关系值得说清楚:它们不是两个产品,而是同一个目标(官方称之为 in-flow Next Action Prediction,"流内的下一步动作预测")在两个延迟预算下的部署形态。

事实层面(据 Cursor 官方博客《A New Tab Model》):

  • Tab 自 2024 年 3 月起由一个自研的稀疏语言模型驱动,在数十亿 token 上训练"预测编辑"这一专门任务;
  • 它每天产出超过十亿字符的编辑,请求量相比初代模型增长约 100 倍——按 Cursor 的说法,"世界上几乎没有哪个 LLM 生成的代码比 Tab 模型多";
  • 2025 年初随 0.45.0 客户端发布的 Fusion 模型,相比初代:困难编辑的逐行预测准确率提升超 25%,单次建议的连续改动长度提升 10 倍以上,p50 服务端延迟从 475ms 降到 260ms,上下文长度从 5500 扩到 13000 token;
  • Tab 不只补全文本:它预测编辑(改写、删除,而不只是插入)和跳转——接受一个建议后,它预测你下一个要改的位置,把光标送过去,支持跨文件。

Cursor 在另一篇官方博客《More problems》里描述过这套机制的终局形态:tab-tab-tab 序列。许多代码编辑是一串低熵动作——改了函数签名,接下来必然是逐个修调用点、更新类型定义、改测试——每一步都被上一步高度约束。当单步预测足够准,人就可以连续按 Tab 让模型"自动播放"整串改动(官方演示里连按 11 次 Tab 完成一连串修改)。这时 Tab 和 Agent 的边界其实模糊了:前者是"每步都要人按一下"的 agent,后者是"把人按的那一下也省掉"的 Tab。两者的差距不在智能,在信任校准——用户愿意为多大的一步改动放弃逐步确认。

这构成了一个清晰的双层结构:

TabAgent
延迟预算百毫秒级(打断心流即失败)秒到分钟级
任务粒度下一个编辑动作一个完整任务
模型自研小模型(稀疏、特化)前沿大模型
上下文光标邻域 + 编辑历史 + 诊断(13k token)全库检索 + 工具调用(十万级 token)
人在回路每次 Tab 都是一次微审批检查点 + diff 审查

模型分层是 harness 的常规武器

Tab/Agent 的分工是规划与任务分解里"模型分层"原则的极端版:把不同延迟、成本、能力要求的子任务分配给不同规格的模型。教训是反过来的——不要指望一个模型同时胜任"260ms 内预测下一个光标位置"和"自主重构一个模块",那是两种推理负载,harness 的责任是把它们拆开,各配引擎。

让模型改对代码:apply 本身是个工程问题

终端用户看不到、但 Cursor 投入最重的 harness 组件,是代码编辑的落地管线。官方博客《Editing Files at 1000 Tokens per Second》完整交代了这个问题,它是全站"为什么 harness 重要"的最佳个案之一。

问题陈述:让前沿模型直接输出大段修改后的代码,又慢又不靠谱——模型会"偷懒"(用 // ... rest of code 省略未改部分)、会做无关改动、动辄陷入多轮修复循环。Cursor 的解法是把代码编辑拆成规划(planning)与应用(applying)两个阶段:前沿大模型在对话里只负责想清楚改什么(输出一份"改动草图"),落地成完整新文件的工作交给一个专门训练的 fast apply 模型。这又是一个模型分层。

更有启发性的是他们对编辑格式的实验结论——为什么让模型重写整个文件,而不是输出 diff:

  1. 更多输出 token = 更多思考。 自回归模型每输出一个 token 就多一次前向计算;diff 格式强迫模型用更少的 token"想",反而损害正确率。
  2. Diff 在分布外。 预训练和后训练语料里,完整代码文件远多于 diff 格式——让模型输出 diff 是在让它做不擅长的事。
  3. 行号是模型的死穴。 标准 diff 要求输出行号,而模型 notoriously 不会数数;如果 tokenizer 把 "123" 当一个 token,模型必须在第一个输出 token 上就押对行号。

Cursor 也测试过 Aider 风格的 search/replace 块(消掉行号问题,只要搜索文本能在文件里唯一匹配),结论是全文件重写在 400 行以内的文件上综合表现更好——但注意"除 Claude Opus 外,大多数模型都输出不了准确的 diff"这句官方结论的反面含义:编辑格式的选择取决于你用什么模型,这不是格式之争,是模型能力分布的映射。

速度侧,他们基于 70B 模型微调,配合为代码编辑改造的推测解码变体 speculative edits——原文件内容充当"草稿",模型只验证和修改差异部分——达到约 1000 token/s(约 3500 字符/s),是原版 Llama-3-70b 推理的约 13 倍。一个 70B 模型跑出小模型的速度,代价是这套推理优化只能部署在自托管的自研模型上(闭源 API 模型做不了 speculative edits),这解释了为什么 Cursor 要自己训模型:不是追 benchmark,是某个 harness 环节的能力缺口没有现成模型可买。

这条管线对整个 工具系统设计的启示是普适的:"edit_file" 这个工具的表面是模型输出编辑指令,实质是格式选择、解析容错、失败重试、专用模型的整套工程。 各家的解法不同——Aider 押 search/replace 块,Claude Code 押"读旧文 + 精确替换 + 全量重写兜底",Cursor 押两段式管线——但都在回答同一个问题:模型的输出格式和环境的真实状态之间,需要一层鲁棒的适配。

与权限设计的关系

两段式管线还顺带改变了权限与人机协作的形状:改动以可视化 diff 呈现、逐块接受或拒绝,加上 agent 会话期间自动创建的检查点(checkpoint)快照可一键还原整个工作区——审批从"执行前问不问"部分转移到了"执行后留不留"。这与终端系 agent 前置式权限确认是不同的风险平衡点。

Cursor 给 harness 设计的启示

把 Cursor 拆开看,它对 harness 工程贡献了四个可迁移的判断:

  1. 宿主环境决定上下文策略。 IDE 提供免费结构化信号(诊断、光标、编辑历史),就建管道去采;终端只提供字节流,就把探索工具做锋利。上下文工程的第一步是盘点环境里已经存在的信号。
  2. 大代码库需要离线基础设施,不能只靠在环探索。 embedding 索引、增量同步(Merkle 树)、内容寻址缓存,是把"检索"从 agent 的运行时负担变成常驻基建的三件套;隐私成本要一并计入。
  3. 模型分层按延迟预算切。 百毫秒级预测、秒级编辑落地、分钟级任务规划,是三档不同的推理负载,值得三套(甚至自研的)模型。
  4. "改对代码"值得一个专门组件。 编辑格式不是细节而是正确率变量;当现成模型做不好某个 harness 环节时,训一个小模型补上,是头部厂商已经验证过的路线。

延伸阅读

参考资料