理解Cursor(AI IDE)的工作原理
2025年3月31日 · 274 字 · 2 分钟
本篇文章, 深入探讨了像 Cursor 这类 AI IDE 的工作原理, Cursor 的系统提示词, 以及如何优化我们编写代码和 Cursor 规则的方式.
原文: https://blog.sshh.io/p/how-cursor-ai-ide-works
Claude 翻译, 人工校对, 略有删改.
如有理解不准确或者错误的地方, 敬请指正.
理解像 Cursor、Windsurf 和 Copilot 这样的AI编码工具的内部工作原理, 可以极大地提高你的生产力,使这些工具在更大、更复杂的代码库中前后一致性更好地工作。
通常,当人们难以让 AI IDE 高效运行时,他们会像对待传统工具一样对待它们,忽视了了解其固有限制及如何最好地克服这些限制的重要性。一旦你掌握了它们的内部工作原理和约束条件,这将成为显著改善你工作流程的"神器"。在撰写本文时,Cursor 已经帮我编写了约70%的代码¹。
在这篇文章中,我想深入探讨这些 IDE 实际上是如何工作的,Cursor 的系统提示词,以及如何优化你编写代码和 Cursor 规则的方式。
从大语言模型到AI智能体
大语言模型
大语言模型的工作原理实际上是通过不断预测下一个词,从这个简单的概念中,我们能够构建复杂的应用程序。
图:从基础编码大语言模型到AI智能体的三个阶段。
蓝色是我们的前缀(即提示词),橙色是大语言模型自动完成的内容。对于AI智能体,我们运行大语言模型多次直到它产生面向用户的响应。每次,客户端代码(而不是大语言模型)计算工具结果并将它们提供回AI智能体。
为早期解码器大语言模型(如GPT-2)设计提示词,涉及精心制作一个前缀字符串,当完成时,会产生所需的结果。与其告诉 GPT “写一首关于鲸鱼的诗”,不如说
“主题:鲸鱼\n
诗歌:
"
或者甚至
“主题:树木\n
诗歌:…
"
实际的树木诗歌…\n主题:鲸鱼\n诗歌:"。
对于代码,这看起来像
“PR标题:重构Foo方法\n
描述:…\n
完整差异:",
你构建了一个前缀,当完成时会实现你想要的内容。“提示工程"就是创造性地构建理想的前缀,引导模型自动完成答案。
然后引入了指令调整(如ChatGPT),使大语言模型变得更加易于使用。
你现在可以说"写一个重构Foo的PR”,它会返回代码。在底层,它几乎完全是与上述相同的自动完成过程,但前缀已更改为
“写一个重构Foo的PR
",
大语言模型现在是在聊天中扮演角色。
即使在今天,你也会看到一些奇怪的情况,这个事实会泄露出来,大语言模型会通过继续自动完成”“标记之后的内容来开始向自己提问。
当模型变得足够大时,我们更进一步,添加了"工具调用”。不仅仅是补全文本,在前缀中我们可以提示"如果你需要读取文件,请说read_file(path: str)
而不是回应”。
现在,当给定编码任务时,大语言模型会完成
“read_file(‘index.py’)",然后我们(客户端)再次提示”…index.py的完整内容…”
并要求它继续完成文本。虽然它仍然只是一个自动完成过程,但大语言模型现在可以与世界和外部系统交互。
AI智能体编码
像Cursor这样的IDE是围绕这个简单概念的复杂套壳。
要构建一个AI IDE,你需要:
-
Fork 一个 VSCode
-
添加聊天UI并选择一个好的大语言模型(如Sonnet 3.7)
-
为编码AI智能体实现工具
- read_file(full_path: str)
- write_file(full_path: str, content: str)
- run_command(command: str)
-
优化内部提示词:“你是一个专家编码者”,“不要假设,使用工具"等。
高层次上来说,基本上就是这些。
困难的部分是设计你的提示词和工具,使它们能够保持一致性地工作。如果你确实完全按照我描述的方式构建它,它会有点作用,但它经常会遇到语法错误、幻觉,并且相当不一致。
优化AI智能体编码
制作一个好的AI IDE的诀窍是弄清楚大语言模型擅长什么,并仔细设计围绕其局限性的提示词和工具。通常这意味着通过使用更小的模型进行子任务, 来简化主大语言模型AI智能体完成的任务(参见另一篇文章《构建多智能体系统》)。
图表:当你使用AI IDE时在底层发生的情况。
我们为主AI智能体简化复杂度并将"认知负载"转移到其他辅助大语言模型。IDE将你的@标签注入上下文,调用工具来获取更多的上下文信息,使用特殊 diff 语法编辑文件,然后向用户返回响应摘要。]
优化和用户提示
通常用户已经知道正确的文件或上下文,所以我们在聊天UI中添加了”@file"语法,当调用大语言模型时,我们使用”“块传递所有附加文件的完整内容。这对用户来说就是语法糖,不过是自动替用户复制粘贴整个文件或文件夹。
Tip:在这些IDE中积极使用@folder/@file(更明确的上下文, 将会有更快和更准确的响应)。
搜索代码可能很复杂,特别是对于语义查询,比如"查找我们在哪段代码实现身份认证”。这里, 我们并非是让AI智能体去写它擅长的搜索正则表达式,而是使用编码器大语言模型将整个代码库索引到向量存储中,将文件及其功能嵌入到向量中。在查询时,另一个大语言模型基于相关性重新排序和过滤文件。这确保主AI智能体获得关于身份验证代码问题的"准确"结果。
Tip:代码注释和文档, 在AI IDE中指导代码如何嵌入模型,这使得在面向 AI IDE 时, 代码中的注释和文档, 比仅仅面向组织内部同事的情况下更加重要。建议在文件顶部详细注释, 文件是什么、它在语义上做什么、何时应该更新。
编写字符完美的代码是困难且昂贵的,因此优化 write_file(…)工具是许多这些IDE的核心。与其编写文件的完整内容,大语言模型通常会生成一个"语义差异",只提供更改的内容,并添加代码注释来指导在哪里插入更改。另一个更便宜、更快的代码 apply 大语言模型将这个语义差异作为提示词,编写实际的文件内容,同时修复任何小的语法问题。
然后,新文件通过一个 linter工具(译者注: 检查代码风格/错误的小工具,作用是提高代码质量、让你方便的发现一些拼写错误和语法错误,类似于 word 中的拼写检查)结果传递给主AI智能体,包含实际差异和lint结果,可用于自我纠正文件更改中的错误。我喜欢把这想象成与一个懒惰的高级工程师一起工作,他只写足够的代码片段,让实习生进行实际的更改。
Tip:你不能提示应用模型。“停止删除随机代码”,“停止添加或删除随机注释"等是徒劳的建议,因为这些因素来自应用模型的工作方式。相反,给主AI智能体更多控制权,“在edit_file指令中提供完整文件”。
Tip:当编辑极大的文件时,应用模型速度慢且容易出错,将你的文件分解为 <500 行代码。
Tip:lint反馈对AI智能体来说是极高信号,你(和Cursor团队)应该投资一个真正可靠的linter²,提供高质量建议。拥有编译和类型化语言提供的更丰富的lint时,反馈会更有帮助。
Tip:使用唯一的文件名(不是在你的代码库中有几个不同路径下的 page.js文件,更好的方式是否别命名为 foo-page.js、bar-page.js等),在文档中使用完整文件路径,并将代码热点组织到同一文件或文件夹中,以减少编辑工具的歧义。
使用一个擅长以这种AI智能体风格编写代码的模型(而不仅仅是一般性地编写代码)。这就是为什么Anthropic模型在像Cursor这样的IDE中如此出色的原因,它们不仅能写出好的代码,还擅长将编码任务分解成这些类型的工具调用。
Tip:使用的模型不仅要"擅长编码”,还要专门针对AI智能体IDE进行优化。据我所知,唯一测试这一点的排行榜是WebDev Arena³。
我在自己的AI IDE sparkstack.app 中使用的一个(非常昂贵的)技巧,使它在自我纠正方面变得更好,是给它一个"apply_and_check_tool"。这将会运行更昂贵的 linting,并启动无头浏览器来检索控制台日志和应用程序用户流的屏幕截图,为AI智能体提供反馈。在这样的情况下,MCP(Model Context Protocol)将真正发挥优势,给AI智能体更多自主权和上下文的方式。
Cursor 系统提示词逐行分析
使用基于MCP的提示词注入,我提取了Cursor AI智能体模式使用的最新(2025年3月)提示词。作为一个大量构建大语言模型的人,我非常尊重Cursor的"提示工程师",他们(在我看来)真的知道如何编写好的提示词,相比我在其他AI IDE中看到的。我认为这是他们成为领先编码工具的一个重要原因。深入研究这样的提示词也是提高你自己的提示词和AI智能体架构能力的好方法 - 在某种意义上,大多数GPT套壳都是"开源提示词",这很棒。
图片: Cursor AI智能体系统提示词的片段。点击查看完整提示词和工具定义。]
-
“","<tool_calling>“等 — 使用markdown和XML部分标签的混合提高了提示词对人类和大语言模型的可读性。⁴
-
“由Claude 3.5 Sonnet提供支持” — 很多时候大语言模型并不准确地告诉你它运行的是什么模型。明确地写出这一点减少了Cursor为与大语言模型自身所说不同的模型计费的投诉。⁵
-
“世界上最好的IDE” — 这是一种简洁的方式告诉大语言模型当事情出错时不要推荐替代产品,这对品牌AI智能体来说相当重要。⁶
-
“我们可能会自动附加一些信息…按照<user_query>标签中的USER指令操作。” — 与其直接将用户提示词传递给大语言模型,Cursor还将它们放入一个特殊标签中。这允许Cursor在消息中传递额外的用户相关文本,而不会混淆大语言模型或用户。
-
“避免道歉” — 显然是因为Sonnet的倾向而添加的。
-
“在说话时绝不要提及工具名称” — Cursor用粗体添加了这一点,讽刺的是我仍然经常看到"使用edit_tool"这样的表述。这是最近Sonnet模型的一个烦人问题。
-
“在调用每个工具之前,首先解释” — 在大语言模型流式传输工具调用时,聊天看起来会卡住几秒钟,这可能是一种奇怪的用户体验。这有助于用户确信正在发生一些事情。
-
“部分满足USER的查询,但你不确定,收集更多信息” — 大语言模型AI智能体有过早自信停止的倾向。给它们一个出口,让它们在回应前深入挖掘,这很有帮助。
-
“绝不向USER输出代码” — 默认情况下,大语言模型想要在内联markdown代码块中生成代码,所以需要额外引导,强制它只使用工具进行代码生成,然后通过UI间接向用户显示。
-
“如果你从头开始构建一个网络应用,给它一个美观现代的UI” — 在这里你可以看到一些演示黑客技术,以生产真正华丽的单提示应用。
-
“在编辑之前,你必须阅读你正在编辑的内容或部分” — 通常编码AI智能体真的想写代码但不收集上下文,所以你会看到很多明确的指令来引导它避开这一点。
-
“在修复linter错误时不要循环超过3次” — 旨在防止Cursor陷入编辑循环。这有帮助,但任何经常使用Cursor的人都知道这仍然很容易陷入困境。
-
“解决根本原因而不是症状。” — 作为大语言模型对齐不良的一个例子,它们通常会默认删除错误消息代码,而不是修复问题。
-
“不要硬编码API密钥” — 这是许多安全最佳实践之一,至少可以防止一些明显的安全问题。
-
工具"codebase_search”、“read_file”、“grep_search”、“file_search”、“web_search” — 鉴于大语言模型在编码前收集正确上下文是多么关键,它们提供了几种不同形式的搜索工具,给它提供所需的一切,以轻松弄清楚要做哪些更改。
-
在几个工具中,“一句话解释…为什么需要运行这个命令…” — 大多数工具都包含这个非功能参数,它强制大语言模型推理它将传递的参数。这是改进工具调用的常见技术。
-
工具"reapply”,“调用更智能的模型来应用最后的编辑” — 允许主AI智能体动态升级应用模型为更昂贵的模型,以自行解决愚蠢的 apply 问题。
-
工具"edit_file"声明"使用你正在编辑的语言的注释,表示所有未更改的代码" — 这就是所有那些随机注释的来源,这对于修改 apply 模型正常工作是必需的。
你还会注意到,整个系统提示词和工具描述都是静态的(即,没有用户或代码库个性化文本),这样Cursor可以充分利用提示词缓存以降低成本和减少第一个token的延迟。对于每次工具使用都会进行大语言模型调用的AI智能体来说,这一点至关重要。
如何有效使用Cursor规则(Cursor Rules)
现在的大问题是编写Cursor规则的"正确方式"是什么,虽然我的总体回答是"对你有用的就是好的",但基于提示经验和对Cursor 内部的了解,我确实有很多看法。
图片: 这就是你的Cursor 项目规则对大语言模型的展示方式。它看到一个名称和描述列表,基于此它可以进行工具调用fetch_rules(…)并读取其内容。
关键是要理解这些规则不是附加到系统提示词上,而是作为指令的命名集引用的。你的思维方式应该是将规则写成百科全书文章而不是命令。
-
不要在规则中提供身份,如"你是一个擅长typescript的高级前端工程师",就像你可能在cursor.directory中找到的那样。这可能看起来有效,但对于已经由内置提示词提供身份的AI智能体来说很奇怪。
-
**不要(或避免)**试图覆盖系统提示词指令或尝试提示应用模型,使用"不要添加注释",“在编码前向我提问”,和"不要删除我没有要求你删除的代码"。这些直接与内部工具使用冲突并使AI智能体感到困惑。
-
**不要(或避免)**告诉它不要做什么。大语言模型最擅长遵循积极的命令"对于<这个>,<做这个>",而不仅仅是一系列限制。你在Cursor自己的提示词中看到这一点。
-
请花时间编写高度显著的规则名称和描述。关键是AI智能体在对你的代码库知之甚少的情况下,能够直观地知道何时使用其fetch_rules(…)工具来应用规则。就像你正在构建文档的手工反向索引一样,你有时应该有具有不同名称和描述的重复规则,以提高获取率。尽量保持描述密集而不过于冗长。
-
请像百科全书页面一样为你的模块或常见代码更改编写规则。像维基百科一样,将关键术语(使用mdc链接语法)链接到代码文件,在确定更改所需的正确上下文时,为AI智能体提供巨大的提升。这有时也意味着避免逐步指令(专注于"什么"而不是"如何"),除非绝对必要,以避免AI智能体对特定类型的更改过度拟合。
-
请使用Cursor本身来起草你的规则。大语言模型擅长为其他大语言模型编写内容。如果你不确定如何格式化你的文档或编码上下文,请"@folder/ 生成一个markdown文件,描述常见期望更改的关键文件路径和定义"。
-
请将拥有大量规则视为一种反模式。这很反直觉,虽然规则对于使AI IDE在大型代码库中工作至关重要,但也反过来表明代码库对 AI 不友好。我在《AI驱动的软件工程》中写了更多关于这一点的内容,但未来的理想代码库足够直观,以至于编码AI智能体每次只需要内置工具就能完美工作。
查看我生成的一些示例。
结论
一个基于 VSCode 的分支,建立在实际上开源的AI智能体提示词和公开可访问的模型API上,能够达到接近100亿美元的估值 - 拿到了 6倍⁸ 点 “套壳系数”,这是多么疯狂。且看看Cursor是否最终会开发自己的智能体模型(感觉不太可能)或者Anthropic是否会以Claude Code + 下一代Sonnet的形式作为竞争对手插足,这将很有趣。
无论最终的情况如何,知道如何塑造你的代码库、文档和规则将继续是一项有用的技能,我希望这次深入探讨让你对事情如何运作以及如何为AI优化有了一个不那么"基于感觉"而更加具体的理解。
我经常说,如果Cursor对你不起作用,那是你使用方式不对。
¹ 这是一个基于感觉的统计数据,但我认为它并不偏离太远。一旦你掌握了Cursor规则,相当一部分PR实际上只成为一次性提示词。我最初认为要到2027年才能达到这个程度,但随着Anthropic、Cursor和我自己的提示词技巧同时改善,事情比我猜想的改善得更快。
² 到目前为止,我对CodeRabbit的linting印象深刻,并计划使用MCP将其反馈到Cursor中。如果Cursor的默认linter更好,而一切保持不变,感觉就像使用Sonnet 3.8。
³ (大多数)大语言模型的美妙之处在于,虽然这是一个Web开发基准,但根据我的经验,性能与所有类型的编码和框架高度相关。
⁴ 我找不到关于这方面的科学研究,但根据我的经验,这非常有效,我不会惊讶如果Anthropic模型明确在伪XML语法上进行训练。
⁵ 这确实有一些意想不到的副作用,编码模型会更改你代码库中引用的模型名称,使其与自身相同。
⁶ 这里有一个有趣的法律灰色地带。对于Cursor来说,在他们的网站上放置这个实际上是非法的(参见FTC法案,Lanham法案),然而让他们将其放在提示词中并让大语言模型代表他们说出来(目前)是可以的。
⁷ 顺便说一下Cursor团队,我发现了一个拼写错误 (:
⁸ 这是我发明的一个术语,指的是GPT “套壳”与模型提供商之间估值的比率。在这个例子中,Anthropic : Cursor = 600亿美元 : 100亿美元 = 6。我的直觉告诉我,“6"不是一个合理的比率。戴上我那不成熟的投资者帽子,我会推测Anthropic应该更接近1000亿美元,而Cursor最高可达10亿美元(套壳的系数为100)。我很难看出他们中的任何一个真正拥有长期护城河,而且Anthropic构建自己的下一代AI IDE竞争对手似乎很容易。
作者:极目楚天舒
链接:https://talkaboutos.com/posts/how-cursor-ai-ide-works/
声明:除非另有声明,本文采用 CC BY-NC-SA 3.0 协议,转载请注明。
赞助:若你觉得本文对你有启发,非常欢迎你成为我的 Sponsor ,感恩遇见