当前位置: 首页 > news >正文

AI 编码工具的底层架构:Cursor 是怎么给你补全代码的

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

你每天在用的 AI 编码工具(Cursor、Copilot、Continue),它们的底层是怎么工作的?

你按下 Tab 键接受一行补全建议,背后经历了什么?Cursor 是怎么知道你要写什么代码的?为什么有时候补全精准到离谱,有时候又一坨废话?

答案藏在三个核心环节里:Context 收集、Prompt 组装、模型选择


AI 编码的三种模式

在拆架构之前,先分清 AI 编码工具的三种工作模式——它们的底层架构完全不同。

模式交互方式延迟要求模型选择典型场景
Tab 补全光标处出现灰色建议,按 Tab 接受< 300ms小/快模型补全当前行、函数体
Chat 对话侧边栏对话,问答式2~5s中等模型解释代码、写函数、调试
Agent 模式自主多步操作10~60s最强模型跨文件重构、复杂任务

三种模式对速度和能力的权衡完全不同——Tab 补全要快到用户感知不到延迟,Agent 模式可以慢但要准。


Tab 补全:300ms 内的极限优化

Tab 补全是 AI 编码工具的核心体验——用户每敲一个字符都可能触发,必须快到"感觉不到在等"。

▎FIM:Fill in the Middle

传统 LLM 从左到右生成文本。但代码补全不一样——光标可能在文件中间,上面有代码、下面也有代码。如果模型只能看到上面的内容,补全就不准。

FIM(Fill in the Middle)解决了这个问题——把光标前的代码(Prefix)和光标后的代码(Suffix)都给模型看,让它"填空"。

typescript

// 你的代码,光标在 ▎ 处
function calculateTotal(items: CartItem[]) {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);

return total;
}

FIM 模型的输入格式:

<PRE>function calculateTotal(items: CartItem[]) {
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
<SUF>
return total;
}<MID>

模型输出:

const tax = subtotal * 0.08;
const total = subtotal + tax;

同时看到上文和下文,模型知道要返回total,所以补全了计算total的逻辑——这是纯左到右生成做不到的。

▎Context 收集:知道得越多补得越准

Tab 补全的 token 预算非常有限(通常 2000~4000 token),必须精准选择上下文。优先级从高到低:

P0:光标前后代码(Prefix / Suffix)

这是必选的——没有这个就无法补全。通常取光标前 1000~2000 字符和光标后 500~1000 字符。

P1:当前文件的 import 链

你引用了import { UserService } from './user.service',那user.service.ts的类型定义就是高价值上下文——模型知道UserService有什么方法后,才能正确补全调用代码。

typescript

// 分析 import 获取相关文件
function getImportedFiles(sourceFile: string): string[] {
const ast = parseAST(sourceFile);
const imports = ast.body.filter(node => node.type === "ImportDeclaration");
return imports.map(imp => resolveModulePath(imp.source.value));
}

P1:LSP 诊断信息

Language Server Protocol 提供类型信息、错误诊断、符号定义——这些是模型理解代码语义的关键线索。

typescript

// LSP 提供的类型信息
const typeInfo = await languageServer.getHoverInfo(cursorPosition);
// { type: "CartItem[]", documentation: "购物车商品列表" }

P2:最近编辑的文件

用户刚改过的文件大概率跟当前编辑相关。

P3:项目规则(.cursorrules)

项目级的编码规范、技术栈约束——比如"使用 Tailwind CSS"、"组件用函数式写法"。

▎Prompt 组装

收集完上下文后,组装成模型能理解的 Prompt:

typescript

function buildCompletionPrompt(context: CompletionContext): string {
const parts: string[] = [];

// 项目规则
if (context.rules) {
parts.push(`Project rules:\n${context.rules}\n`);
}

// 相关文件类型定义
for (const file of context.relatedFiles) {
parts.push(`// File: ${file.path}\n${file.content}\n`);
}

// FIM 格式
parts.push(`<PRE>${context.prefix}<SUF>${context.suffix}<MID>`);

return parts.join("\n");
}

▎模型选择:速度优先

Tab 补全对延迟极度敏感——超过 500ms 用户就会感知到"卡"。所以用的是专门优化过的小模型

工具Tab 补全模型特点
Cursor自研 cursor-small针对代码补全优化,极快
CopilotCodex / GPT-4o-mini代码专用模型
ContinueStarcoder2 / DeepSeek开源模型,可本地部署

这些模型参数量小(1B~7B),推理快,专门在代码数据上训练。跟 Chat 用的 GPT-4o(几百B参数)完全不是一个量级。

▎补全的触发与取消

不是每个按键都触发补全——有一套防抖 + 预测机制:

typescript

let debounceTimer: NodeJS.Timeout;
let abortController: AbortController | null = null;

function onTextChange(event: TextChangeEvent) {
// 取消上一次请求
abortController?.abort();

clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
// 快速判断是否值得请求
if (!shouldTrigger(event)) return;

abortController = new AbortController();

try {
const completion = await requestCompletion({
context: collectContext(),
signal: abortController.signal,
});

if (completion) {
showGhostText(completion);
}
} catch (e) {
if (e.name !== "AbortError") throw e;
}
}, 150); // 150ms 防抖
}

function shouldTrigger(event: TextChangeEvent): boolean {
// 删除操作不触发
if (event.isDelete) return false;
// 在注释中不触发(可选)
if (isInsideComment(event.position)) return false;
// 在字符串中可能不触发
if (isInsideString(event.position)) return false;
return true;
}


Chat 模式:对话式代码生成

Chat 模式的架构跟 Tab 补全差别很大——它本质是一个RAG + 代码生成的组合。

▎上下文更丰富

Chat 模式的 token 预算大得多(8k~32k),可以传更多上下文:

typescript

function buildChatContext(query: string, editor: Editor): ChatContext {
return {
// 用户选中的代码(如果有)
selection: editor.getSelection()?.getText(),
// 当前文件完整内容
activeFile: editor.getActiveDocument().getText(),
// 语义搜索相关代码片段
codeSnippets: await semanticCodeSearch(query, projectRoot),
// 终端输出(如果在调试)
terminalOutput: getRecentTerminalOutput(),
// 错误信息
diagnostics: getDiagnostics(editor.getActiveDocument()),
// 项目文件结构
fileTree: getProjectStructure(projectRoot, { depth: 3 }),
};
}

▎Prompt 结构

System: 你是一个专业的前端开发助手。基于以下上下文回答用户问题。

## 当前文件
{activeFile}

## 选中的代码
{selection}

## 相关代码片段
{codeSnippets}

## 错误信息
{diagnostics}

User: {userQuery}

▎用大模型处理

Chat 没有 Tab 补全那么严格的延迟要求——用户问了问题,愿意等几秒。所以用 GPT-4o / Claude Sonnet 等大模型,效果更好。


Agent 模式:自主编码的完整循环

Agent 模式是最复杂的——它不只是回答问题,而是自主执行多步操作:读文件、理解需求、写代码、运行测试、修复错误。

本质就是前面 18 篇学的 ReAct 循环,只不过工具集换成了 IDE 操作:

typescript

const agentTools = [
// 文件操作
{ name: "read_file", description: "读取文件内容" },
{ name: "write_file", description: "写入/修改文件" },
{ name: "list_directory", description: "列出目录结构" },

// 代码搜索
{ name: "grep_search", description: "全局搜索代码" },
{ name: "semantic_search", description: "语义搜索代码" },

// 执行
{ name: "run_command", description: "在终端运行命令" },

// IDE
{ name: "get_diagnostics", description: "获取 LSP 诊断信息" },
{ name: "go_to_definition", description: "跳转到定义" },
];

一个典型的 Agent 执行轨迹:

用户:帮我给 UserService 加上参数校验

Agent: [思考] 先看看 UserService 的代码
→ read_file("src/services/user.service.ts")

Agent: [思考] 看到了 createUser 方法,需要加 zod 校验
→ read_file("package.json") // 检查是否有 zod

Agent: [思考] 没有 zod,先安装
→ run_command("npm install zod")

Agent: [思考] 现在写校验代码
→ write_file("src/services/user.service.ts", updatedCode)

Agent: [思考] 检查有没有类型错误
→ get_diagnostics("src/services/user.service.ts")

Agent: [思考] 有一个类型错误,修复
→ write_file("src/services/user.service.ts", fixedCode)

Agent: [思考] 再检查一次
→ get_diagnostics("src/services/user.service.ts")

Agent: 完成!给 createUser 和 updateUser 方法添加了 zod 参数校验...


开源替代:Continue 的架构

Cursor 是闭源的,想深入理解底层可以看Continue(开源 AI 编码助手,Star 40k+)。

Continue 的架构清晰地分为五层:

1. Context Provider(上下文提供器)
- 收集代码、文件、终端、Git 等信息
- 可自定义扩展

2. Prompt Builder(Prompt 构建器)
- 把上下文组装成模型可用的 Prompt
- 不同模式用不同模板

3. LLM Provider(模型提供器)
- 支持 OpenAI / Anthropic / Ollama / 本地模型
- 统一接口,一键切换

4. Response Handler(响应处理器)
- 解析模型输出
- 代码 diff 应用到编辑器

5. Editor Integration(编辑器集成)
- VS Code / JetBrains 插件
- 处理 ghost text、inline diff 等 UI

Continue 的config.json让你能完全控制架构的每一层:

json

{
"models": [
{
"title": "GPT-4o",
"provider": "openai",
"model": "gpt-4o",
"apiKey": "sk-xxx"
}
],
"tabAutocompleteModel": {
"title": "Starcoder2 3B",
"provider": "ollama",
"model": "starcoder2:3b"
},
"contextProviders": [
{ "name": "code", "params": {} },
{ "name": "docs", "params": {} },
{ "name": "terminal", "params": {} }
]
}

Tab 补全用本地 3B 小模型(零延迟零成本),Chat 用远程大模型——混合架构,是开源方案的最佳实践。


关键技术细节

▎Speculative Decoding(推测解码)

Tab 补全要快,有一个常用优化——小模型先快速生成候选,大模型验证

1. 小模型(1B)快速生成 5 个 token 候选
2. 大模型一次性验证这 5 个 token 对不对
3. 如果前 3 个对、后 2 个不对 → 接受前 3 个,从第 4 个重新生成

验证比生成快得多,所以整体速度接近小模型,质量接近大模型。

▎Caching:多级缓存

补全请求非常频繁——用户每敲一个字符都可能触发。多级缓存避免重复请求:

typescript

class CompletionCache {
private prefixCache = new Map<string, string>();

async getCompletion(prefix: string, suffix: string): Promise<string | null> {
const key = hashKey(prefix, suffix);

// L1:精确匹配
if (this.prefixCache.has(key)) {
return this.prefixCache.get(key)!;
}

// L2:前缀匹配(用户刚接受了一部分补全,剩下的还能用)
for (const [cachedKey, cachedCompletion] of this.prefixCache) {
if (prefix.startsWith(getCachedPrefix(cachedKey))) {
const remaining = trimAcceptedPart(cachedCompletion, prefix);
if (remaining) return remaining;
}
}

// L3:调模型
const completion = await requestFromModel(prefix, suffix);
this.prefixCache.set(key, completion);
return completion;
}
}

▎Ghost Text 渲染

补全建议以灰色"幽灵文本"显示——这需要 VS Code 的InlineCompletionProviderAPI:

typescript

import * as vscode from "vscode";

class AICompletionProvider implements vscode.InlineCompletionItemProvider {
async provideInlineCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
context: vscode.InlineCompletionContext
): Promise<vscode.InlineCompletionItem[]> {
const prefix = document.getText(
new vscode.Range(new vscode.Position(0, 0), position)
);
const suffix = document.getText(
new vscode.Range(position, document.lineAt(document.lineCount - 1).range.end)
);

const completion = await this.getCompletion(prefix, suffix);
if (!completion) return [];

return [
new vscode.InlineCompletionItem(
completion,
new vscode.Range(position, position)
),
];
}
}

vscode.languages.registerInlineCompletionItemProvider(
{ pattern: "**" },
new AICompletionProvider()
);


避坑指南

▎坑 1:Context 太少导致补全不准

模型只能基于你给它的上下文来补全。如果只传了当前行的前几个字符,补全结果就像盲猜。

解法:确保 import 链文件、类型定义、项目规则都在上下文里。Cursor 的.cursorrules@引用就是解决这个问题的。

▎坑 2:Context 太多导致补全变慢

塞太多上下文,token 数飙升,延迟也跟着涨。

解法:设 token 预算上限,按优先级裁剪。Tab 补全控制在 2000~4000 token,Chat 控制在 8000~16000。

▎坑 3:不同模式用了同一个模型

Tab 补全用 GPT-4o?每个按键都要等 2 秒,体验灾难。

解法:Tab 补全用专门的快模型(cursor-small / Starcoder2 3B / DeepSeek Coder),Chat 和 Agent 用大模型。

▎坑 4:忽略了 FIM 的重要性

很多开发者自建补全服务时只传 prefix(光标前的代码),忽略 suffix(光标后的代码)——补全效果打五折。

解法:一定用 FIM 格式,prefix + suffix 一起传。主流代码模型(StarCoder、DeepSeek Coder、CodeLlama)都支持 FIM。


总结

AI 编码工具的底层架构,核心是三个环节的极致优化

Context 收集——知道得越多补得越准

  • ▸P0:光标前后代码(Prefix / Suffix)
  • ▸P1:import 链文件 + LSP 类型信息
  • ▸P2:最近编辑的文件
  • ▸P3:项目规则(.cursorrules)
  • ▸在 token 预算内按优先级裁剪

Prompt 组装——把代码变成模型能理解的格式

  • ▸Tab 补全用 FIM 格式(Prefix + Suffix + Middle)
  • ▸Chat 用系统提示 + 上下文 + 用户问题
  • ▸Agent 用工具定义 + 上下文 + 任务描述

模型选择——速度和能力的权衡

  • ▸Tab 补全:小快模型(< 300ms)
  • ▸Chat:中大模型(2~5s)
  • ▸Agent:最强模型(10~60s)

想深入了解可以去读Continue 的源码——它把这三个环节做成了可插拔的模块,代码结构清晰,是学习 AI 编码工具架构的最佳教材。

下一篇我们聊Prompt Engineering 进阶——System Prompt 的设计模式、Few-Shot 策略、以及如何让 LLM 的输出稳定可控。

推荐资源:

  • Continue 开源项目:https://github.com/continuedev/continue
  • Cursor 官方博客:https://cursor.com/blog
  • FIM 论文:https://arxiv.org/abs/2207.14255
  • VS Code InlineCompletion API:https://code.visualstudio.com/api/references/vscode-api#InlineCompletionItemProvider

往期推荐

Multi-Agent Teams:让多个专家 Agent 像团队一样协作

AI Agent 是怎么"想一步做一步"的?拆解 ReAct 模式

从零开始:用 LangChain.js 构建你的第一个 Tool-Calling Agent

最后

点个在看支持我吧

http://www.jsqmd.com/news/534707/

相关文章:

  • 用Python和Matplotlib搞定RML2016.10a数据集:手把手教你画IQ信号的三种图(附完整代码)
  • 主管护师教辅怎么选?看这篇避坑指南 - 医考机构品牌测评专家
  • 手把手教你用GDB和Objdump搞定南大ICS缓冲区溢出实验(Phase1-Phase5保姆级攻略)
  • Bespoke Curator实战指南:3大主流LLM集成与性能优化全攻略
  • LeetCode 3.无重复字符的最长子串|Python题解(滑动窗口最优版)
  • 从ELK迁移到阿里云SLS,我们团队一年省了XX万运维成本(实战复盘)
  • Misago:构建现代化社区论坛的全方位解决方案
  • YOLO X Layout开源镜像免配置部署:Gradio+ONNXRuntime开箱即用
  • 安装Claude Code 以及配置 Coding Plan 教程
  • Proteus仿真PCA9685踩坑实录:I2C波形正常但PWM无输出?手把手教你排查
  • 储能双向DCDC变换器的模型预测控制及仿真分析
  • 2026年电木板加工厂家推荐排行榜:绝缘电木板、耐高温电木板、治具及零配件定制切割加工专业实力解析 - 品牌企业推荐师(官方)
  • AI Agent 面试必问:设计一个写周报的 Agent,你会怎么答?
  • 利用快马平台快速构建copaw本地部署原型:十分钟搭建验证环境
  • 深度解析:oh-my-opencode智能代理架构设计与实现原理
  • ComfyUI-AnimateDiff-Evolved深度解析:掌握运动模块与上下文选项
  • 2026年玻纤板加工厂家推荐排行榜:定制/成品/绝缘件/治具/零切加工,耐高温绝缘玻纤板专业制造实力解析 - 品牌企业推荐师(官方)
  • nomic-embed-text-v2-moe部署案例:政务知识库多语种政策文件语义检索系统
  • ComfyUI工作流架构深度解析:从节点编排到企业级部署的完整技术栈
  • LeetCode 438.找到字符串中所有字母异位词|Python题解(滑动窗口最优版)
  • 单容水箱液位随动系统的模糊控制研究——基于‘化工与自动化仪表‘期刊论文复现
  • 2026年3月北京酒回收公司最新推荐:老酒回收、名酒回收、茅台酒回收、洋酒回收、红酒回收、五粮液酒回收公司选择指南 - 海棠依旧大
  • GitHub Actions:Python项目的CI/CD实践
  • 【20年架构师亲测】MCP插件安装成功率提升92%的7个关键操作(含SHA256校验与离线安装包获取路径)
  • 信奥赛网课水太深!家长选机构前,先看懂这4个坑
  • 离线音频转录全攻略:Buzz本地语音处理工具的高效应用指南
  • 老旧Mac图形性能重生计划:从卡顿到流畅的完整解决方案
  • 留言板
  • 嵌入式调试效率翻倍!玩转平头哥CDK的Watch窗口与串口打印(附实战技巧)
  • Solidity Patterns访问控制模式详解:构建安全的智能合约权限系统