Cursor 实现深度分析 🖥️
"Cursor 是目前最成功的 AI 编程助手之一,其设计思路值得深入学习。"
1. Cursor 架构概述
1.1 核心组件
Editor / Terminal / File Explorer
↓
Context Engine ← 上下文收集
↓
Agent System ← AI 对话与工具调用
↓
Tool Executor ← 工具执行1.2 交互模式
| 模式 | 触发方式 | 用途 |
|---|---|---|
| Tab 补全 | 自动触发 | 代码自动完成 |
| Cmd+K | 快捷键 | 编辑选中代码 |
| Chat | 侧边栏 | 对话式问答 |
| Composer | Cmd+I | 多文件编辑 |
| Agent | @agent | 自主任务执行 |
2. System Prompt 分析
2.1 核心结构
markdown
You are a powerful agentic AI coding assistant, powered by Claude 3.5 Sonnet.
Your main goal is to follow the USER's instructions at each message.
<communication>
1. Be conversational but not too verbose.
2. Be concise, brief, and to the point.
3. Use markdown formatting when appropriate.
</communication>
<tool_calling>
1. ALWAYS follow the tool call schema exactly.
2. Only call tools when necessary.
3. Never refer to tool names in user-facing messages.
</tool_calling>
<making_code_changes>
1. Prefer editing existing files over creating new ones.
2. Never output extremely long code blocks.
3. Always apply changes using the edit tools.
4. If you've introduced linter errors, fix them.
</making_code_changes>2.2 关键设计原则
1. 隐藏工具细节
❌ "我将使用 edit_file 工具修改代码"
✅ "我会修改这个函数"2. 优先编辑而非创建
typescript
// 避免创建新文件,优先在现有文件中添加
if (fileExists(path)) {
await editFile(path, changes);
} else {
await createFile(path, content);
}3. 自动修复错误
typescript
async function applyChanges(changes) {
await executeChanges(changes);
const errors = await getLinterErrors();
if (errors.length > 0) {
await fixErrors(errors);
}
}3. 上下文收集
3.1 自动收集的信息
typescript
interface CursorContext {
// 编辑器状态
activeFile: string;
cursorPosition: { line: number; column: number };
selection: string;
// 文件信息
openFiles: string[];
recentFiles: string[];
// 代码库信息
projectStructure: string;
dependencies: Record<string, string>;
// 错误信息
linterErrors: LinterError[];
terminalOutput: string;
// 编辑历史
recentEdits: Edit[];
}3.2 上下文注入策略
typescript
function buildContextPrompt(context: CursorContext) {
const parts = [];
// 当前文件
if (context.activeFile) {
parts.push(`Current file: ${context.activeFile}`);
if (context.selection) {
parts.push(`Selected code:\n\`\`\`\n${context.selection}\n\`\`\``);
}
}
// Linter 错误
if (context.linterErrors.length > 0) {
parts.push(`Linter errors:\n${formatErrors(context.linterErrors)}`);
}
// 最近编辑
if (context.recentEdits.length > 0) {
parts.push(`Recent edits:\n${formatEdits(context.recentEdits)}`);
}
return parts.join('\n\n');
}4. 工具系统
4.1 核心工具
typescript
// 1. 读取文件
interface ReadFileTool {
name: 'read_file';
parameters: {
path: string;
};
}
// 2. 编辑文件(字符串匹配)
interface EditFileTool {
name: 'edit_file';
parameters: {
path: string;
old_str: string; // 要替换的内容
new_str: string; // 新内容
};
}
// 3. 创建文件
interface CreateFileTool {
name: 'create_file';
parameters: {
path: string;
content: string;
};
}
// 4. 执行命令
interface RunCommandTool {
name: 'run_command';
parameters: {
command: string;
};
}
// 5. 语义搜索
interface SearchCodebaseTool {
name: 'search_codebase';
parameters: {
query: string;
};
}4.2 编辑工具实现
typescript
async function editFile(path: string, oldStr: string, newStr: string) {
const content = await fs.readFile(path, 'utf-8');
// 精确匹配
if (!content.includes(oldStr)) {
throw new Error(`Cannot find exact match for:\n${oldStr}`);
}
// 替换
const newContent = content.replace(oldStr, newStr);
await fs.writeFile(path, newContent);
// 验证
const errors = await runLinter(path);
if (errors.length > 0) {
throw new Error(`Introduced linter errors:\n${formatErrors(errors)}`);
}
}5. 语义搜索
5.1 代码索引
typescript
class CodebaseIndex {
private embeddings: Map<string, number[]> = new Map();
async indexFile(path: string) {
const content = await fs.readFile(path, 'utf-8');
const chunks = this.chunkCode(content);
for (const chunk of chunks) {
const embedding = await getEmbedding(chunk.content);
this.embeddings.set(`${path}:${chunk.start}`, embedding);
}
}
private chunkCode(content: string) {
const ast = parseAST(content);
return ast.body.map(node => ({
content: content.slice(node.start, node.end),
start: node.start,
end: node.end
}));
}
async search(query: string, limit: number = 5) {
const queryEmbedding = await getEmbedding(query);
const results = [];
for (const [key, embedding] of this.embeddings) {
const similarity = cosineSimilarity(queryEmbedding, embedding);
results.push({ key, similarity });
}
return results
.sort((a, b) => b.similarity - a.similarity)
.slice(0, limit);
}
}5.2 使用示例
typescript
// 用户: "找到处理用户认证的代码"
const results = await codebaseIndex.search("user authentication");
// 返回:
// [
// { file: "src/auth/login.ts", line: 45, similarity: 0.89 },
// { file: "src/middleware/auth.ts", line: 12, similarity: 0.85 }
// ]6. Composer 模式
6.1 多文件编辑流程
typescript
class Composer {
async execute(task: string) {
// 1. 理解任务
const plan = await this.planTask(task);
// 2. 收集相关文件
const files = await this.gatherFiles(plan);
// 3. 生成编辑计划
const edits = await this.generateEdits(plan, files);
// 4. 预览变更
await this.showDiff(edits);
// 5. 用户确认后应用
if (await this.getUserConfirmation()) {
await this.applyEdits(edits);
}
}
private async planTask(task: string) {
const response = await llm.chat({
messages: [{
role: 'user',
content: `分析任务并生成执行计划:\n${task}\n\n返回 JSON 格式的计划。`
}]
});
return JSON.parse(response.content);
}
private async gatherFiles(plan: Plan) {
const files = new Set<string>();
// 从计划中提取文件
for (const step of plan.steps) {
if (step.file) files.add(step.file);
}
// 语义搜索相关文件
const searchResults = await codebaseIndex.search(plan.description);
searchResults.forEach(r => files.add(r.file));
return Array.from(files);
}
}7. Agent 模式
7.1 自主任务执行
typescript
class Agent {
async execute(goal: string) {
let step = 0;
const maxSteps = 20;
while (step < maxSteps) {
// 1. 决定下一步
const action = await this.decideNextAction(goal);
if (action.type === 'complete') {
return action.result;
}
// 2. 执行动作
const result = await this.executeAction(action);
// 3. 更新状态
this.updateState(result);
step++;
}
throw new Error('Max steps reached');
}
private async decideNextAction(goal: string) {
const response = await llm.chat({
messages: [
{ role: 'system', content: this.getSystemPrompt() },
{ role: 'user', content: `Goal: ${goal}\n\nCurrent state:\n${this.getState()}\n\nWhat's the next action?` }
],
tools: this.tools
});
return response.tool_calls?.[0] || { type: 'complete', result: response.content };
}
}7.2 自我修正
typescript
async function executeWithRetry(task: Task) {
let attempts = 0;
const maxAttempts = 3;
while (attempts < maxAttempts) {
try {
const result = await execute(task);
// 检查错误
const errors = await validate(result);
if (errors.length === 0) {
return result;
}
// 尝试修复
task = await generateFixTask(errors);
attempts++;
} catch (error) {
if (attempts === maxAttempts - 1) throw error;
attempts++;
}
}
}8. 流式交互
8.1 实时显示
typescript
async function streamResponse(prompt: string) {
const stream = await llm.chat({
messages: [{ role: 'user', content: prompt }],
stream: true
});
for await (const chunk of stream) {
// 显示文本
if (chunk.type === 'content_block_delta') {
ui.appendText(chunk.delta.text);
}
// 执行工具
if (chunk.type === 'tool_use') {
ui.showToolExecution(chunk.name);
const result = await executeTool(chunk);
ui.showToolResult(result);
}
}
}9. 复刻要点
如果你想构建类似 Cursor 的工具:
- 构建上下文引擎: 收集 IDE 状态、文件、终端等信息
- 设计精确的编辑工具: 使用字符串匹配而非行号
- 实现语义搜索: 索引代码库,支持自然语言查询
- 流式交互: 实时显示 AI 输出和工具执行
- 可观测性: 记录所有操作,支持撤销
- 迭代修正: 检测错误并自动修复