候使用 Codebase(Explored 搜索):從 AST 到 Embedding 的檢索鏈路拆解與 settings.json 配置驗(yàn)證)
1. 為什么你的 Cursor 有時(shí)搜代碼、有時(shí)像在瞎猜用 Cursor 寫代碼的人大概率都遇到過(guò)這種割裂感同樣一句「幫我改一下登錄邏輯」有時(shí)候它精準(zhǔn)地把src/api/auth.ts、src/store/user.ts、src/middleware/token.ts一起拉進(jìn)上下文改完還能跑有時(shí)候它只盯著你當(dāng)前打開(kāi)的那個(gè)文件改出來(lái)的東西一編譯就報(bào)Cannot find name refreshToken。差別不在模型而在它到底有沒(méi)有走 Codebase也就是左側(cè)那個(gè) Explored 搜索這條鏈路。Codebase 檢索本質(zhì)上是 Cursor 的「代碼意圖路由器」它先判斷你這句話是不是在說(shuō)代碼再?zèng)Q定要不要去索引里撈相關(guān)文件撈的時(shí)候又分語(yǔ)義向量檢索Embedding和結(jié)構(gòu)檢索AST 引用圖兩條腿走路。搞不清這個(gè)觸發(fā)條件你就會(huì)一直處在「它怎么又沒(méi)看懂我項(xiàng)目」的狀態(tài)里。這篇不聊玄學(xué)直接把觸發(fā)條件、top-k 動(dòng)態(tài)范圍、相似度閾值、AST 在其中的角色拆開(kāi)講最后給一份可復(fù)制的settings.json骨架和驗(yàn)證動(dòng)作讓你在真實(shí)項(xiàng)目里確認(rèn) Codebase 索引到底有沒(méi)有生效。適合已經(jīng)在用 Cursor、但還沒(méi)搞明白它檢索行為的人也適合想把這套邏輯遷移到自己 Agent 項(xiàng)目里的同學(xué)。2. 前置TaoToken 在鏈路里的位置與準(zhǔn)備Cursor 的 Codebase 檢索負(fù)責(zé)「找文件」但真正生成 diff、判斷語(yǔ)法是否合法、決定要不要 reject 補(bǔ)丁靠的是背后的大模型。如果你想讓這條鏈路穩(wěn)定模型側(cè)的接入得先理順。我這邊習(xí)慣用 TaoToken 做統(tǒng)一入口官網(wǎng)在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 它把不同模型的調(diào)用收斂成一套 OpenAI 兼容格式Cursor 里配自定義模型時(shí)不用來(lái)回改 base_url。準(zhǔn)備動(dòng)作很簡(jiǎn)單先去控制臺(tái)拿一個(gè) Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 然后在 API Keys 頁(yè)面生成密鑰頁(yè)面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。拿到sk-開(kāi)頭的串之后先別急著填進(jìn) Cursor用 curl 驗(yàn)一下通不通避免后面排查時(shí)分不清是檢索問(wèn)題還是鑒權(quán)問(wèn)題。curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: ping}], max_tokens: 16 }返回里帶choices[0].message.content就說(shuō)明模型側(cè)通了。這一步過(guò)了再去看 Codebase 檢索才有意義否則你分不清是「沒(méi)檢索到文件」還是「模型根本沒(méi)被調(diào)起來(lái)」。接入文檔在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面列了各模型對(duì)應(yīng)的 model 名配 Cursor 時(shí)直接抄。3. 觸發(fā)條件Cursor 什么時(shí)候才會(huì)走 CodebaseCursor 不是每次提問(wèn)都檢索 codebase它先做一次意圖判斷。判斷的核心問(wèn)題是你這句話是不是在描述一個(gè)「需要落到具體代碼上的動(dòng)作」。會(huì)觸發(fā)的情況基本長(zhǎng)這樣「修改這個(gè)函數(shù)」「給 login API 加 token 校驗(yàn)」——有明確改動(dòng)對(duì)象「這個(gè)錯(cuò)誤怎么修」「找到所有調(diào)用 refreshToken 的位置」——有定位需求「幫我找到這個(gè)變量是什么」「加一個(gè)按鈕實(shí)現(xiàn)點(diǎn)擊跳轉(zhuǎn)」——有實(shí)現(xiàn)意圖不會(huì)觸發(fā)的情況也很典型純聊天、翻譯、比較語(yǔ)言框架、問(wèn)理論問(wèn)題、寫總結(jié)文檔。這些它直接走模型不碰索引。你可以把這條規(guī)則理解成有代碼意圖intent才檢索沒(méi)有就純對(duì)話。觸發(fā)之后Cursor 的檢索不是把整個(gè)倉(cāng)庫(kù)塞給模型而是分四步走。第一步做 Embedding 語(yǔ)義向量搜索本地用 HNSW 或 SQLiteFAISS 建索引請(qǐng)求時(shí)算 top-k 最相關(guān)的文件碎片。第二步對(duì)這些候選文件做 AST 解析抽出函數(shù)列表、類結(jié)構(gòu)、imports 關(guān)系、類型定義。第三步疊一層輕量依賴圖如果 A 引用了 B、B 調(diào)用了 C搜到 A 時(shí)會(huì)順帶把 B、C 拉進(jìn)來(lái)。第四步才是把最終文件列表作為 context 注入給模型形成你看到的「read these files」。這里有個(gè)容易忽略的點(diǎn)AST 不是用來(lái)「搜」的它是用來(lái)「理解結(jié)構(gòu)」的。Embedding 負(fù)責(zé)召回AST 負(fù)責(zé)在召回結(jié)果里精確定位到函數(shù)級(jí)節(jié)點(diǎn)并保證模型生成的 patch 不破壞語(yǔ)法結(jié)構(gòu)。這也是 Cursor 比純 ChatGPT 改代碼穩(wěn)的原因——它會(huì)在應(yīng)用 diff 前再跑一次 AST 校驗(yàn)括號(hào)漏了、大括號(hào)沒(méi)閉合直接 reject不寫盤。3.1 top-k 是動(dòng)態(tài)的不是固定 5 或 10很多人以為 top-k 是個(gè)寫死的常數(shù)其實(shí)它隨任務(wù)復(fù)雜度浮動(dòng)。簡(jiǎn)單函數(shù)級(jí)修改大概 3~5中等類/模塊級(jí)任務(wù) 8~12跨文件功能開(kāi)發(fā) 15~20全局重構(gòu)能到 20 以上。判斷復(fù)雜度的信號(hào)包括提問(wèn)長(zhǎng)度、是否提工程功能「做一個(gè)登錄系統(tǒng)」算大任務(wù)、是否含多個(gè)操作動(dòng)詞添加修改重構(gòu)、是否涉及多個(gè)模塊名、是否有抽象表達(dá)「全局加日志系統(tǒng)」。3.2 相似度閾值同樣是自適應(yīng)的閾值也不是固定的 0.3 或 0.8。大范圍檢索時(shí)降到 0.18~0.25 多召回中等任務(wù) 0.30~0.40精準(zhǔn)定位當(dāng)前文件 0.45~0.55極高精度才上 0.60。規(guī)律是任務(wù)越抽象閾值越低任務(wù)越具體閾值越高。你說(shuō)「找一下所有相關(guān)代碼」它會(huì)降閾值放更多候選進(jìn)來(lái)你說(shuō)「修改 src/api/login.ts 的 login 函數(shù)」它抬閾值只留最強(qiáng)匹配。4. 可復(fù)制配置settings.json 骨架與參數(shù)對(duì)照Cursor 的 Codebase 行為有一部分可以通過(guò)settings.json影響尤其是索引范圍和排除規(guī)則。下面這份骨架可以直接抄放在項(xiàng)目根目錄的.cursor/settings.json或用戶級(jí)配置里都行。注意codebaseIndex相關(guān)字段在不同版本命名略有差異以你本地版本為準(zhǔn)但結(jié)構(gòu)邏輯是一致的。{ codebaseIndex: { enabled: true, maxFileSize: 1048576, maxFiles: 20000, embeddingModel: default, excludePatterns: [ **/node_modules/**, **/dist/**, **/build/**, **/.next/**, **/coverage/**, **/*.min.js, **/*.map, **/vendor/**, **/.git/** ], includePatterns: [ src/**, app/**, lib/**, packages/** ] }, search: { topK: { simple: 5, medium: 12, complex: 20 }, similarityThreshold: { broad: 0.22, medium: 0.35, precise: 0.50 } }, ast: { enabled: true, parseOnIndex: true, validatePatch: true } }參數(shù)對(duì)照表如下方便你按項(xiàng)目規(guī)模調(diào)參數(shù)作用建議值調(diào)大后果maxFileSize單文件索引上限1MB大文件拖慢索引maxFiles索引文件總數(shù)2萬(wàn)內(nèi)存占用上升excludePatterns排除目錄構(gòu)建產(chǎn)物/依賴漏排會(huì)污染召回topK.simple簡(jiǎn)單任務(wù)召回?cái)?shù)3~5上下文變雜topK.complex復(fù)雜任務(wù)召回?cái)?shù)15~20token 消耗快similarityThreshold.precise精準(zhǔn)定位閾值0.45~0.55太高會(huì)漏文件ast.validatePatch補(bǔ)丁 AST 校驗(yàn)true關(guān)掉易寫壞語(yǔ)法注意excludePatterns一定要把node_modules、dist、.next這類目錄排掉。我見(jiàn)過(guò)有人沒(méi)排結(jié)果搜「登錄」召回一堆壓縮后的第三方包模型被帶偏改出來(lái)的代碼引用了根本不存在的內(nèi)部變量。5. 驗(yàn)證請(qǐng)求確認(rèn)索引生效與檢索符合預(yù)期配完不能靠感覺(jué)得用可復(fù)現(xiàn)的動(dòng)作驗(yàn)證。第一步看索引狀態(tài)在 Cursor 里打開(kāi)命令面板搜Codebase Index相關(guān)命令或者看左下角狀態(tài)欄有沒(méi)有 indexing 進(jìn)度。索引沒(méi)跑完后面所有檢索都是空的。第二步做一次語(yǔ)義檢索驗(yàn)證。在 Chat 里輸入一個(gè)明確指向某文件的指令比如找到 src/api/auth.ts 里 login 函數(shù)的實(shí)現(xiàn)并列出它調(diào)用了哪些函數(shù)如果 Codebase 生效左側(cè)會(huì)彈出 Explored列出auth.ts以及它 import 的token.ts、crypto.ts等。如果只回了當(dāng)前打開(kāi)文件的內(nèi)容說(shuō)明要么索引沒(méi)建好要么這句話被判定成非代碼意圖。第三步驗(yàn)證 AST 校驗(yàn)。故意讓模型改一個(gè)函數(shù)觀察它是否在寫入前做了結(jié)構(gòu)檢查。你可以這樣問(wèn)把 src/utils/format.ts 里 formatDate 函數(shù)的返回值改成 ISO 字符串保持函數(shù)簽名不變正常情況它會(huì)生成一個(gè)只改函數(shù)體的 diff不會(huì)動(dòng)export和參數(shù)列表。如果它把整個(gè)文件重寫、還改了導(dǎo)出名說(shuō)明 AST 校驗(yàn)沒(méi)起作用回去檢查ast.validatePatch是否為 true。第四步用 curl 直接打模型側(cè)確認(rèn)檢索到的 context 確實(shí)被送進(jìn)去了。這一步偏硬核但能徹底分清是檢索問(wèn)題還是模型問(wèn)題curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: claude-sonnet-4-20250514, messages: [ {role: system, content: 你只能基于用戶提供的文件內(nèi)容回答}, {role: user, content: login 函數(shù)調(diào)用了哪些函數(shù)\n\n[粘貼 Explored 列出的文件內(nèi)容]} ], max_tokens: 256 }如果這樣問(wèn)能答對(duì)但 Cursor 里答錯(cuò)問(wèn)題就在檢索召回如果這樣也答錯(cuò)那是模型理解或 context 拼接的問(wèn)題。模型對(duì)話入口在 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 可以拿它做對(duì)照實(shí)驗(yàn)快速定位是哪一環(huán)掉了。6. 本篇常見(jiàn)錯(cuò)排查Explored 一直不出現(xiàn)先確認(rèn)索引是否建完再看你的提問(wèn)是不是被判定成非代碼意圖。把「這個(gè)項(xiàng)目怎么樣」換成「找到 src 下所有調(diào)用 fetchUser 的位置」觸發(fā)概率立刻不一樣。召回了無(wú)關(guān)文件八成是excludePatterns沒(méi)排干凈構(gòu)建產(chǎn)物和依賴目錄混進(jìn)了索引。把dist、.next、node_modules補(bǔ)上重建索引。改了函數(shù)但編譯報(bào)語(yǔ)法錯(cuò)檢查ast.validatePatch是否被關(guān)掉。AST 校驗(yàn)是 Cursor 少出語(yǔ)法錯(cuò)的底牌關(guān)了就退化成純文本編輯。top-k 太大導(dǎo)致 token 爆復(fù)雜任務(wù)召回 20 個(gè)文件時(shí)context 會(huì)很長(zhǎng)??梢栽谔釂?wèn)里收窄范圍比如「只改 src/api 下的文件」讓閾值和 top-k 都往精準(zhǔn)側(cè)走。模型側(cè) 401 或超時(shí)先跑第 2 節(jié)的 curl確認(rèn) Key 和 base_url 沒(méi)問(wèn)題。Cursor 里自定義模型時(shí) base_url 填https://taotoken.net/api別多加/v1之外的路徑。索引重建后行為沒(méi)變Cursor 有緩存改完settings.json后手動(dòng)觸發(fā)一次重建別指望它自動(dòng)感知。7. 長(zhǎng)期編碼與 Agent 場(chǎng)景的接入建議如果你只是偶爾改改代碼上面這套配置夠用了。但如果你在跑長(zhǎng)期編碼任務(wù)、或者自己搭 Agent 讓它反復(fù)讀寫倉(cāng)庫(kù)檢索鏈路的穩(wěn)定性就變成剛需。這時(shí)候建議把模型接入固定下來(lái)用 Coding Plan 做長(zhǎng)期額度管理入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它比按次調(diào)用更適合高頻 Agent 場(chǎng)景。Claude Code 這類工具接 Anthropic 兼容端點(diǎn)時(shí)配置頁(yè)在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 把 base_url 指到 TaoToken 就能統(tǒng)一走一套 Key。這樣你的 Codebase 檢索、模型生成、AST 校驗(yàn)三段鏈路里只有前兩段在 Cursor 本地第三段和模型調(diào)用都收斂到可控入口排查問(wèn)題時(shí)邊界清楚很多。最后留一個(gè)我踩過(guò)的坑別在索引沒(méi)建完的時(shí)候就開(kāi)始大規(guī)模重構(gòu)。Embedding 索引是增量的但首次建庫(kù)期間召回質(zhì)量不穩(wěn)定你會(huì)誤以為「Cursor 變笨了」其實(shí)只是索引還在跑。等狀態(tài)欄顯示完成再開(kāi)始正式任務(wù)能省掉大量「它怎么又沒(méi)找到」的困惑。