昨天講了誰決定什麼值得記,今天講存進去的東西怎麼被找到。這兩件事表面上是兩個獨立的設計,把昨天那批系統的檢索設計對照著看會發現:搜尋方式幾乎完全由寫入內容決定。先建立這個對應關係,各家看起來五花八門的檢索設計就都能推導出來了。
「I love dark mode」和「I prefer dark themes」意思相同、字詞不同,這叫 paraphrase 問題。使用者用第二句話查詢時,存著第一句話的記憶找不找得到?每個記憶系統都要回答這題,而答案只有兩個時機:
注意一件事:這個問題對 chat 和 tool call 是不對稱的。聊天內容充滿 paraphrase,但工具呼叫是固定格式的 JSON,write_file(path="auth.go") 永遠長一樣。這個不對稱直接解釋了下面幾種設計的分歧。
不搜,全部塞進去。 DeerFlow(ByteDance 開源的 research agent 框架,記憶設計走極簡路線)把記憶上限鎖在 100 條 fact,每次呼叫全量注入 system prompt。這個選擇的帳算得很清楚:搜尋本身有成本(算 embedding、查索引),記憶夠小的時候,這些成本比直接全塞還高。適合記憶集中的場景(個人偏好、當前專案慣例),記憶量一大就破功。
只用關鍵字排名。 昨天介紹過的紀律派 engram,只用 SQLite 內建的全文搜尋(FTS5,底層是 BM25,一種按詞頻排名的關鍵字算法,不理解語意)。敢這麼省是因為它的寫入紀律:agent 存的是精煉過的決策敘述,同主題只留最新一筆,paraphrase 在寫入時已經被消除了。關鍵字搜尋還有一個被低估的優勢:搜 auth.go 或工具名這種精確字詞,它比語意搜尋更準。代價是語意跳躍接不住,搜「驗證機制」找不到存成「JWT 設計」的記憶。
多路訊號融合。 mem0(昨天講過的背景提取路線代表)面向的是 chat 場景,即使寫入時做過標準化,查詢和記憶之間還是可能有語意距離,所以它同時跑三路:語意搜尋處理意思相近、BM25 處理精確字詞、實體加分處理「Alice」和「the user」指同一人的問題,最後把分數融合排名。覆蓋面最廣,代價是每次查詢跑多個訊號,延遲和複雜度都高一截。
先看目錄再翻內文。 OpenViking 是一個把記憶當成階層式檔案系統管理的開源記憶系統:記憶分成摘要、細節、原文三層,查詢先在摘要層確認方向,需要時才往下展開。粒度的決策延後到查詢當下,token 消耗可控,代價是整條檢索管線重很多,光解析查詢意圖就是一次 LLM call。
| 寫入的內容 | 特性 | 適配的搜尋 |
|---|---|---|
| LLM 壓縮過的 fact | paraphrase 已消除 | 語意搜尋(或再加融合) |
| agent 精煉的決策 | 寫入時已標準化 | BM25 關鍵字就夠 |
| 原始全文 | 保真但說法多樣 | hybrid:語意 + 關鍵字 |
| 自動捕捉的原始觀察 | 雜訊多 | 多路融合硬撈 |
讀這張表的方向是由左到右:先決定你要存什麼(那是昨天的題目),搜尋方式跟著就定了。反過來設計(先選一個炫的檢索技術,再回頭遷就寫入)通常會做出既慢又不準的系統。
檢索還有一個沒有正解的取捨:一次取回多大的單位?mem0 的 fact 只有二十來個字元,精確但失去前因後果;mempalace(一個只存原文、用「記憶宮殿」空間隱喻組織記憶的開源系統)一次取回 800 字元的原文段落加上鄰居,脈絡完整但 token 貴、排名也容易糊。小單位賭的是提取品質夠好,大單位賭的是模型能自己從原文裡撈重點。這題和 Day 9(RAG)的 chunking 是同一題的變形,選哪邊取決於你的下游要拿記憶做什麼。
寫入定了、檢索定了,還剩一個維度:時間。三個月前存的偏好和昨天存的臨時觀察,該用同一個權重浮上來嗎?記憶該不該過期、誰決定一條記憶的死亡?明天講記憶的生命週期。