
AI coding 是模型推理能力增加之後的下一個競爭高地,除了模型廠商、AI Labs 之外,這個領域的參與者也有著 Cursor 這樣的初創團隊。作為一個 LLM-first IDE,Cursor 在今年迅速出圈,一方面在於底層模型 Claude Sonnet 3.5 模型 coding 能力提升帶來的體驗升級,另一方面也在於團隊在 AI Coding UI/UX 上的持續投入。
本篇內容是 Lex 對 Cursor 團隊訪談的詳細整理,團隊創始成員 Aman Sanger (CEO)、Arvid Lunnemark(CTO)、Sualeh Asif(COO)和 Michael Truell(設計主管)詳細分享了 Cursor 產品體驗、infra、模型訓練、資料安全等細節以及他們對於 AI coding、AI Agent 的思考,透過這些分享也能瞭解 Cursor UI/UX 背後的理念。
• o1 不會幹掉 Cursor,AI Coding 領域才剛剛開始;
• 圍繞程式碼預測、補齊等各類任務 Cursor 還訓練了一系列專門的小模型;
•Cursor 正在試驗一個叫做 Shadow Space 的產品概念,透過在後臺執行一個隱藏視窗讓 AI 在不影響到開發者的操作的情況下進行 coding 任務;
•團隊在 code base indexing 上投入了大量精力,這個 indexing 系統會成為接下來其他程式碼任務可以展開的基礎;
•未來程式設計會是自然語言和程式碼將共存,根據具體任務選擇最有效的互動;
•AI 正在重塑程式設計體驗,提高效率的同時保持程式設計師的創造力和控制力;
•Cursor 認為 Claude 3.5 Sonnet 的綜合實力更強,Sonnet 最強的地方在於能夠很好地理解開發者表述並不清晰的目標,預測程式設計師接下來的操作、給出適當建議;
•即便是 SOTA 模型也不擅長找 bug,這會是 Cursor 的機會;
•當前的程式碼任務基準測試並不能準確反映模型的真實能力,因為現實中的程式碼任務更加複雜多樣,並且充滿了模糊性和上下文依賴。Cursor 團隊更傾向於透過真實使用者的使用反饋來評估模型的效能;
•目前還沒有人能很好地解決 models routing 問題,底座模型和自有模型之間可以初步實現模型切換,但如果是 GPT-4o、Claude sonnet 和 o1 之間的切換可能要更加複雜。

💡 目錄 💡
01 Cursor 的誕生
02 Cursor 的產品
03 Cursor 是如何提高產品響應速度的
04 Priompt :以網頁為靈感的提示詞工程
05 Shadow Workspace:
Coding Agent 的工作流設計
06 SOTA 模型也不擅長找 bug
07 Code Indexing 和 Context
08 Scaling law 和復現 o1 模型
01.
Cursor 的誕生
Github Copilot 是第一個 LLM-driven 的消費級應用
Lex:程式碼編輯器這個領域整體是什麼樣的?
Michael:程式碼編輯器(code editor)主要用來開發軟體。對外行來說,可以把程式碼編輯器想象成是一個專門給程式設計師設計的高階版的文字處理器。之所以說它是高階版,是因為程式碼有不同結構,而程式碼編輯器能做的事情比普通的文字處理器要多得多。
比如,程式碼編輯器能讓我們直觀地區分程式碼中不同的標記、快速瀏覽程式碼、在程式碼庫裡快速瀏覽內容,就像在網上點選超連結一樣,快速跳轉到需要的定義;甚至還能檢查錯誤,幫助發現一些簡單的 bug。程式碼編輯器主要的功能一般就是這些。但我認為,在接下來的 10 年裡,由於軟體開發方式發生變化,程式碼編輯器的概念也會發生很大的改變。
Lex:Cursor 是新一代程式碼編輯器,flok 了VS Code 的一個分支。你們自己使用程式碼編輯器的經歷是什麼樣的?
Aman:一開始我們都是用 Vim 做程式碼編輯。當時還沒有 Neovim,只有 Vim 和一個終端。2021 年 Copilot 釋出的時候,我很想體驗一下 Copilot,但由於 Copilot 只能在 VS Code 上使用,所以我就開始用 VS Code 了。Copilot 和 VS Code 這個組合的使用體驗特別好,所以即便我很喜歡 Vim,我還是轉向了 VS Code。在我們開發 Cursor 之前,VS Code 都是我的預設編輯器。
Arvid:我覺得 GitHub Copilot 有一個被低估的方面是,即使它的程式碼補齊出現了錯誤,但最多隻是讓人覺得有點麻煩,我們再多打一個字元,它可能就懂我們的需求了,所以也不會太差。
Sualeh:其實就是透過“犯錯-糾正”的過程來迭代。Copilot 還有一個被低估的地方,它是第一個真正的 AI 產品,也是第一個 LLM-driven 的消費級產品。
Lex: Cursor 是怎麼誕生的?
Michael:大概 2020 年的時候,OpenAI 提出了 Scaling Law,從那時候開始 LLM 領域的進步就變得清晰可預測,只要算力和資料夠多,模型表現就能更好,也是在那時候,開始有很多人討論“這項技術的進步能給我們帶來什麼”,“它要怎麼去改善各種不同的知識工作領域”等等。
有幾個時刻讓我感受到 scaling law 的預測變得越來越具體。第一個是 Copilot beta 版本推出,第二時刻是 GPT-4 釋出的時候。
GPT-4 的提升讓我們看到 scaling laws 對於“算力-模型能力”的預測已經可以落到具體的地方了,有可能很快就能開發出來更多有價值的東西。而且,如果我們繼續往下做,不僅能點對點地解決問題,甚至整個程式設計領域都可以用到這些模型,只不過需要我們搭載一個不一樣的程式設計環境,提供一種不同的程式設計方式。這就是為什麼我們開始圍繞這個更大的 vision 去進行開發。
Sualeh:2022 年 6 月左右,Shengtong 和 Aman 打過一個賭,預測到 2024 年 6 月或 7 月,人們能不能靠模型能力拿到 IMO 金牌。雖然很確定模型能取得進步,但我當時的想法是模型贏得奧數金牌完全不可能,但 Aman 認為可以。
Aman:不過我還要補充一點,能取得多大進步,也取決於我們在哪個領域。數學就是一個很好的領域,尤其是形式化定理證明(formal theorem proving),因為我們能透過訊號來驗證對不對,所以 RL 就能很好地起作用。不過,即便有的模型可以在數學能力上超越人類,但是我們還是沒有達到 AGI。
Cursor 是什麼?
Lex:VS Code 可以說在某種程度上統一了開發者社群,既然已經可以在 VS Code 裡安裝很多 Copilot 這樣的 AI extension,你們為什麼還要重新做一個 VS Code?
Michael:開發一個編輯器的想法其實是自然而然形成的。因為在開發 Cursor 的時候,我們想的是,模型肯定還會變得更好,能力還會提高,不但能顯著提高生產力,也會從根本上改變現在我們開發軟體的方式。如果只是在現有的編輯器里加一個外掛,對編輯器的控制提升是很有限,我們不像被這些限制束縛,希望能夠構建最有用的東西。
Lex:VS Code 和 Copilot 的組合可以說是 Cursor 最直接的競爭對手,Cursor 要如何在競爭中獲勝?
Aman:如果我們回看之前的幾次科技浪潮,會發現它們的普遍特點是,有某一件大事發生,然後圍繞這個大事件一波新的公司開始出現。但現在不同的是,每年只要有一個模型的能力取得了飛躍,都會出來一波新的功能,有一波新的可能性。尤其是在程式碼領域,甚至都不需要一年,只要領先幾個月,就可以讓我們的產品更有用。一年後我們再回過頭來看今天的 Cursor,會發現它也已經過時了。Microsoft 確實做了很多非常出色的事情,但是從程式設計的角度上來說,我覺得他們不像別的初創公司一樣在一直創新。
Sualeh:我其實不會從功能的角度來考慮,我會從程式設計師的能力角度來看這個問題。o1 模型剛出來的時候,我就覺得肯定還會有很多不同的模型,比如更長的上下文、或者算力更快的模型。所有這些聽起來很瘋狂的想法都值得嘗試,最後大概能有 1/10 的想法可以真正做成比較有用的東西,我們希望它們推出的速度能更快。
還有一個被低估的事實是,我們其實是在給自己製作這個工具。我們之所以有 Cursor 這個想法也是因為我們能明確感受到模型能力的提升,但 Copilot 給我們帶來的體驗並沒有變得更好,也很疑惑為什麼。當然,Copilot 賣得很好,對於 Microsoft 來說也是一個很成功的業務,但相當長時間 Copilot 都沒有新東西出來,我個人來說會很想試試新的東西。
Arvid:我們是一整個團隊在開發 Cursor,不管是開發使用者體驗,還是探索模型的互動方式,我們都在同步去開發怎麼才能讓模型給出更好的答案,比如怎麼構建 prompt,怎麼找到上下文,怎麼用 Cursor Tab 訓模型等等。
02.
Cursor 的產品
Cursor Tab
Lex:Cursor 的互動中, Tab 鍵是一個很獨特的存在,它可以看作是增強版的自動補全,幾乎什麼都會。可以展開講講 Tab 鍵的邏輯的嗎?
Sualeh:我們很想要實現的一件事就是讓模型幫我們做程式碼編輯。為此我們嘗試了很多種辦法,當我們有了能力很強的模型之後,為了讓使用體驗更加流暢,我們花了很大功夫提高了推理速度,最佳化使用者體驗。
Tab 鍵存在的目標是,如果使用者接受了某一處編輯建議,模型應該很自然地就知道下一個可能需要編輯的位置在哪裡。比如說在我改動了一個地方之後,模型就應該知道下一處要改動的地方需要跳轉到 18 行下面,而不是像 Vim 那樣需要使用者自己來按 18JJ 這樣的快捷鍵。
所以我們解決這個需求的方式就是 Tab 鍵。使用者只需要按下 Tab 鍵,模型就可以自動跳轉到第 18 行可能需要編輯的位置,顯示下一條編輯建議,再按一下 Tab 鍵,又會顯示下一條,可以這麼一直按下去。
所以 Tab 背後的核心思想是:如何讓編輯過程的熵為 0。一旦使用者表達了他們的想法,模型應該能夠推斷出接下來的編輯內容,也就是說沒有新的資訊需要使用者輸入。但現在使用者還是需要輸入一些字元,才能讓計算機理解他們的想法。理想情況下,模型應該能夠“讀懂”使用者的想法,所有不需要新資訊的編輯部分都應該透過按 Tab 鍵來跳過。
Aman:如果我們觀察不同型別語言模型的 loss,會發現在程式碼領域,位元每位元組(bits per byte)的 loss 比語言領域要低。也就是說,在程式碼領域,很多 token 和字元完全是可以預測的。所以除了自動補全以外,在預測使用者編輯程式碼後的下一步操作時,這種可預測的現象會更明顯。
Cursor Tab 就是要消除編輯器中所有低熵的操作。一旦確定了使用者的操作步驟,就應該直接跳過這些低熵的操作,讓使用者直接跳到下一處編輯點。
Lex:Cursor 是怎麼做預測的?背後的技術細節是什麼樣的?
Aman:首先,Cursor 的延遲很低。我們訓練了專門的小模型來解決這類任務。這些模型很依賴 pre-fill tokens,也就是說,這些模型面對的是非常長的 prompt,需要處理很多程式碼行,但是實際生成的 token 並不多。這種情況下使用稀疏模型(Sparse Model)就很合適,這是一種 MoE 模型。這是我們做的一個突破,這個突破顯著提高了模型處理長上下文時的效能。
另一個關鍵點是我們基於推測解碼(Speculative Decoding)構建了推測編輯(Speculative Edits)。
這兩個因素在我看來是 Cursor 生成質量高、速度快的關鍵。
快取在這裡的作用很大,由於輸入的 token 非常多,如果每在一個給定行輸入,都要在 token 上重新執行模型,那麼延遲會很高,GPU 也會因為過載而崩掉,所以就需要設計模型的 prompt,這樣它們就可以進行快取。我們也會跨請求重用 KV Cache,這樣就減少了工作量和需要的算力。
Lex:除了程式碼生產、程式碼補齊、跨行編輯,以及在同一個檔案內的不同位置之間跳轉外,Cursor 的 Tab 鍵還能做哪些事情?
Sualeh:我們接下來想透過 Tab 鍵來實現在不同檔案之間的跳轉。有可能使用者先編輯了某個檔案,但是想要在另一個檔案中繼續編輯,這樣的話 Tab 也應該要能跳轉到另一個檔案。
Arvid:這個過程可以概括成下一步動作預測(next action prediction)。比如有時候在終端執行命令,需要 Cursor 根據已經寫好的程式碼來推薦命令,它雖然能給出一些建議,但如果沒有更多的資訊,使用者很難判斷到底對不對。所以,Tab 就可以引導使用者先跳轉到定義,瞭解了定義知識以後再跳轉回來,這樣使用者就知道了這個概念,願意接受下一處補全。
Michael:Coding 是一個很獨特的領域。很多時候,在程式設計中其實是可以根據使用者前面的工作來預測接下來 5 分鐘他們想要做什麼的。
所以我們可能可以發展到這樣一個狀態:在人類開發者已經做了一些工作之後,接下來要麼讓 Cursor 幫忙接管接下來 5 分鐘的任務,對於人類員工來說,只需要稍微看一下 Cursor 的步驟,發現它完成得很不錯,再在螢幕上點選 Cursor 做出的改動就可以了。
Code diff
Lex:Cursor diff 會透過紅綠對比來告訴我們怎麼修改程式碼,在聊天窗裡也可以檢視並且接受 diff。diff 的設計邏輯是什麼樣的?
Sualeh:我們總共有四到五種不同的 diff 檢視,比如我們就為自動補全優化了專門的 diff 檢視,這個 diff 介面和審查大塊程式碼時的 diff 介面是不一樣的。針對處理多個不同檔案的情況,我們也在最佳化響應的 diff 檢視。
從 high-level 角度,自動補全會要求更快的閱讀速度,程式設計師的視線需要集中在一個特定區域,不能關注到太多地方,避免注意力分散,所以我們做這個任務的 diff 的時候會優先考慮這個問題。
現在自動補齊的 diff 介面中,除了程式碼寫作框外,如果我們想要刪掉某個位置的程式碼,新增另一個程式碼,它會在旁邊獨立顯示一個框。

我們圍繞程式碼補齊做過三、四種不同的 diff 介面。最初是類似於 Google Doc 的刪除線的形態,還嘗試過紅色高亮塊等等形態,最終選定了這個方案。下一版迭代會更有趣。首先會有一個藍色高亮來突出 AI 給出建議的位置,但具體的建議內容不會直接顯示出來,使用者可以透過按住 Option 鍵的方式來檢視 AI 的建議到底是什麼,如果松開 Option 就又會回到原始程式碼介面。
Arvid:我個人很期待接下來可以圍繞 diff 做出很多改進和最佳化。diff 涉及的場景通常是程式碼審查裡面的驗證難題,diffs 很適合小範圍的程式碼改動,但如果程式碼改動的範圍比較大,或者涉及到多個檔案,要來回審查這些 diff 其實就有點難了。
關於這一點,我們其實也有幾種解決思路:
其中一個想法是,在比較大規模的程式碼改動中,有些 diff 的資訊量很大,很重要,而有些 diff 只是一樣的資訊來回重複,也就是說它們的熵很低。所以我們就把重要的那些 diff 高亮,不重要的標成灰色。
另一種思路是,我們可以有一個模型來對這些 diff 進行審查對比,如果找到某個地方有 bug 的話,就用紅色波浪線劃出來,讓使用者重新審查這裡的 diff。
這些想法都讓我覺得在產品體驗上我們還有很多事情值得做。
Lex:這些產品思路最終想達到的目標是讓人類工程師在程式碼編輯和審查中只關注他們最需要閱讀的內容,從而達到最佳效果?
Arvid:我們也考慮過設計一個模型來做這件事,現在 diff 環節的演算法只是普通的演算法,沒有特別智慧。雖然我們在設計這套演算法的時候將人類的智慧融入了進去,但在實際執行中,演算法並不會關心處理的具體任務是什麼,而這正是我們希望模型能做到的事情。
Sualeh:問題就在於,這些模型會越來越聰明,如果模型越來越聰明,它們能提出的程式碼改動的內容和範圍也會越來越大。如果改動越來越大,程式設計師就要做越來越多的程式碼審查工作,而 Cursor 就可以幫他們做這個,這樣他們就不需要花大把的時間來做程式碼審查。
Lex:涉及到多個檔案的程式碼審查時,diff 又是怎麼設計和實現的?
Aman:就像 Arvid 剛才說的,我們可以把人從程式碼審查中解放出來。現在,我們要花很長時間去理解陌生的程式碼,而且它找出的 bug 往往非常多。但是使用 LLM 就可以很大程度上提高審查的體驗,比方說,Arvid 剛剛說的那些想法就可以引導使用者關注真正重要的地方。
程式碼審查的使用者體驗設計通常要考慮到 reviewer 和寫程式碼的人兩類角色,但如果寫程式碼的是 LLM 的話,我們就可以不用考慮程式碼編寫的具體體驗了,從而可以更多去圍繞 reviewer 來設計整個產品體驗,從而讓 reviewer 可以輕鬆高效地完成任務。
我覺得只是關注程式碼審查環節本身的話並不能真正解決問題,我們在產品設計中可以更有創意,去探索可能性的邊界。
Arvid:我覺得需要指出來的一點是,順序也很關鍵(ordering matters)。一般來說,在審查時我們會先看到一串檔案列表,然後習慣性地從上往下看。但實際上,有些內容因為邏輯上是基礎的,應該最先理解這部分內容,之後才是其他部分資訊。這種梳理順序的工作就可以交給模型,讓模型來引導我們去找到資訊邏輯順序,再來完成審查。
Lex:未來程式碼的創作過程是不是會越來越多地使用自然語言?變得像寫書一樣。
Arvid:有時候會用到自然語言,但不是所有的程式設計任務都會用到自然語言。比如,如果我和 Sualeh 一起程式設計,Sualeh 可能坐在電腦前敲鍵盤,而我在開車,那我會告訴 Sualeh 要怎麼去做。但也有些有時候,要給 Sualeh 解釋清楚要做什麼很麻煩,那我就會直接接過鍵盤,給他演示一下要怎麼做,我只需要寫一部分程式碼,他就懂了,這種溝通方式很簡單直接。對 AI 來說也是這樣的。有時候最簡單的溝通方式就是給 AI 一個例子,這樣它就會繼續往下做完這個任務。
或者,如果我們要設計網站,最簡單的辦法並不是告訴 AI 我想要的是什麼,而是直接拖動、繪製頁面,給它展示應該怎麼做。可能到最後我們也會透過腦機介面讓 AI 理解我們的想法,到那時自然語言一定會有一席之地,但是我覺得大多數時候,自然語言不會是大多數人寫程式碼的方式。
底層模型
Lex: Cursor 背後的模型是什麼?
Aman:除了呼叫最新一代模型能力之外,Cursor 自己也訓練了一些自定義模型(custom models),這些模型會配合那些在複雜推理任務上表現更好的頭部模型,共同完成任務。
Cursor Tab 就是一個很好的例子,在 Cursor Tab 的需求下,我們訓練了一個針對這個特定任務的模型,最新一代特定模型在特定任務上的表現甚至超過前沿模型,這一點從評估結果就能看出來。
另一個需要特定任務模型的是 Apply,在 Apply 任務中,自定義模型不僅很有必要,而且效果也很好。雖然頭部模型很擅長做程式碼規劃、生成大致的修改框架,但是它們不能很好地顯示 diff。
無論是 Sonnet 還是 o1,這些模型都會在一些簡單的細節上出錯,比如數程式碼行,尤其是在處理超大檔案時,這類問題會更普遍。為了解決這個問題,我們就讓模型先勾勒出一個大致的程式碼塊,用來表示需要修改的內容,然後再訓一個模型把更改 Apply 到檔案中。

我們也還在探索怎麼把 o1 模型的能力嵌入到產品體驗裡,我覺得如何目前就 o1 的最佳實踐還沒有定論,但比較明確的方向是,它可以讓我們更容易地在後臺處理一些任務、agent 的屬性更強。
Aman:o1 也有一些明顯的限制,先拋開能力本身,它不支援流式輸出這一點就很影響體驗。o1 目前的狀態更像是一個 V0 版本,還有很多需要改進的方面。
Lex:有訊息說 GitHub Copilot 可能會以某種方式整合 o1 模型的能力,也因此會有人說“Cursor 完了”,你們怎麼看這件事?
Michael:我認為這個領域與過去 2010 年左右的軟體領域有些不同,因為這裡的上限真的非常高。我認為再等 3-4 年,到那個時候最好的 AI 程式設計產品可能比現在的要實用得多。就當下說只要能打造出更好的產品,就有機會超越那些擁有大量使用者的競爭者。因此,我認為接下來的幾年關鍵在於打造最好的產品和系統,不僅包括模型引擎的改進,還包括最佳化程式碼編輯體驗。
Aman:Cursor 相比其他產品對於使用者的附加值不僅僅在於能快速整合 o1 這樣的新模型,它的價值還來自於我們針對各種型別任務開發的專有模型,這些模型可能在使用者不知情的情況下默默發揮作用,並且每個功能都是圍繞使用者體驗精心設計的。
Lex:在你們的實踐經驗中,GPT 和 Claude 對比,誰的程式碼能力更強?不過由於 Cursor 各個環節涉及到的模型不同,答案可能不是絕對的?
Aman:沒有哪個模型能在所有方面的表現都比其他模型更好,包括速度、程式碼編輯能力、處理大量程式碼的能力、上下文長度和程式碼能力等等。不過,就目前來看,我覺得整體上表現最好的模型是 Sonnet,這也是大家的共識。
o1 確實很有意思,它很擅長推理,如果給它一些非常難的程式設計面試題問題或者 LeetCode 題目,它可以處理得很好,但是它不像 Sonnet 那樣能很好地理解到使用者的模糊需求。
至於其他的頭部模型,我並不是想說它們是針對基準測試訓練出來的,但確實存在一個顧慮是,和中腰部水平模型相比,它們在基準測試中表現很出色,但一旦涉及到超出這些範圍的任務,這些模型的表現就會下降。
但當我們的任務範圍超出基準測試時,Sonnet 是最能保持其原有能力的模型,它在基準測試中展現的能力和在實際程式碼任務中的表現是保持一致的。
Lex:前面提到的 Apply 這個功能可以檢視程式碼,提出很好的建議,告訴我們接下來怎麼做。對我們來說,把這兩個模型結合起來好像也不難,為什麼你們覺得並不簡單?
Sualeh:和大家的猜測相反,Apply 的實現並沒有採用確定性演算法(deterministic algorithm)。
Aman:人們在其他程式碼產品那裡看到的 Apply 其實都是對 Cursor Apply 的簡單模仿,可能很多人覺得 Apply 是可以透過確定性匹配的方式來實現,但這種方式下有 40% 的機率都會出錯,產品體驗也會很差。
我們在設計 Apply 的時候還能最智慧的模型在生成程式碼時,使用較少的 token,從而降低延遲和成本。我們可以給出一個非常粗略的程式碼,讓模型去跑,因為跑通這個框架的程式碼對模型來說很簡單。
這樣的形式還可以繼續下去。可以用越來越智慧的模型來做規劃,然後讓不那麼智慧的模型來跑。比如我們可以用 o1 或者更智慧的模型來做更 high-level 的規劃,再先後讓 Sauna 和 Apply 來處理。圍繞“如何讓 Cursor 反應更快”也有很多細節在裡面。
03.
Cursor 是如何提高產品響應速度的
Lex:你們是怎麼提高 Cursor 響應速度的?
Aman:其中一個重要組成就是推測編輯(speculative edits),這是從推測解碼(speculative decoding)中衍生出來的。
可以先解釋一下推測解碼是什麼。大部分情況下,尤其是在 LLM 生成受記憶體限制的情況下,並行處理 tokens 比逐個生成 tokens 更快。如果檢視每秒生成的 tokens 數量,會發現處理 prompt tokens 的速度比逐個生成 tokens 要快得多。推測解碼可以透過並行處理提高模型速度。
傳統的推測解碼是用一個很小的模型來預測 draft tokens,然後用更大的模型來做驗證。但因為預測的 code 實際就是已有的 code 本身,而我們已經很熟悉這些 code,所以可以直接把原始的 code 片段輸入到模型中,讓模型去驗證程式碼相不相符。絕大多數情況下,模型會認為這些 code 片段是正確的,並且直接原樣輸出。
這樣,我們可以並行處理足夠多的程式碼行,直到模型預測的文字與原始 code 出現不一致的地方。這時,模型會生成新的 token,我們則根據 code 的匹配程度判斷什麼時候重新開始檢測程式碼塊。
最終呈現出來的效果就像是加速版的程式碼編輯,看起來就像是模型在以更快的速度重寫所有 code。所以我們就可以用和 diff 一樣的介面,只不過資料傳輸的效率更高。
Sualeh:這樣設計的好處是,我們可以在資料傳輸的時候就開始做程式碼審查而不是等到全部載入完才開始整個過程。這個設計很有意思,但其實 speculation 這個路徑現在已經很常見了,不僅僅是 LLM,CPU 和資料庫實踐中都有用到 speculation。
Lex:具體到 chat、diff 這種功能上,是怎麼讓模型響應這麼快的?
Aman:我們用了很多策略,其中一個比較有意思的就是快取預熱(cache warming)。當用戶在輸入時,我們能預判他們可能用到的上下文資訊。我們之前提到過,複用 KV catch 可以降低延遲和成本,還能跨請求共享。只要使用者開始輸入內容,我們就可以立即使用當前的檔案內容預熱快取,這樣使用者按下回車後,需要預處理的 token 就少了很多,可以更快開始生成響應,大大降低了首次響應時間(TTFT)。
Lex:還有哪些更 high-level 的快取機制可以幫助提升響應速度?
Aman:在 Cursor Tab 的設計裡,系統可以預測使用者可能會接受哪個建議,提前處理可能會發生的下一個請求操作。這種方法結合了快取和預測。透過預測使用者的選擇,系統可以提前快取下一個建議,這樣一來,當用戶按下 Tab 鍵的時候,系統就會立即給使用者呈現下一個建議。這是一種啟發式的策略,雖然模型本身沒有變化,但透過更 high level 的快取機制,會讓使用者感覺響應速度很快。
Sualeh:如果我們能讓 KV Cache 變得更小,就可以做更多預測。比如,系統可以預測並快取接下來 10 個可能的建議,使用者在這 10 個選項中選中一個的機率會比單個預測要高得多。使用者可能輸入新字元後會選擇快取中的其他選項。這個策略在 RL 上也很有用,單個預測樣本可能不夠準確,但如果有多個預測選項,其中至少有一個正確的機率就會大大提升。
模型內部會對於哪個建議是正確的、使用者想要的是什麼建議,都存在一定的不確定性。我們在用 RL 訓 Cursor Tab 的模型時的其中一個事情就是預測模型生成的 100 個建議中,哪一個對使用者來說更合適,更符合他們的偏好。
模型可能在某些方面能夠做出很準確的預測,而在其他方面預測得就不是太準確,基於使用者反饋,我們可以對模型進行獎勵或懲罰,如果使用者喜歡這個建議,就給它獎勵,如果模型喜歡而使用者不喜歡,就給它懲罰。透過這種方式,我們可以訓練模型輸出更多使用者會喜歡的建議。
Aman:雖然這樣不能直接提高響應速度,但是二者之間其實是有聯絡的。因為即使是小模型,只要能用 RL 訓模型,就有可能達到與大模型相當的效能水平。除了前面的 KV catch 的角度,還有一些技術能幫忙提高速度。
比如,兩年前比較主流的是多頭注意力機制(multi-head attention),現在已經逐漸轉向使用更加高效的注意力機制,比如 group query 或者 multi-query attention,這些機制能在更大的 batch size 下更快地生成 token。
但這些方法並不會影響首個 token 的預填充速度,它們主要是提升的是後續 token 的生成速度。這是因為使用者生成 token 時面臨的瓶頸不再是在所有 token 上執行高度可並行的矩陣乘法,而是在處理 long context 和更大規模的 batch size 時,如何快速讀取那些快取的 keys 和 values。這就又涉及到記憶體頻寬的問題,我們可以透過壓縮 keys 和 values 來提升速度。group query 或者 multi-query attention 的目的都是減少 KV Cache 的大小。
DeepSeek 開發的 MLA(Multi-Latent Attention,多隱向量注意力機制)也是一種,但MLA 的思路更新:用一個大的共享向量來儲存所有keys 和 values,同時為每個 token 配備較小的向量,這樣系統就只需儲存較小的向量,它的原理是把所有注意力頭的鍵值都轉換成一個隱向量,這個向量隨後會在時間維度上擴充套件。
Lex:這些在處理記憶體限制上的細節最終是如何影響使用者體驗的?
Aman:主要有兩方面的影響。首先,我們可以大幅增加快取容量,因為 KV Cache 佔用空間變小了。這也意味著我們可以快取更多內容,提高快取命中率(cache hits),從而減少首個 token 的生成時間。其次,即使使用者的請求量增加、批次變大,模型推理時的速度也不會明顯下降,因為模型能以匹配的速度生成 token。
Sualeh:這種方法還能支援更長的 prompt,從而給到模型的提示也更明確。
Aman:KV Cache 是“所有 prompts 的總大小”和“並行處理的 prompt 數量”的乘積,所以這兩個維度中任何一個的增加也都不會降低 token 生成的延遲。
04.
Priompt :以網頁設計為靈感的提示詞工程
Lex:我們剛剛提到,基準測試中的 prompt 往往結構清晰、表述規範。Arvid 也寫過一篇 Prompt Design 的文章,在你看來一個好的 prompt 能起到什麼作用?
💡
Arvid 在 Prompt Design 一文中參考現代網頁設計實踐提出了“提示詞設計”的概念,認為二者的共性在於都要做到清晰有效的溝通、對動態內容作出響應、涉及到不同“螢幕尺寸”的適應等。
Arvid:每個模型都不太一樣,對不同的 prompt 的反應也不一樣,比如之前的一系列模型都對 prompt 的要求非常嚴格,上下文視窗也很小。
在程式碼開發場景中,我們會遇到大量和 prompt 相關的資訊,包括文件、新增的檔案和對話歷史等。這就帶來一個問題:在 context window 有限的情況下,該如何篩選和組織這些資訊?拿現在的模型來說,即使上下文視窗更長了,但要是塞滿整個視窗,模型的響應速度就會變慢,有些時候還會被 prompt 混淆,而且有的模型比其他模型更容易受到混淆。
為了解決這個問題,我們開發了一個叫 Priompt 的內部系統,它在一定程度上幫我們解決了這個問題。

Priompt 借鑑了現代網頁開發的最佳實踐。和固定版式的雜誌排版不同,網站開發中會涉及到的一個情況是,不同裝置的中資訊的展示多少、格式等是動態變化的,而使用者到底在哪裡檢視網站開發者事前並不知道,但無論終端怎麼變,都要保證網站資訊在不同裝置上正常顯示。AI 提示詞工程也是類似,我們要做到無論 input 內容多大、怎麼變,output 的格式都能正確展示。
我們比較喜歡用 React 庫的宣告式程式設計方法(declarative approach),也就是說,我們可以透過 JSX 直接描述期望的 UI 呈現方式,例如透過設定樣式屬性或CSS類名來宣告元素的展示層級(如z-index)或其他視覺特性。

就像瀏覽器有自己的渲染引擎一樣,Cursor 也有一個 Priompt 渲染器,它的作用是把內容合理地排布在頁面上,我們只需要告訴它你想要什麼,它就會幫你實現。
我們發現這種方法非常有用,而且它的作用也在不斷演變,一開始設計它是為了應對有限的上下文視窗,現在它幫我們把資料處理和實際展示分成了兩個部分,這樣做的好處是 debug 就會變得更容易:在不實際修改程式碼的情況下,我們可以先透過輸入 prompt、Priompt 幫助預渲染的方式來看某個程式碼改動是否真的有效。
Arvid:Priompt 和 React 類似,它還有一些元件,比如檔案元件會獲取當前游標的位置,因為理論上使用者正在檢視的那行程式碼往往是最重要的。我們會給不同的程式碼行設定優先順序,游標所在行的優先順序最高,每遠離一行優先順序就降低一級。最終在渲染的時候,系統會計算一共能顯示多少行程式碼,並以游標所在行為整個程式碼的中心。
Aman:Priompt 和還有一些更復雜的功能,如果整個程式碼庫中有大量程式碼塊,我們還可以使用 retrieval 、embedding 和 re-ranking scores 等方式讓元件自動設定優先順序。
Lex:為了讓模型更好理解使用者提問,是不是也可以用到類似方法?
Arvid:我們的目標是,“讓使用者做自己的事情就好”,我們的工作或者說整個 Cursor 要做的就是如何去 retrieve 相關資訊。
Lex:我和 Perplexity 的 CEO Aravind Srinivas 也聊過這個問題,他認為要讓使用者想怎麼問就怎麼問。對於程式設計師來說,我們是不是還可以提出更高的要求?在完全放任使用者隨意提問和引導使用者給出更多 prompt 之外是不是也存在一種情況,即系統在鼓勵甚至要求使用者把想法表達得更清楚,這裡說的不是語法或者句子要多規範,而是 prompt 中體現出的思考深度。
Aman:我認為即便模型能力已經接近完美,當用戶向模型提出需求的時候,往往還是沒法完全表達自己的想法。要解決這個問題有幾個方法:
•第一,讓模型直接告訴使用者,基於使用者的問題,它還是不太確定這部分內容,讓使用者再明確一下;
• 第二,如果模型可以一次生成 5-6 種可能的結果,基於這些結果也可以告訴使用者,由於使用者的問題有一些模糊的地方,模型會把所有可能的結果都展示出來,讓使用者來選擇最合適的。
Lex:讓模型主動提問有多難?我是不是需要透過提問來獲得更多資訊,從而減少一些可能發生歧義的情況?
Sualeh:最近我們在 Cursor 裡新增了一個檔案推薦功能。使用者在編輯問題的時候,模型就能猜到問題是什麼,比如,使用者在編寫 API 程式碼的時候,模型會根據這個檔案之前的提交記錄,推測出客戶端和伺服器端的相關程式碼可能也很重要。這背後存在一個技術上的難題:怎麼在歷史記錄中找到之前的資訊,並根據使用者當前給的 prompt 判斷出哪些檔案最重要?目前我們的版本還比較簡單,這個功能還在試驗階段,相信未來會越來越精確。有個這個功能,使用者就可以很方便地新增檔案,讓模型來編輯。
比如說,當用戶在編寫 API 時,可能還需要修改使用這個 API 的客戶端程式碼和處理 API 請求的伺服器端程式碼。如果 Cursor 能在使用者輸入 prompt 並按下回車鍵之前,就幫助理解這些模糊的地方,就能提供很大幫助。
05.
Shadow Workspace:Coding Agent 的工作流設計
Lex:你們怎麼看 Agent?
Arvid:雖然目前 agent 在很多場景下還不是特別實用,不過我覺得它們離真正派上用場已經不遠了。有些任務非常適合使用 agent,比如我們遇到了一個 產品 bug,這個 bug 可能是“聊天框裡的內容不能複製貼上”,我只需要用兩句話來說明這個任務:“這裡出問題了,請修復一下”,agent 就會立刻去處理。第二天我回來看看結果就行了。
在這個任務裡,Agent 首先能定位到正確的檔案,嘗試重現並修復 bug,然後驗證修復後的程式碼對不對。這個過程可能需要很長時間,所以我很希望能有這樣的 agent 來幫忙。
很多人覺得程式碼 Agent 最終會完全取代程式設計師,但我不這麼想,因為程式設計的價值在於迭代,很多人在看到一個初始版本之前並不明確地知道自己想要什麼。在大多數的程式設計任務裡,使用者需要的是一個能立即給出初始版本的系統,讓他們可以立即開始迭代最佳化、補充更多資訊。
Lex:在 Replica Agent 的設計中,Agent 不僅能設定開發環境、解決軟體包問題、配置資料庫,還能部署應用,這種設計是不是符合你們對於程式碼 Agent 的預期?Cursor 會開發類似的功能嗎?
Arvid:是的,Replica Agent 的確是在這個方向上。對於某些型別的程式設計來說,這個工具非常好用。
目前 Cursor 還沒有去積極開發類似的產品,不過我們的目標是讓程式設計師的工作更輕鬆有趣,那些繁瑣的重複性任務完全可以交給 agent 處理。我們甚至可以讓 agent 在後臺執行,理解程式設計師的操作意圖。
假設有一個同時涉及後端和前端的 pull request,程式設計師在前端工作時,可以讓一個 agent 在後臺執行,理解程式設計師的做法。當程式設計師從前端轉到後端部分時,agent 就可以生成一些初始程式碼,供程式設計師在這一基礎上進行迭代。
Lex:Arvid 寫過一篇文章叫 Shadow Workspace: Iterating on Code in the Background。可以具體講一下 Shadow Workspace 嗎?

Arvid:我們希望很多工可以在後臺處理完成,我們也在這個方向上做嘗試。
除了快取預熱(cache warming)或者確定進入命令提示詞的正確上下文之外,我們還沒有太多後臺操作。整體想法是,如果我們能在後臺進行計算,那麼就可以在更長的時間維度上幫助使用者完成任務,不僅僅是預測接下來幾行你要寫什麼,而是預測在接下來的 10 分鐘裡,使用者打算做什麼,透過在後臺處理,可以投入更多的計算資源。
基於這個想法我們實驗了 Shadow Workspace 。想要充分利用後臺處理的優勢,就需要給模型提供反饋訊號。如果沒有反饋,單純延長模型的思考時間也能提升效能,比如 o1 就是個很好的例子。
另一種提升效能的方式是讓模型迭代和獲取反饋。對於程式設計師來說,一個非常重要的反饋來源是 language server ,它們可以在開發者 coding 的時候就給出錯誤提示,還支援跳轉到程式碼的定義位置,理解程式碼的結構。我們的做法和 VS code 一樣,基於 LSP 給開發者作出對應提示,但我們還想在不影響使用者的前提下,在後臺把同樣的資訊提供給底層的 LLM 模型。
💡
Language Server Protocol (語言伺服器協議,簡稱 LSP)是微軟在 2016 年提出的一套統一的通訊協議方案。,定義了一套程式碼編輯器或 IDE 與語言伺服器之間使用的協議,語言伺服器提供自動完成、轉到定義、查詢所有引用等語言功能。每種程式語言都有其獨立的 language server,這些 language server 都會透過 LSP(language server protocol)與VS Code進行互動,由此 VS Code 就不需要自己內建一些東西來支援各類提示需求。
所以 Shadow Workspace 背後的思路是,也許我們可以在後臺執行一個隱藏視窗,透過特定的標誌,把這個隱藏的視窗設定成不可見狀態,開發者使用者視角下是無法看到這個視窗的。在這個視窗中,AI agent 可以隨意修改程式碼,只要它們不儲存就行,因為隱藏視窗和主視窗共用一個資料夾,只要不儲存修改,就不會影響到實際檔案。然後 agent 可以從 linting 中獲得反饋,實現 go to definition 功能,對程式碼進行迭代。
最終的預期是 Agent 可以在後臺執行一切。
我們的很多 blog 也都在在探討怎麼實現這件事,因為確實有點複雜。我們希望這個系統能在使用者的電腦上執行,這樣它就能精確地映象使用者的環境。
在 Linux 系統上,我們可以映象檔案系統。透過這種方式對檔案進行修改,會讓 AI 以為自己是在操作實際檔案,但這些修改都是儲存在記憶體中的。要實現這個功能,需要開發一個類似核心的擴充套件。在 Mac 和 Windows 系統上,實現起來可能會稍微困難一些,但這個技術挑戰也很有意思。
Aman:為了避免人類開發者和後臺 agent 同時工作時可能出現的檔案寫入混亂,我們還設計了一個寫入鎖定的限制。具體來說,在個過程中,我們 LLM 會對檔案寫入許可權加鎖,Agent 只在 Shadow Workspace 中操作,所有改動都只存在於記憶體中,而不直接修改磁碟上的實際檔案。在這個過程中, LLM/Agent 仍然可以觸發程式碼檢查和獲取語法提示。當 Agent 需要執行程式碼時,系統會發出提示,如果使用者同意這個操作,就會從 language server 或 Shadow Workspace 那裡解除鎖定。
06.
SOTA 模型也不擅長找 bug
Lex:讓模型修改文件這件事聽起來很有趣,以後我們就可以讓 agent 去執行一系列任務,第二天再來看這個 Agent 同事都完成了哪些工作。
Aman:我們可能會設計不同的執行版本。有些任務比較簡單,比如使用者在程式設計時,有些操作模型幾分鐘就可以幫使用者完成,這種時候可以讓程式碼在使用者的本地機器上執行。但有些任務更復雜,需要更長時間來完成更大的更改,可能更適合在遠端沙盒環境中執行。這就帶來了一個問題:如何精確地複製或最大程度複製使用者的本地環境,確保遠端沙盒與使用者本地環境中執行程式碼的效果一致。
Lex:我覺得 Agent 應用範圍不應該侷限於 coding。比如說我們現在正在錄的這期播客就涉及影片剪輯、上傳影片、翻譯和配音等,我可能會希望 Agent 可以幫助我們把很多和影片編輯不直接相關的任務自動化。在 coding 上,我比較期待 agent 能幫忙找 bug,特別是邏輯錯誤,以及幫助確定大方向等等。
Aman:有一個很有意思的現象是,如果使用者給出的 prompt 是找 bug,那麼模型的準確率其實很低,在這類任務上即便是最聰明的模型表現也很差,甚至是 o1 這樣的模型。
Lex:怎麼解釋這個現象?
Aman:模型是 pre-train 資料分佈的一個強對映,隨著 loss 越來越低,模型的泛化能力也會不斷提升,但目前 loss 還沒有低到能讓模型在程式碼領域完全實現泛化。我們目前使用到的最新一代的模型很擅長程式碼生成和問答,這是因為在 pre-train 階段這類資料特別多,GitHub 上有幾萬億個 token,Stack Overflow 上也有海量的問答內容。
但是如果我們想讓模型做一些網上很少見的任務,比如 Cursor Tab 要做的根據已完成的編輯預測下一處編輯,這些模型在這類任務和場景上的弱點就會暴露出來。漏洞檢測(bug detection)也是一個很好的例子能說明模型的這個短板。網際網路上真實的 bug 檢測和修復的完整案例其實很少,並不像程式碼問答那樣資料豐富,所以模型在這類任務上就表現得很吃力。
不過我認為這是模型能力遷移的問題。就像我們已經看到的,我們可以把模型在 pre-tarin 階段獲得的通用的程式碼理解能力遷移到 Cursor Tab 的任務上。如果一個通用模型在程式碼方面很強,也應該能透過類似的方式把它的能力遷移到 bug 檢測任務上,只是需要一些適當的引導和調整。
Sualeh:需要說清楚的一點是,這些頭部模型其實對程式碼的理解是很到位的。在 pre-train 階段,在構建內部 representation 時 ,我們幾乎可以確定,在處理流程的某個環節,模型能夠感知到程式碼中可能存在一些問題,但是僅僅是感知到“不對勁”是不夠的,最關鍵的問題在於,人類對於哪些bug真正重要有著準確的判斷力,比如有的 bug 無關緊要,有的卻會導致伺服器宕機。
人類工程師之所以能做到這一點一部分也是行業經驗和文化的積累。為什麼資深工程師很厲害,是因為他們知道 3 年前有人寫了一段有問題的程式碼導致了伺服器宕機,相比之下,如果只是在做實驗性質的程式碼,有一些 bug 也沒關係,因為主要目的是嘗試和體驗。所以如果在寫實驗程式碼時,模型過分較真,不停地給出提示,反而會很煩人。
但如果是在寫生產環境的程式碼,比如寫一個數據庫,在 Postgres 或 Linux 這樣的系統寫程式碼,或者假如我們是 Linus Torvalds 這樣的人(注:Linux 的創始人),那就必須做到萬無一失,任何 edge case 的 bug 都不能有,這就要求模型能準確理解開發者對程式碼質量的要求程度。
Aman:但即使作為開發者我們把對模型程式碼任務的質量要求調到最高,模型還是不能完全理解到這些微妙的差異。
Lex:但其實人類工程師要真正做到準確判斷不同 bug 的重要性也很難。比如你們也有一個原則是,如果某一段程式碼可能會帶來不可控的影響,應該給它特別註釋去說明“This line of code is dangerous.”,甚至還會用到大寫標記來突出。
Arvid:是的,我覺得這個原則對今天的 AI 模型同樣適用。如果每一行程式碼上都有專門的標註,那麼模型就會更關注這一部分,也更有可能發現這一部分的bug。但這個做法本身也也存在爭議。一些人會覺得這種做法不太美觀,比如 Sualeh 就不喜歡。
除非我們可以做到所有程式碼都能透過嚴格的數學方法來證明其正確性,到那個時候就可以隨意寫程式碼了,因為只要驗證透過,我們就能確定這裡沒有引入到任何 bug。
Sualeh:雖然從程式碼審美角度來看我並不喜歡這種做法,但我認為它確實對模型和人類都有用,因為人類經常會忘記很多事情,而且很容易犯一些小錯誤,從而導致伺服器崩潰。就算我們進行了大量的測試,但在這方面我們還是要格外謹慎。
Aman:是的,就拿普通的文件註釋來說,人們在修改程式碼時往往只是匆匆掃一眼,想著“哦,我知道怎麼做”,但實際上我們還需要更加明確地提醒他們注意某些細節,否則很容易就疏忽了。
Arvid:我認為未來人們不會再手寫測試程式碼了。當 coding 的同時,AI 模型會自動為這段程式碼生成一個規範說明(spec),開發者只需要稽核這個規範是否符合預期。同時,推理模型會透過計算來驗證程式碼實現是否完全符合這個規範。我覺得這種方式將適用於大多數程式碼功能的測試。
Michael:這件事是不是又會涉及到我們在前面提到的,人們在開發軟體時可能很難很清楚地描述自己的目標和意圖,而有時候之所以難以證明程式碼實現符合預期,也可能正是因為我們很難準確地描述出我們想要什麼。
即便模型給出了一個 spec ,我們還是要面對一個問題:我們真的能做到 formal verification 嗎?這在技術上是否可行?我覺得這裡面還有很多需要深入探討的地方。又比如 spec 是用自然語言編寫的還是用公式來表達?即便是 spec 也還有很多細節很難被清晰界定。所以只是考慮 formal verification 還不夠。
Lex:你在前面提到模型在處理 bug findings 這類任務時通常表現不是很好,長遠來看你的預期是什麼樣的?
Sualeh:我覺得模型首先應該能幫助我們解決那些簡單的小錯誤,它應該能快速檢查並及時發現“差一錯誤(off by one error)”這種簡單的 bug,因為這類問題非常常見。在這個時候模型就應該給出對應提示,但長遠來看,它肯定還需要能夠捕捉到更難發現的bug。
Michael:我覺得很重要的一點是,找 bug 能力是讓 AI 完成更多 coding 任務的必要條件。當我們想要讓 AI 幫我們構建越來越多的系統時,就不僅需要它幫助做程式碼生成,還需要能驗證程式碼的正確性,如果沒有這種驗證能力,我們前面聊到的這些模型在程式碼任務上的問題就很難解決,讓 AI 擁有找 bug 的能力不僅僅是為了找出人類寫程式碼時產生的 bug,更關鍵的是能夠驗證和檢查 AI 生成的程式碼。
Arvid:關於如何實現讓 AI 更好地找到 bug 我們也有過很多次討論,比如如何訓練一個 bug 檢測模型,還有一個更受歡迎的想法是:“製造 bug 比找 bug 更簡單”,所以對應的思路是,我們可以先訓練一個模型,讓它學會在現有的正確程式碼中注入各種型別的 bug,基於這種方式我們會得到大量帶有已知 bug 的訓練資料,然後,就可以用這些合成數據來訓練另一個模型,專門用於發現和檢測程式碼中的 bug。這只是眾多可能方法中的一個例子,還有很多其他的訓練思路可以嘗試。
Michael:還可以有其他思路來完成。我們可以先想象有一個 access 範圍足夠寬的模型,這個模型可以接觸到包括程式碼在內的各種資訊,因為只盯著一個檔案來找 bug 其實是很難的,對人類來說也很難,所以通常情況下我們的做法是執行程式碼、檢視traces、透過偵錯程式逐步檢查。這是另一個完全不同的方向。
所以這裡有兩種不同的產品形態。一種是有一個非常專業且執行速度很快的模型在後臺執行,一直去尋找 bug,另一種情況就是開發者已經很明確有一個 bug 需要解決,在這種情況下,人們可能會投入大量的計算資源來做這件事。
Lex:你們考慮過程式碼生成、編輯和 debugging 環節裡引入付費的可能性嗎?如果能幫助發現 bug,或者生成讓使用者非常滿意的程式碼,使用者願意為之付費或者打賞,而除了獲得收入之外,付費也是比“接受程式碼”更強的訊號來幫助最佳化產品和模型。
Arvid:這個思路很有意思但也很有爭議,並且還涉及到我們對於使用者心理的把握,也涉及到算力成本和定價之間的平衡。但整體上我們會覺得引入這個機制可能會讓產品變得不那麼有趣,可能透過訂閱的方式,每月支付一定費用後享受所有這些功能更符合開發者的需求,而不是將 debugging 等功能點單獨付費,即便是打賞機制也會有影響。
Lex:在具體的程式碼任務和程式碼終端結果之間有更好的資訊互動?比如是否存在一個類似於 code -> terminal -> output -> analysis -> code suggestions 這樣的迴圈和反饋機制?
Aman:使用者在使用 command+K 功能的時候會利用到終端上下文資訊,但我們目前還沒有實現迴圈反饋的部分,但我們覺得這樣的功能應該很有價值,問題在於這個功能是在前臺執行,還是像我們之前討論的那樣在後臺執行。
Lex:前面提到基準測試並不能真實反應模型程式碼任務的能力?這二者之間有什麼區別?如果要評估模型,我們看到的基礎測試有什麼不足?
Sualeh:基準測試和現實中的程式碼任務中間有一個非常重要且關鍵的細節差異,那就是現實中的程式碼任務並不是“程式設計面試題”。在現實的工作中,人們有時會用不標準的英語來表達任務需求,或者會給出“照我之前那樣做”、“新增這個,然後幫我做那個,再做一個 UI 元素” 之類的指令。很多工都是依賴於具體場景的。
所以對於模型和產品來說,真正需要的是理解使用者意圖並完成他們想要的事情,真實的人類需求往往是模糊的、不那麼明確的,而不是“面試題”那樣具有非常明確、詳細規範說明的任務。
Michael:除了 Sualeh 和 Aman 提到的問題外,基準測試還存在一個問題是,實際可以建模的內容與真實的程式設計之間存在一定的偏差,這種偏差很難概括,因為真實的程式設計是很混雜的,有些時候很難去明確解釋什麼是對的,什麼是不對的。
而且,由於測試集是公開資料集的原因,這件事會變得更難。有兩方面的原因,首先,公開的基準測試有時會被進行針對性地“爬坡最佳化”(hill climbing),另一方面,要從模型中剔除掉這些公開基準測試的資料也非常難。
舉個例子,SWE-Bench 是目前最受歡迎的 Agent 基準測試之一,但它的測試資料也是 Foundation Model 的訓練資料的一部分。所以如果我們給到 Foundation Model 一個 SWE-Bench 問題,即使沒有提供程式碼庫的上下文,模型還是可以“編造”出一個正確的檔案路徑和函式名。
💡
SWE-Bench 是一個用於評估 LLM 解決從 GitHub 獲取的現實世界軟體問題的能力的基準測試。該基準測試包括向 Agent 提供程式碼儲存庫和問題描述,並要求它們生成可解決問題描述的補丁。SWE-bench 中的每個測試樣本都來自 GitHub 上 12 個開源 Python 儲存庫之一中已解決的 GitHub issue。每個樣本都有一個關聯的拉取請求 (PR),其中包括解決方案程式碼和用於驗證程式碼正確性的單元測試。
為了更準確地評估 AI 模型的軟體工程能力,OpenAI 在對 SWE-bench 的改進基礎上推出了 SWE-bench Verified。
Aman:在這種情況下,模型可以直接在 issues 和 pull requests 上訓練,也許各個實驗室會開始做得更好,或者他們已經在資料清理方面做得不錯了,但他們不太可能完全剔除掉本身的訓練資料。這些都是一些最受歡迎的 Python 庫,比如 SimPy。我不認為他們會為了在基準測試中獲得真實的評估分數,而犧牲模型在 SimPy 這些主流 Python 庫上的效能。
Michael:由於基準測試的這些 bug,很多大模型團隊開始用其它測評方式來獲得模型反饋,比如讓真人實打實地去使用這些系統並提供定性反饋。我還知道有一些 foundation model 公司中,有專門的員工就是專門做這件事。在我們內部,除了用我們自己的私有測試集外,我們也非常依賴這種定性評估。
07.
Code Indexing 和 Context
Code base Indexing
Lex:如何防止Cursor對資料庫進行未經授權的或錯誤的修改?
Sualeh:目前確實有一些還不錯的解決方案,我們也在基於 PlantScale 開發一個 API ,這個 API 的核心功能是為資料庫提供分支(branches)。如果開發人員在開發某個功能並想要對整個資料庫進行測試,但又不想直接對整個資料庫進行測試時,就可以透過給資料庫新增 branches 來做這件事。他們實現這一功能的方式是在 write-ahead log 中新增一個分支,而要做到完全正確涉及很多技術複雜性。今天資料庫公司也在不斷迭代來應對新的需求。
我們使用的一個數據庫 Turbopuffer 未來可能會實現 write-ahead log 的分支功能,AI agents可以利用這些分支進行測試,這可能會成為資料庫必須支援的一個功能。
並且我覺得到未來所有東西都需要 branches,隨著 branches 越來越多就要設計出更好的演算法來做記憶體、CPU 以及其它資源的最佳化。
Lex:隨著使用者規模的擴大,Cursor 遇到哪些挑戰嗎?
Michael:使用者增長的過程其實也是每秒請求數不斷增加一個數量級的過程。一開始我們是用通用元件進行快取和資料庫操作,隨著系統規模越來越大,就會遇到各種問題,今天我們的規模就會出現表溢位(table overflows)這樣的問題。所以到後來,我們也構建了一些自定義系統,比如我們的 indexing 系統,用於計算程式碼庫的語義索引並回答關於程式碼庫的問題。我覺得系統一直最難 scale 的事情之一。
Sualeh:我有一些朋友是很資深的工程師,他們經常提到,在系統 scale 的過程中,很難準確預判判斷系統會在哪個環節出問題,即使事先做出了預測,但還是會有遺漏,比如每次增加新功能時總會發生一些意外。
不過對於剛剛提到的程式碼 indexing 系統,我們目前在做的事情是,在當上傳程式碼時,我們將所有程式碼分塊讓然後傳送出去。我們對待客戶問題非常謹慎,將程式碼 embedding 儲存在資料庫中實際上儲存的並不是程式碼本身的內容,這樣做的好處是可以確保我們不會引入客戶端的bug。此外,我們還在伺服器上儲存了很多關於程式碼塊的詳細資訊,並且所有內容都已加密處理。
我們的方法是先把所有程式碼分塊,然後將這些程式碼做 embedding,在這之後,我們把這些 emebdings 儲存到資料庫中,而不是去儲存任何原始碼。這麼做首先客戶確保不會引入使用者程式碼庫中的 bug,另外這些存到伺服器上的所有內容也都是加密的。
在這個過程中我們遇到的一個技術難點就是,如何確保本地 index 和程式碼庫的狀態與伺服器上的保持一致?
我們最終採取的技術方案是,給每一個檔案和資料夾都設定一個雜湊值,其中資料夾的雜湊值是其所有子檔案和子資料夾雜湊值的組合。這個過程可以遞迴進行,直到根目錄。這其實是相對複雜的一種方式,還有另一種簡單的方法是,為每一個檔案儲存一個雜湊值,然後再以每分鐘為單位嘗試從伺服器上下載這些雜湊值,找出哪些檔案在伺服器上不存在,從而保證客戶端和伺服器之間的狀態一致。
但這種操作會給客戶端帶來嚴重的網路負擔,沒有人希望使用 Cursor 時,我們一直佔用它們的網路頻寬,而且這種操作也會給資料庫帶來龐大負擔,差不多每秒鐘都要讀取數 10TB,甚至接近 20TB 的資料庫,所以肯定不能這樣做。
所以我們採取的方法是隻嘗試對專案根目錄的單個雜湊值進行比對,如果發現有不匹配,才進一步去找出具體哪裡不一致,再透過子節點一層一層向下找到具體哪裡發生了變化。這種逐層檢查和遞迴的方式,能夠在確保同步準確性的同時最大程度地減少網路和資料庫成本,因為對於大多數使用者、大部分任務來說,雜湊值都是一致的。這個結構叫做 Merkle Tree。
Sualeh:程式碼檢索系統的 scale 之所以很難是因為,不僅使用者量增加,其中一些客戶的程式碼庫相當龐大的,我們一開始設計的時候參考的是 Electron 這樣大型的程式碼庫,但它的規模顯然和那些存在了20 年、擁有海量檔案的公司的程式碼庫還差很多,與此同時還需要考慮到怎麼支援一大群程式設計師來使用。
這裡涉及很多細節,構建一個簡單的方案很容易,但要考慮到擴充套件到支援大量使用者、大量公司客戶顯然是個很困難的問題。如果去拓展程式碼檢索系統是 Cursor 規模擴充套件過程中遇到的挑戰之一。過去幾周幾個月裡一直在處理這些擴充套件性問題。
Aman:我們在設計這個 index 系統的時候也加入了很多巧妙的細節。比如,成本的瓶頸並不在於向量資料庫或資料庫中儲存的資料量激增,而在於 embedding ,假如一個公司中有好幾個人使用了相同的一段程式碼,就沒必要為每個人的每一次操作單獨做 embedding 的計算,即便這些程式碼在不同 branches 上,或者只是做了一些本地修改。
在這個時候,就可以使用到一個技巧:對給定程式碼塊的雜湊值所計算出的實際向量進行快取。這意味著,當公司裡第 n 個人需要嵌入他們的程式碼庫時,系統就可以快速響應。而且,這個過程中我們其實不需要在伺服器上儲存原始碼,因為只調用到了向量資料庫和向量快取中儲存向量。
Lex:對程式碼庫 indexing 的投入帶來的好處是什麼?
Arvid:我認為最明顯的好處是,當用戶在龐大的程式碼庫中想要查詢某個功能的具體實現位置時,可能只記得個大概,比如“我想找到我們實現 X 功能的地方”,但在普通的文字搜尋中,用高糊並不清楚應該搜尋什麼關鍵詞,如果有了 code base 的 indexing,就可以向一個 code base chatbot 提問,很多時候,code base chatbot 都能準確找到對應的程式碼位置。
Aman:我認為在未來,這個功能只會變得越來越強,我們也在努力提高檢索的質量。我認為這程式碼搜尋這部分的想象力比人們想象中的要大。
Lex:在這個過程中為什麼沒有考慮過端側方案?
Arvid:在本地做 embedding 聽起來很厲害,但實際操作起來很難。有一點我想指出的是,使用者們所使用的裝置效能參差不齊,比如有些使用者用的是最新的MacBook Pro,但實際中超過80%的使用者用的是 Windows系統的電腦,而且其中很多電腦的效能都不是很好。因此,本地模型實際上實際上只適用於最新的高效能電腦而且會帶來很大的負擔,即使我們想實現本地嵌入,目前也沒辦法集中精力去實現這一點。
Sualeh:除了計算機效能的限制之外,還有個現實問題是這種方案會犧牲輕鬆和高效的體驗。大公司的程式碼庫規模極大,即便一個公司給自己的程式設計師都配上了最高效能的電腦,處理這麼大的程式碼庫也會非常吃力,即便是頭部公司的頂尖程式設計師,如果一切都在本地執行,體驗也會非常糟糕。
Aman:是的,在處理近似最近鄰演算法(nearest neighbors)和大型程式碼庫時,本地做 embedding 會大量佔用記憶體和CPU 資源。現在端側模型的發展的一個趨勢是向 MoE方向發展,好處是它更多地依賴於記憶體頻寬,而不是 GPU,但缺點是這些模型通常都很大,甚至可能需要多個節點來支援,即使是效能非常好的 MacBook 也無法支援這些模型。特別是在 coding 場景,這不僅是一個模型夠不夠好的問題,而是模型能否滿足開發者日益增長的期望。也許對於一些問題,本地模型已經模型夠用,但人們總想用最好、最智慧、最強大的模型。但這些模型對於大多數人來說,在本地執行都非常困難。
Lex:相對於中心化的頭部 AI Labs,還是有不少人在提升端側模型能力的,尤其是在開源社群。你們怎麼看這一趨勢?
Arvid:除了端側模型,還有一個我很喜歡的替代案,但目前還處於研究階段,就是用同態加密(homomorphic encryption)來做 LLM 的推理,也就是說,我們在本地裝置上的輸入會被加密後傳送到雲端來做計算推理,這部分可以呼叫頭部模型能力來完成,但伺服器看不到原始資料,當伺服器完成推理計算後返回答案,我們可以在本地進行解密,整個過程中只有使用者自己才能看到最終的結果。
這個技術還很早期,並且這裡面的關鍵在於如何降低計算成本。同態加密可以很好解決目前的使用者資料隱私會暴露給模型的問題,所以如果這件事能實現就會影響很大。因為隨著模型能力越來越強、可以帶來的經濟價值是越來越高的。
💡
同態加密(homomorphic encryption):密碼學中的一種特殊加密技術,它允許在不解密資料的情況下對密態資料執行特定的計算操作,並且計算結果仍然是密文狀態,對密態結果解密後可以得到與直接用明文資料計算相同的結果。這一技術被用於隱私計算與安全多方協同計算領域。
如何解決 Context 問題
Lex:Cursor 是怎麼解決 Context 問題的?模型自動找出上下文這件事有多難?
Michael:Context 是一個很複雜的問題,我認為未來我們的確可以在自動找上下文這件事上做得更好,但也需要指出來的是,自動識別 context 這件事是有成本的。
首先,當我們給模型提供的上下文越多,它們的響應速度就越慢、請求的成本就越高,這就會犧牲我們對模型能力的呼叫以及可以在後臺處理的任務就會減少,此外,對於許多模型來說,如果 prompt 中包含大量資訊,它們也會感到困惑。因此,我們在給模型提供上下文時需要確保資訊的準確性和相關性都很高。
我們其實已經在產品中實現了一些自動上下文的功能,這是我們希望做得更好的地方,比如更好的 indexing 系統、embedding 和 retrieval 等。
這裡面還有很多很有意思的學術問題,既有我們內部在嘗試的,也有一些共識性的討論。
比如,如何讓語言模型真正理解一個新的資訊語料庫?很多人都會提到的一個方向是,能不能讓上下文視窗無限大?如果可以實現,那麼能不能讓模型在具體的任務中考慮到這個無限大上下文?如果這個問題成立,那麼接下來就又會考慮能不能對無限大的上下文進行快取?
與此同時我們也在嘗試其他思路,比如類似於模型微調的思路,將這些資訊融入到 weights 中進行學習。如果模型更多地是在 weights 級別上學習到這些資訊,而不是在上下文的話,那麼結果又會不一樣。
但最終要怎麼實現目前還沒一個定論。
Aman:讓模型直接在 weights 層面學習這個路徑可以先用 VS Code 來做概念驗證。因為 VS Code 本質是開源的,也就是說,模型在 pre-train 階段就學習過這些程式碼,甚至也學習過相關的程式碼問答內容,然後經過微調和 RLHF 後,這些模型就被訓練到能夠回答一些程式碼相關的通用問題。當我們問這個模型和 VS Code 相關的問題時,雖然有時候它會產生幻覺,但有時候它確實能很好給出答案,我認為這只是它碰巧表現得還不錯,但如果我們專門地對模型進行訓練或者 post-training,從而讓它更好地理解整個程式碼庫,可能結果又會不一樣?
還有一個值得探索的問題是,我們到底是希望模型能夠端到端地完成所有工作,比如自己做完 retrieval、回答問題、生成程式碼,還是說我們更希望將 retrieval 和底層模型獨立開?如果是後者,可能在未來幾個月內就會出現一批比今天開源 SOTA 模型還要強的模型,在這種情況下,我們可能可以把一個高質量的開源模型單獨訓練成 retrieval 模型,由這個獨立的 retrieval 模型給更強的頭部底層模型提供 context。
08.
Scaling law 、復現 o1 模型
Scaling laws
Lex:你們怎麼看接下來 scaling law 的發展?
Aman:我覺得 Chinchilla 之後,大家某些程度上在 scaling law 上有些走偏,現在大家更關心的是,怎麼在給定的inference budget下,讓模型的表現儘可能好。
Scaling law 實際上考慮的維度比我們之前提到的算力規模、引數數量和資料量要多得多。例如,inference computer 就是一個顯而易見的維度,我認為 context 長度也是一個。如果有人跟關注這兩者的最佳化,那麼可能就會訓練出一個處理超長上下文時成本更低、速度更快的模型,即便訓這個模型需要投入多 10 倍的算力,但這個過程中優先順序目標是 long context window 和 inference compute。所以怎麼理解人們對不同緯度之間的關係,以及圍繞這些緯度做權衡和探索其實很有意思。
Lex:也有人認為 Scaling law 即將遇到上限。
Aman:單純就智慧能力講的話,一定是模型越大、能力越好。
我很看好看好蒸餾。蒸餾也許是解決 data wall 的另一種方法。當資料不夠用的時候,我們可以先在已有這些 tokens 上訓練一個非常大的模型,然後將其蒸餾成一個較小的模型。這樣一來,與直接訓練這個小模型相比,我們可能能夠從這個更小的模型中獲得每個token的更多資訊。
如果投入大量資金進行訓練,能夠得到最具價效比的模型,那麼要調整多少個引數來實現。應該重視模型推理時間的計算。然而,有些人可能已經採取了一種過於簡單的方法來關注推理時間的計算,即他們只是用遠超必要數量的資料來過度訓練那些70億引數的模型,就像Llama。
如果你真的很在意這個問題,也許可以採用 Google Gamma-27B 的做法:不要僅僅訓練 token,而是直接訓練模型來最小化與Gemma 27B分佈之間的KL散度(KL divergence)。這就是知識蒸餾的思路。我們實際上是在用這個 270 億引數的模型處理所有這些token,最終目的是得到一個更小的模型。
如何復現 OpenAI o1
Lex:你們怎麼看 OpenAI o1?Test time compute system 未來會將在程式碼任務中扮演什麼角色?
Aman:Test time compute 提供了 scaling up 之外的一個不同的思路,即,我們也可以透過增加 inference 使用的 flops 來提升模型效能,透過更長時間的 inference 來實現和更大規模模型的同等質量的輸出。
有一個很有意思的現實是,有些任務可能需要一個擁有 100 萬億引數、基於100 萬億 tokens 訓練的超大模型才能解決,但這樣的問題可能只佔到 queries 的 1% 甚至 0.1%。那麼,這種情況下願意投入如此巨大資源、時間和算力去訓練一個成本高昂但使用頻率極低的模型是否還有意義呢?這樣做感覺很浪費。
相比之下,一個更合理的策略是,訓練一個規模較小的模型,這個模型能夠高效地處理 99.9% 的查詢,然後對於那些對智慧水平極高的 query,可以選擇透過更長的 inference time 來實現解決。
Lex:如何判斷某個問題需要哪種程度的智慧?能不能根據實際情況動態決定,比如什麼時候使用GPT-4,什麼時候使用小模型、什麼時候又需要用到 o1?
Aman:這個問題還沒有答案,我覺得目前還沒有人能很好地解決這種 model routing 問題。我們在 Cursor Tab 上做過類似的初步嘗試,但如果是 GPT-4o、Claude sonnet 和 o1 之間的切換可能要更加複雜。
這裡還會涉及到個一個問題是,我們需要什麼水平的模型來幫助判斷某個任務是否超出了 GPT-4 水平模型的能力範圍?可能需要一個 o1 水平的模型來作判斷。這個問題目前也沒有答案。
Lex:OpenAI 的做法是不直接給使用者展示思維鏈,而是讓模型對思維鏈進行總結。同時他們還在後臺監控這些思維鏈,以確保模型不會嘗試操控使用者,你怎麼看這種做法?
Michael:對於OpenAI來說的一個顧慮可能是他們不想讓人們從他們的模型中提煉出這種能力。如果能夠訪問到那些被隱藏的思維鏈資料,復刻 o1 可能會變得更加容易,因為我們可以直接看到模型得到最終結果所採取的步驟,這些資料是非常重要的。
Lex:CoT 資料可以被用來直接訓模型嗎?
Michael:有一些類似情況可以參考,不過也只是推測。之前有些模型的 API 會提供所有生成 token 的對數機率(log probabilities)的訪問許可權,包括對 prompt tokens 的機率,不過後來一些模型 API 移除了這些功能。其中一個猜測就是:如果外界可以拿到對數機率資料,就像今天我們可以拿到被 OpenAI 隱藏的 CoT 資料一樣,這些資料都能幫助提供更多資訊,讓人們能夠把 SOTA 模型的能力提取出來,再蒸餾到自有模型中。

排版:Doro
延伸閱讀