Levi Lee

2026年6月1日

先別急著上向量資料庫:Agent 時代的關鍵字檢索

過去兩年,只要談到 RAG,很多人第一個想到的就是向量資料庫。做法大概是這樣:

把文件切成小段 → 轉成 embedding 向量 → 存進向量資料庫 → 問問題時找最相似的段落

這套方法很強,尤其適合「找語意相近的內容」。例如你問:

有沒有哪段文件在講「員工離職流程」?

就算文件裡沒有出現「離職流程」這四個字,而是寫「帳號停用」、「權限回收」、「交接程序」,向量檢索也可能找得到。

但最近在 AI Agent,特別是 coding agent 的世界裡,一個很有趣的現象出現了:

很多時候,最有用的搜尋工具不是向量資料庫,而是最傳統的 grep。 也就是那種很樸素的關鍵字搜尋。

聽起來很反直覺。現在都已經有 LLM、embedding、向量資料庫了,為什麼還要回頭用 grep?答案不是因為 grep 比 embedding 更聰明。而是因為在 agent 的工作方式裡,grep 有幾個非常關鍵的優勢:

  • 它簡單。
  • 它快。
  • 它(在大多數情況下)可解釋。
  • 它失敗得相對清楚。
  • 它可以被 agent 反覆使用、修正、驗證。

這就是這篇文章想談的主題:

在 AI Agent 時代,好的檢索不一定是最複雜的檢索,而是最容易被 agent 使用、修正、驗證與追溯的檢索。

不過先把話說在前面:這個「grep moment」是 2025 到 2026 初的觀察。到了 2026 年中,討論其實又往前走了一步——關鍵字搜尋的回歸只是更大的一件事(context engineering)裡面的一小塊,而且在最難的程式碼任務上,語意檢索正在悄悄回流。這些後面第八、九節會講。

一、向量檢索很強,但它不是所有問題的標準答案

先講清楚:這篇不是要說 embedding 沒用。embedding 很有用,尤其適合這類問題:找跟這段意思相近的內容。

例如:

  • 使用者不知道文件裡的精確用詞。
  • 文件中有很多同義詞。
  • 問題是概念型的,不是關鍵字型的。
  • 資料量很大,需要先做語意召回。
  • 文件有很多不同說法,但背後意思相近。

這些場景,向量檢索很有價值。

但很多企業內部文件、技術文件、操作手冊、程式碼檢索,其實不是這種問題。很多時候,使用者問的是:

  • 這個 API 怎麼用?
  • 這個錯誤訊息在哪裡出現?
  • IP-MAC 位址怎麼申請?
  • 共用資料夾權限在哪裡設定?
  • 這個 function 是誰呼叫的?
  • 這個欄位在哪份文件裡有寫?

這些問題不一定需要「語意相近」。它們更需要的是:

精確命中 → 找到來源 → 可以核對 → 可以引用

而這剛好是關鍵字搜尋、grep、BM25 很擅長的地方。

二、為什麼 coding agent 仍然大量使用 grep?

我們可以先從 coding agent 看起。一個 coding agent 在修改程式碼時,通常不是一開始就知道答案。它會像工程師一樣探索 codebase。例如它可能會這樣做:

1. 先搜尋錯誤訊息
2. 找到相關檔案
3. 讀取 function 定義
4. 搜尋誰呼叫這個 function
5. 找測試檔
6. 修改程式
7. 跑測試
8. 如果失敗,再換關鍵字搜尋

這個流程的重點不是「一次搜尋就找到完美答案」,而是:

搜尋 → 讀取 → 推理 → 驗證 → 再搜尋

這就是 agentic retrieval。在這種工作方式裡,grep 很好用,因為它的回饋對「程式碼、英文、錯誤字串」這類語料非常清楚。

如果搜尋:

grep "PermissionDenied" -r .

找到了,agent 會看到檔名、行號、上下文。如果沒找到,agent 通常知道這條路不通,可以換成:

grep "permission denied" -ri .
grep "access denied" -ri .
grep "403" -r .

這種失敗在程式碼語料裡是「可行動的」。也就是說,agent 不只知道自己失敗,還大致知道下一步可以怎麼修正。這點非常重要。

對照之下,很多複雜的檢索系統失敗時,回饋反而不清楚。向量檢索回來一堆「看起來相似」的段落,但如果答案不在裡面,agent 不一定知道是:

  • query 寫錯?
  • embedding 模型不適合?
  • chunk 太大?太小?
  • top-k 太少?
  • 資料根本沒進索引?
  • reranker 排錯?

grep 的優點不是它高級,而是在程式碼這種語料上它相對透明。

⚠️ 一個誠實的但書:grep 的「可解釋」並非無條件。對中文長句來說,零結果未必告訴你原因是「斷詞切錯」、「使用者用了同義詞」、還是「這頁根本沒進索引」。換句話說,grep 的回饋在英文/程式碼上很 actionable,在中文自然語言上沒那麼神——這也是後面第五節要特別處理斷詞的原因。

三、grep 不是比較聰明,而是比較適合被 agent 操作

這裡要避免一個誤解:不是說 grep 技術上比 embedding 更強。

grep 不懂語意。grep 不知道「離職」和「權限回收」可能相關,不知道「申請帳號」和「建立使用者」可能是同一件事。所以 grep 當然有侷限。

但在很多 agent 工作流中,grep 的優勢在於它很適合作為「工具」。它像是一把小刀,不華麗,但:

  • 拿起來很快
  • 用法很直覺
  • 切到哪裡看得見
  • 切錯了也容易修正
  • 可以連續切很多次

對 agent 來說,這非常重要。因為 agent 的能力不是只來自單次檢索,而是來自反覆迭代:

第一次搜尋:找不到
第二次搜尋:換關鍵字
第三次搜尋:縮小路徑
第四次搜尋:找到相關檔案
第五次搜尋:追呼叫鏈
第六次搜尋:確認測試

這種流程用 grep / ripgrep / find / glob 很自然。也因此,有些人開始把 agent 描述成「正在把檔案系統本身當成主要介面」:如果每一頁文件是一個檔案、每一個章節是一個目錄,那麼 grepcatlsfind 幾乎就是 agent 需要的全部——根本不必另外架一個檢索服務。所以比較精準的說法是:

grep 不是因為理解能力更強而重新重要,而是因為它簡單、透明、可迭代,剛好符合 AI Agent 的工作方式。它把複雜度從「離線索引工程」搬到「線上推理迴圈」。

四、那 BM25 又是什麼?它和 grep 有什麼關係?

如果說 grep 是最直覺的關鍵字搜尋,那 BM25 就是更進階一點的版本。

grep 通常只回答:「有沒有出現這個字?在哪一行出現?」BM25 則會進一步問:「哪一段內容最可能跟查詢有關?

它會考慮幾件事:

  1. 查詢字詞有沒有出現(term presence)
  2. 出現幾次,但邊際效益會遞減(term frequency,且會飽和)
  3. 這些字詞是不是很稀有(IDF,越稀有越有鑑別度)
  4. 文件長度會不會影響判斷(length normalization)

這裡有一個很多人會忽略、但其實是 BM25 靈魂的細節:第 2 點的「出現幾次」不是線性累加,而是「飽和」的。

也就是說,一個詞在文件裡出現第 2 次,比出現第 1 次多加不少分;但出現第 20 次,相對第 19 次幾乎不再加分。BM25 用一個飽和函數刻意壓住高頻詞的貢獻。這正是 BM25 勝過天真版 TF-IDF 的關鍵:它不會因為某段文字「把關鍵字刷了 50 遍」就被衝到第一名。對後面第六節要談的「封面頁/關鍵字農場頁污染排序」這個問題,這個飽和特性其實已經先擋掉一部分了——但擋不全,所以還是要做資料治理。

例如你查「共用資料夾申請」,如果有兩段文件:A 段只出現一次「資料夾」;B 段同時出現「共用」、「資料夾」、「申請」、「權限」。BM25 通常會把 B 段排在前面:不是因為 B 段字多,而是因為它命中了更多「稀有且相關」的詞。

所以 BM25 可以理解成:比 grep 更會排序、而且不會被關鍵字刷分騙到的關鍵字搜尋。 它沒有 embedding 那種語意理解能力,但它透明、快、便宜,而且非常適合術語密集的封閉文件。

這也是為什麼很多內部文件檢索,不一定要一開始就導入向量資料庫。SQLite FTS5 內建 bm25() 排序函數,零外部依賴就能做出很不錯的 baseline。

五、中文文件的難題:不是搜尋不行,是「切詞」很麻煩

英文搜尋比較容易,因為英文單字之間有空白。例如 shared folder request 很自然可以切成 shared / folder / request

但中文沒有空白。「共用資料夾申請」這句話到底要怎麼切?可以是 共用 / 資料夾 / 申請,也可能被系統(例如 SQLite 預設的 unicode61 tokenizer)誤當成整串 共用資料夾申請

如果整句被當成一個 token,那使用者查「資料夾」時,就可能找不到「共用資料夾申請」。這是中文全文檢索常見的坑。

解法不只一種

先講清楚:處理中文斷詞有好幾條路,不是只有一種「正確答案」:

  • 詞彙級斷詞:接 jieba、CKIP 這類中文斷詞器,把「共用資料夾」切成「共用 / 資料夾」。最貼近語言,但多一個依賴、且斷詞器本身也會出錯。
  • n-gram:用 FTS5 內建的 trigram tokenizer,或自己做 bigram。介於兩者之間。
  • unigram(逐字切):把中文當一個一個字索引。最土、依賴最少。

這篇選最後一種來示範,因為它最能體現「grep 精神」——簡單、可控、零外部依賴。做法是:在每個中文字之間插入空白,把中文當成一個一個字來索引。

共用資料夾  →  共 用 資 料 夾

這樣使用者查「資料夾」時,系統也會把查詢轉成 資 料 夾,兩邊切法一致,就比較容易命中。

但要注意,英文和代號不能亂切。IP-MACARESAPI_KEY 這些應該保留成整串,因為它們本來就是專有名詞,拆掉反而降低精準度。所以比較好的做法是:

中文逐字切
英文與代號保留整串
中文和英文交界處補空白

申請IP-MAC位址  →  申 請 IP-MAC 位 址

unigram 的代價,要心裡有數

unigram 不是免費的午餐。它最大的代價是:召回變寬、誤命中變多。 因為「資 料 夾」會命中任何同時出現這三個字的段落,哪怕它們根本不在一起、根本不是在講資料夾。

所以 unigram 不能單獨用,它必須搭配兩個東西才會準:

  1. AND-chunking:同一個詞的幾個字盡量要求相鄰/同時命中(後面第六節的兩段式查詢)。
  2. BM25 排序:靠第四節講的飽和 + IDF,把「真的相關」的段落頂上來。

換句話說,unigram 把「斷詞」的難題,轉移成「排序與查詢策略」的難題。這在很多封閉文件場景裡是划算的交換,但它是個取捨,不是「中文就該這樣做」。因為我們追求的不是完美語言理解,而是:穩定命中、容易 debug、結果可解釋、建置成本低。

一個值得點名的反諷:grep 最大的優點,恰恰在這裡最弱

這篇應該誠實面對、而不是迴避一個更深的張力。grep 的招牌賣點——也就是第二、三節一直在強調的——是「透明」:失敗得清楚、可行動。但這個優點,恰恰在這篇主打的使用情境(中文文件)最弱。

在英文和程式碼上,零結果通常是個可行動的死路:換同義詞、換 flag、放寬路徑。但在中文自然語言上,零結果是「曖昧」的——可能是斷詞切錯、同義詞沒對上、或這頁根本沒進索引——而 unigram「召回寬、誤命中高」的特性,只會讓訊號更混濁。所以誠實的結論不是「lexical search 很好 debug,所以拿來做中文文件就對了」,反而是一條相反的梯度:

語料越乾淨(程式碼、英文、密集的專有名詞),純 lexical 的勝算越大;語言越雜、越偏語意,你就該越早伸手去拿 embedding 與 reranker。

具體到中文企業文件,「該加 embedding」這件事,很可能比那些以英文/程式碼為主的原始素材所暗示的時間點,來得更早。在你決定對一份中文語料只用 lexical baseline 之前,這點值得放在心上。

六、實作上可以怎麼做?

假設我們要做一個內部文件檢索系統,資料來源可能是 PDF、簡報、Markdown、操作手冊、技術文件、內部 FAQ。我們可以設計一個很輕量的 pipeline。

1. 離線處理文件

先把文件轉成結構化資料:文件名稱、頁碼、章節標題、頁面文字、必要時的頁面截圖。如果是簡報或 PDF,也可以一頁一頁處理。這一步很重要,因為後面要讓搜尋結果可以回到原始來源。

2. 清理資料與排除雜訊

不是所有內容都適合進搜尋索引。例如封面頁、目錄頁、章節分隔頁、只有大標題沒有內容的頁面、重複出現大量關鍵字但沒實質資訊的頁面。這些頁面如果放進索引,即使有 BM25 的飽和特性擋著,仍然容易稀釋排序(畢竟它們可能命中了一些稀有詞)。所以比較好的做法是:

主資料表保留所有頁面          → 還是查得到、可以直接指定第幾頁
FTS 搜尋索引只放有實質內容的頁面 → 不污染 BM25 排名

這一階段也順手做 PII 遮蔽:人名、員工編號、內部 email、電話在進資料庫前就抹掉。grep 哲學省下來的工程力氣,正好拿去做這種真正影響品質的資料治理。

3. 處理中文斷詞

在寫入 FTS5 之前,先對文字做處理:

def cjk_space(text: str) -> str:
    # 中文逐字切
    # 英文與數字保留整串
    # 中文和英文交界處插入空白
    ...

建索引時使用這個函式,使用者查詢時也使用同一個函式。這點非常重要——如果建索引和查詢的切法不一致,就會出現「明明文件裡有,卻搜尋不到」的情況。

4. 查詢時先精確,再放寬

搜尋分成兩段。第一段先用比較嚴格的條件(chunk 內 AND、chunk 間 OR):使用者輸入的主要 token 盡量都要命中。如果找得到,就直接回傳。

如果找不到(中文長句很常見),再放寬成純 OR 查詢,交給 BM25 排序:只要命中其中一些 token,也可以列入候選。這樣可以同時兼顧精準度、召回率,以及使用者輸入長句時的容錯。

更重要的是,搜尋結果可以標記:

matched_via = "and"          → 精確命中,比較可靠
matched_via = "or-fallback"  → 放寬後找到的,需要更保守使用

這個小欄位對 agent 很有用,因為它讓上層知道這個結果有多可靠,可以據此決定要不要再查一次。這正是「可行動的 feedback」。

七、真正影響 RAG 品質的,常常不是檢索演算法,而是結果怎麼呈現

很多人做 RAG 時,會把注意力全部放在:用哪個 embedding model?chunk size 要多少?top-k 要幾個?要不要 reranker?這些當然重要。但實務上,另一件事同樣重要,甚至更重要:你把搜尋結果怎麼交給模型?

這不是我隨口說的。把 grep 和向量檢索拿來正面對照的研究(Sen et al., 2026)發現,「工具輸出怎麼餵給模型」至少和「用哪種檢索法」一樣重要——同一份底層資料,結果用 inline 回傳,跟寫成檔案讓模型再讀回來,準確率差很多。不過那篇論文自己的措辭很謹慎:在他們的對照裡 grep 普遍贏過向量檢索,而整體分數又強烈取決於 harness 與 tool-calling 風格,即使底層資料相同也是。所以教訓不是「呈現格式贏過檢索法」這種乾淨的排名,而是:呈現是一個「一等變數」,卻被大多數 RAG 開發者低估了。

一個誠實的但書,第九節會再回來談:那篇研究量的是「長對話記憶」的檢索(LongMemEval 基準),不是合約、SOP、財報那種靜態的企業文件語料。呈現格式這個教訓在兩種場景都通用;但「grep 贏過向量」這個原始標題,要外推到企業文件檢索時得更保守一點——文件的分佈本質上就不一樣。

所以不要只丟一大坨文字給模型。比較好的搜尋結果應該長這樣:

標題:VPN 申請流程
來源:it_onboarding.pdf
頁碼:第 12 頁
命中方式:and
內容:
若需申請 VPN 權限,請先完成員工帳號啟用...

如果是簡報或 PDF,還可以附上對應頁面的截圖。而且文字和它對應的圖片要在檢索端就配對鎖定,永遠來自同一筆結果,不會錯位。

這樣模型拿到的不是一團混亂文字,而是一組有結構的證據。它知道:這段話來自哪份文件、第幾頁;是精確命中還是 fallback;文字和圖片是否來自同一頁;回答時可以引用哪個來源。這會大幅降低模型亂答的機率。

對企業文件檢索來說,可溯源不是加分項,而是基本要求。因為使用者真正需要的不是「AI 看起來很會答」,而是:AI 的答案能不能回頭驗證?

八、什麼時候該用 grep / BM25?什麼時候該用 embedding?

可以用一個簡單判斷。

適合優先用 grep / BM25 的情境:

  • 文件數量不大、資料來源封閉
  • 使用者常查精確名詞
  • 文件裡有很多產品代號、API、錯誤訊息、欄位名
  • 答案需要附來源
  • 主要資料是文字
  • 你希望系統容易 debug

那 BM25 / FTS5 很可能已經是很好的起點。

適合加入 embedding 的情境:

  • 使用者常用模糊問法、問題偏概念型
  • 同義詞很多
  • 文件數量很大、需要跨文件整合
  • 圖片、表格、截圖很多
  • 單靠關鍵字常常找不到

那 embedding / reranker 就很值得加入。

一個必須講清楚的反向訊號:硬程式碼任務上,語意檢索正在回流

這裡要更新一個常被誤讀的結論。「coding agent 都用 grep」是 2025 到 2026 初的觀察,但它有條件,而且前沿已經在變。

到了更難的場景——多檔案、跨語言、需要理解呼叫關係的修改——純 grep 會撞天花板。一個很具體的數據:在較難的 SWE-Bench Pro 這類基準上,平均一份參考解法會動到 4.1 個檔案、改約 107 行,用 Augment 團隊的話說,「這種東西你 grep 不出來」。而像 Augment Code 這種領先的程式碼工具,即使是看似規則化的程式碼搜尋,也不再只靠 grep,而是針對程式碼語意去 fine-tune 專門的 embedding 模型、建語意 context engine。

換句話說,正確的圖像不是「grep 取代了 embedding」,而是:

  • 簡單、規則化、術語密集的搜尋 → grep / BM25 足矣,甚至更好。
  • 跨檔案、靠結構與語意關係的搜尋 → 語意檢索正在回來。

最實務的做法

很多系統最後不會只選一邊,而是混合使用:

BM25 負責精確命中
embedding 負責語意召回
reranker 負責最後排序
agent 負責反覆查詢、讀取與驗證

也就是說,不是 grep vs embedding,而是 grep + embedding + agentic loop。2026 年的技術棧裡有一點值得特別點出來:reranker 往往才是真正的品質槓桿——比「用 BM25 還是 embedding」更關鍵——所以它該被當成一等公民,而不是最後才隨手補上的東西。另外唯一的原則是:不要一開始就假設向量資料庫一定是核心。

九、更大的圖像:從 RAG 到 Context Engineering

到這裡,其實可以把鏡頭再拉遠一點。

「grep 還是 embedding」這個問題,在 2026 年中已經不是討論的最前沿了。場上正在發生的更大轉變是:RAG 從來不是終點,只是起點;進入 agent 時代,「檢索」正在被一個更大的學科吸收進去——context engineering

差別在哪?

  • 舊問題是:怎麼一次撈到最相關的 top-k?
  • 新問題是:在每一個推理步驟,agent 應該把哪些資料、哪些工具、哪些記憶放進 context——以及同樣重要的,該丟掉什麼?

檢索(不管是 grep、BM25 還是 embedding)只回答了其中「要哪些資料」這一塊。Context engineering 還要處理另外三塊,而這篇前面幾乎沒碰到:

1. 記憶(memory)。 第七節的反諷在這裡兜回來了——那篇被拿來主張「grep 贏過向量」的研究,跑的是 LongMemEval,一個整個都在測「長對話記憶」、而不是文件檢索的基準。這提醒我們:grep vs 向量的結果是在「記憶」這個 regime 量出來的,不是「企業文件」這個 regime;同時也提醒我們,記憶本身就是獨立的一層。現代 agent 系統普遍會分層:

語意記憶(semantic memory):知識、事實,常用向量 + 知識圖譜
情節記憶(episodic memory):過去的推理軌跡與成敗,摘要後存起來

讓 agent 從「上次怎麼解的」學習,而不是每次從零開始。這是純檢索框架完全沒碰到的維度。

2. Context 的組裝與壓縮(context assembly & compaction)。 這可以說是 context engineering 的核心,也是最多團隊低估的一格。就算檢索做到完美,把全部東西塞滿視窗仍然會讓推理變差——這就是「context rot」:當視窗被「勉強相關」的內容塞滿,準確率會下滑。所以一個真正的 agent 迴圈,每一步都得決定:哪些原文照留、哪些摘要、哪些丟掉、什麼時候把累積的歷史壓縮掉。檢索餵養這件事,但解決不了它。

3. 結構與來源(graph / provenance)。 GraphRAG、知識圖譜把「實體之間的關係」帶進檢索,讓答案不只是「相似的段落」,而是「可追溯關係鏈的證據」。對需要可溯源、可稽核的企業場景,這越來越是剛需。

所以更完整的心智模型是:

Context Engineering
├── Retrieval:       grep / BM25 / embedding / reranker   (這篇主要在談這塊)
├── Memory:          episodic + semantic
├── Context 管理:     每步決定保留/摘要/丟棄/壓縮(context rot)
└── Tools & Provenance:知識圖譜、結構化來源、驗證 hook

這篇講的 lexical baseline,是這張圖裡「Retrieval」這一格做得又輕又紮實的方法。但別忘了其他幾格的存在——當你的 agent 開始需要「記得上下文」、「管理一個爆滿的視窗」、「解釋關係」時,光靠檢索是不夠的。

十、這件事給 AI 工程師的啟發

「Grep is all you need」這句話真正有價值的地方,不是叫大家放棄新技術。它真正提醒我們的是:不要把問題過早複雜化。

很多 RAG 系統一開始就導入向量資料庫、embedding pipeline、reranker、複雜 chunking 策略,但最後問題其實出在:文件沒清乾淨、metadata 沒設計好、來源沒有保留、中文斷詞沒處理、結果格式不適合模型讀、搜尋失敗時沒有 feedback、agent 不知道下一步該怎麼修正。

這些問題不是換一個 embedding model 就會自動解決。相反地,一個簡單的 BM25 baseline,常常能讓你更快看清楚系統真正的瓶頸。因為它透明、可 debug,你可以很清楚知道:

為什麼這筆結果被找到?
為什麼那筆結果沒被找到?
是哪個關鍵字命中?
是哪個頁面污染了排序?
使用者查詢是不是太模糊?

這些都是工程上非常珍貴的訊號。

十一、結論:不是回到舊技術,而是重新理解檢索

所以,Grep is all you need 嗎?答案是:不一定。

但如果問題是「我是不是不該預設所有 RAG 都要從向量資料庫開始?」——答案是:對,你不該。 更好的做法是:

先用簡單、透明、可溯源的 lexical search 建立 baseline。
遇到語意查詢、同義詞、多模態、大規模排序、跨檔案結構問題,
再把 embedding / reranker / 知識圖譜加進來。
而「記憶」與「上下文怎麼組」這兩件事,要當成和檢索同等重要的問題看待。

在 AI Agent 時代,檢索不再只是「一次找 top-k」的問題。它更像是一個工作流:

搜尋 → 閱讀 → 推理 → 驗證 → 修正 → 再搜尋

而 grep、BM25、FTS5 這類工具,雖然傳統,卻非常適合放進這個工作流裡。它們的價值不是比較炫,而是比較可靠。

最後可以用一句話總結:

在 agent 時代,最好的檢索系統不一定是最複雜的系統,而是最容易被使用、修正、驗證與追溯的系統——而檢索本身,也只是更大的 context engineering 的第一格。

參考資料

文章