Cursor編輯代碼功能是如何實現的?
大家好,我卡頌。
類似Cursor、Cline、Trae這樣的AI IDE已經成為程序員日常開發的標配。
對待AI IDE,一種常見的誤區是認為他們的核心流程很復雜。
實際上,考慮到執行效率,通常Agent的設計并不復雜,更多工作量主要來自:
- 如何保證流程穩定可控(
LLM輸出的不穩定造成)與性能優化 - 如何節約上下文空間
......
Cursor的「編輯代碼」功能,就蘊含了「節約上下文空間」的巧思。
Cursor核心原理
Cursor的核心部分其實是個簡單的Agent:

用戶的需求給到Cursor后,他會思考「要完成任務需要使用哪些內部工具」?
使用具體工具后,結合「工具調用結果」繼續思考下一步應該使用什么工具。
直到最終任務結束。
其內部通過Tool_Use(對于openAI旗下模型,就是Function_Call)字段定義了如下10個工具:
- codebase_search:語義搜索工具
- read_file:讀取文件內容(或大綱)
- run_terminal_cmd:代表用戶在終端執行命令
- list_dir:快速列出目錄內容
- grep_search:基于正則表達式的快速文本搜索
- edit_file:編輯文件內容
- file_search:基于模糊匹配的快速文件路徑搜索
- delete_file:刪除指定路徑的文件
- reapply:在編輯結果不符合預期時,調用更智能的模型重新應用最后一次文件編輯
- diff_history:查看近期文件修改歷史
一個例子
假設我們的需求是「用antd的Button組件替換頁面中的主按鈕」
一種可能的執行過程是:
執行run_terminal_cmd,檢查antd是否已安裝,若未安裝需后續處理,參數如下:
{
"command": "npm list antd || echo 'antd not installed'",
"require_user_approval": false,
"is_background": false,
"explanation": "檢查antd是否已安裝,若未安裝需后續處理。"
}執行grep_search,查找包含主按鈕的代碼位置,確定需要替換的文件,參數如下:
{
"query": "主按鈕|main-button|primary",
"include_pattern": "*.jsx|*.tsx|*.js",
"case_sensitive": false,
"explanation": "查找包含主按鈕的代碼位置,確定需要替換的文件。"
}根據上一步grep_search返回的目標文件,執行read_file,確認按鈕的上下文代碼(如樣式、事件處理等),參數如下:
{
"target_file": "src/components/ExamplePage.jsx",
"start_line_one_indexed": 1,
"end_line_one_indexed_inclusive": 50,
"should_read_entire_file": false,
"explanation": "讀取包含主按鈕的文件內容,分析按鈕的屬性和上下文。"
}執行edit_file,將原生按鈕或舊組件替換為antd Button,并保留原有邏輯(如onClick),參數如下:
{
"target_file": "src/components/ExamplePage.jsx",
"instructions": "將原生按鈕替換為antd的Button組件,保留原有樣式和事件。",
"code_edit": "http:// ... existing code ...\nimport { Button } from 'antd';\n// ... existing code ...\n<Button type=\"primary\">主按鈕</Button>\n// ... existing code ..."
}由于LLM生成的隨機性,完整的工具調用并不一定按上述步驟。
比如在“搜索主按鈕的位置”時,也可能先使用list_dir工具,列出文件目錄,再從文件名判斷「哪個文件可能與“主按鈕”相關」。
為了讓LLM記得「之前的執行步驟」,以及「接下來要做什么」,這些必要的信息都會存在于模型上下文中:

由于模型上下文有限,Cursor會在多個層面做「上下文長度優化」,比如:
- 默認情況下,
Agent只會執行20輪步驟 read_file一次最多只會讀取200行代碼,不夠的話再繼續讀200行
本文要講的edit_file工具就是「上下文長度優化」的表率。
edit_file的實現原理
如果說read_file已經夠占用上下文了,那么未經優化的情況下,edit_file占用的上下文應該在read_file的兩倍左右。
畢竟,要想修改文件,你得同時知道:
- 原始文件是什么樣
- 要修改成什么樣
所以,有別于其他工具的實現原理就是「單次命令執行」(比如list_dir對應ls),edit_file是一個獨立的AI workflow。
他包含至少3個步驟,涉及至少2次模型調用:
- 讀文件
- 生成編輯方案(使用先進的模型)
- 執行編輯方案(使用小參數模型)
- (可選)如果編輯方案不理想,用先進模型再執行一次(使用
reapply工具)
舉個例子,假設用戶需求是「在 src/utils/math.ts 文件中添加一個計算斐波那契數列的函數」
第一步,獲取文件路徑,讀取內容。
假設內容如下:
// 數學工具函數集合
/**
* 計算兩個數的和
*/
export function add(a: number, b: number): number {
return a + b;
}
/**
* 計算兩個數的差
*/
export function subtract(a: number, b: number): number {
return a - b;
}第二步,首先準備編輯方案:
{
"content": "上面讀取到的原始代碼",
"query": "添加一個計算斐波那契數列的函數",
"path": "src/utils/math.ts",
"is_new": false
}將上述方案發送給智能的模型(比如Claude 3.5及以上)。
模型返回如下內容:
// 數學工具函數集合
// ... existing code ...
/**
* 計算斐波那契數列的第n個數
*/
export function fibonacci(n: number): number {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}注意,其中原始內容中「未修改的部分」被注釋 // ... existing code ... 代替了。
這種方式的好處是:可以顯著減少上下文空間占用。
但也有缺點:沒法通過上述信息直接還原編輯后的代碼,得通過模型的能力還原。
所以,第三步,將上述信息一齊給到模型,將注釋替換為原始代碼:
{
"content": "讀取到的原始代碼",
"query": "添加一個計算斐波那契數列的函數",
"path": "src/utils/math.ts",
"is_new": false,
"code_edit": "上述代碼編輯信息"
}模型返回:
// 數學工具函數集合
/**
* 計算兩個數的和
*/
export function add(a: number, b: number): number {
return a + b;
}
/**
* 計算兩個數的差
*/
export function subtract(a: number, b: number): number {
return a - b;
}
/**
* 計算斐波那契數列的第n個數
*/
export function fibonacci(n: number): number {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}由于這一步邏輯不復雜,且對模型生成速度要求較高,所以通常交給微調過的小參數模型執行。
如果模型的執行效果不好(比如在一個大文件中一次性修改多處),還能使用reapply工具使用更智能的模型再執行一遍。
總結
Cursor的核心邏輯是一個簡單的Agent,包含10個可調用的內部工具。
其中,大部分工具的實現原理是「簡單的命令執行」。
而edit_file工具是一條涉及3個步驟的AI Workflow,中間涉及到注釋的替換。
之所以這么做,是為了節約模型上下文空間。































