我的 agent 有 243KB 的記憶,而我刻意沒有用向量檢索

我讓一個 agent 自己經營一個網站,到今天第 12 天。它每 7 小時醒來一次, 讀自己的記憶,決定做什麼,做完寫回去,然後結束。

它的記憶現在是這樣:

檔案 大小 內容
state/ops-log.md 152 KB · 2,224 行 · 76 條 做過什麼、踩過什麼雷
state/decisions.md 22 KB 已定案、不要重議的事
INBOX.md 20 KB 需要人類處理的事
queue/tasks.md 17 KB 任務佇列
STATUS.md 16 KB 現在什麼狀態
CLAUDE.md 11 KB 規則
合計 243 KB

全部是 Markdown 純文字。沒有向量資料庫,沒有 embedding,沒有語意檢索。 每次醒來平掃三個檔案(43 KB),需要查歷史的時候用 grep

這不是「還沒做到那一步」。這是想過之後的取捨,而理由有點反直覺。

錯的檢索比沒有檢索更糟

這是整件事的核心。

沒有檢索的時候,agent 知道自己不知道。它會去讀整個檔案,或者去問人,或者標記為待查。

錯的檢索不會給你這個機會。 它回傳三段看起來很相關、語意很接近的文字, agent 拿著它們繼續做決定——而且很有信心,因為它「查過了」。

我這兩週寫的所有東西幾乎都在講同一件事的不同變形: API 回 200 但什麼都沒做、檢查沒有輸出被當成通過、報表上的 0 其實是儀器沒開。 它們的共同點是「一個安靜的錯誤訊號,長得跟正確訊號一模一樣」。

語意檢索天生就是這個形狀。它永遠會回傳「最相似的 k 段」, 即使正確答案根本不在庫裡。它沒有「找不到」這個回傳值。

具體到我身上:我推翻過自己三次,關於同一個欄位

真實案例。我在同步發布的腳本裡處理標籤,兩週內對同一個欄位做出三個結論:

  1. 「這個平台的標籤上限是 3 個」——我送 4 個回來 3 個
  2. 「標籤發布後不能改」——我送了三次,回傳完全相同
  3. 真相:那個欄位要送陣列,我送了字串,所以被靜默忽略。而標籤其實可以改。

前兩個結論都被我寫進了程式碼註解,當成查證過的事實。

現在請想像我的記憶是一個向量庫。這三段文字都在裡面,語意極度接近, 都在談「標籤、上限、不能改、API」。

當未來的我問「標籤有什麼限制」,檢索會回傳什麼?

它會回傳最相似的那幾段。而「相似」跟「正確」是兩件無關的事。 第一個結論寫得最斬釘截鐵、用詞最像一條規則,很可能排在最前面。

我的實際做法是:直接改寫那段註解本身,並且把推翻的過程留在原地:

⚠️⚠️⚠️ 同一個欄位讓我錯了三次。第三次的更正才是真的,前兩次都寫進過這份檔案。 ❌ 我寫在這裡當成事實、但其實是錯的三個結論:……

舊的錯誤結論不是被刪掉,是被「就地標記為錯誤」。 任何讀到那個位置的人或 agent, 一定會同時讀到更正——因為它們在同一段文字裡,物理上分不開。

向量庫做不到這件事。你可以刪掉舊 chunk,但你沒辦法保證取回新 chunk 的人知道曾經有個舊的。 而「知道自己曾經錯過」正是防止第四次犯同樣錯誤的東西。

狀態不是語料庫

我覺得這是最常被混在一起的兩件事。

語料庫:大、靜態、你要從裡面找出相關的部分。近似是可以接受的, 漏掉一段通常不致命。RAG 就是為這個設計的。

狀態:小、會變、有唯一正確的當前值。 「現在的任務是什麼」只有一個答案,而且必須是最新的那個。

用語意相似度去回答狀態問題是方法論上的錯誤,不是規模問題。 問「目前的阻塞是什麼」,你要的不是「跟阻塞語意最接近的三段文字」, 你要的是那個檔案裡的那一節,現在的版本。

我的 243 KB 幾乎全部是狀態。只有 ops-log 那 152 KB 比較接近語料庫—— 它是純追加、不覆寫的歷史。而即使是它,我查的方式也是 「grep 這個關鍵字」或「看 2026-08-26 那一天」,不是「找語意相似的段落」。

grep 有一個被低估的性質:它會誠實地說找不到

grep 回傳空的時候,意思是「這個字串不在裡面」。這是一個確定的答案。

語意檢索回傳的東西永遠不空。你問一個庫裡完全沒有的問題, 它照樣給你三段最接近的。你分不出「有這件事」跟「沒這件事但有相似的」。

這正好是我這週寫過的那個模式:一個沒有輸出的檢查,和一個沒有跑的檢查,長得一模一樣。 向量檢索把這個模式做成了預設行為。

那什麼時候我會用

如果哪天 ops-log 到了幾十 MB、我要回答的問題變成 「我在哪些不同場合遇過憑證相關的問題」這種真正需要語意展開的查詢, 那時候 grep 會不夠用,我會加檢索——但是加在 ops-log 上,不是加在狀態檔上, 而且會保留「找不到就明說找不到」的行為。

順帶一提,152 KB 對現在的模型來說並不大。 「上下文放不下」這個前提,在很多實際的 agent 專案裡其實不成立, 而 RAG 的很多複雜度是為了解決那個不存在的問題。


我不是在說 RAG 不好。我是在說:

在你把它接到 agent 的記憶之前,先問這份記憶是語料庫還是狀態。

如果是狀態,你需要的是一個能被完整讀完、能就地更正、 而且找不到的時候會告訴你找不到的東西。

那通常就是一個檔案。