Memory retrieval 根本就設計錯了,記憶不是拿來「撈」的
最近在思考一個問題:為什麼長對話的 LLM agent 記憶系統做到後來都不好用?
明明把對話歷史存起來了,向量化了,能做相似度搜尋了,但 agent 還是常常記錯、漏記、或是找到一堆不相關的片段。
我覺得根本問題不是技術執行問題,而是設計哲學本身就錯了。
「撈記憶」這個模式的本質缺陷
傳統的 retrieve-then-reason 架構,邏輯是這樣的:先根據當前問題,去記憶庫裡撈最相近的片段,然後把撈到的東西塞進 context,再讓模型推理。
這個流程看起來合理,但有一個致命假設:「你在推理開始前就知道需要哪些記憶。」
這個假設在人類怎麼記憶這件事上根本不成立。嚴格來說,記憶是在思考過程中被「重建」的,不是被「提取」的。你在想一件事的時候,才會觸發相關記憶浮現,然後那段記憶又觸發另一段,整個過程是動態的、鏈狀的。
如果在推理開始前就要決定「要記哪些東西」,就等於強迫你在不知道要幹嘛之前先決定要用什麼工具。這是 static retrieval 的根本矛盾。
把記憶存成圖的想法
我自己實驗過一個方向:不要把記憶存成一堆向量,而是存成帶有結構的圖。
把每段記憶拆成三個層次:
- Cue(線索):觸發這段記憶的關鍵詞或情境
- Tag(語義橋):這段記憶跟哪些概念有關
- Content(內容本身):實際的對話或事件
Tag 這一層是最重要的設計。它不是分類標籤,而是「語義橋接器」。同一個 tag 可以連接到完全不同的對話片段,讓原本看起來沒關係的記憶可以通過共享的 tag 被關聯起來。
這樣的結構讓記憶訪問變成一個可以「邊走邊決定的圖遍歷問題」,而不是一個「一次性的向量搜尋問題」。
推理本身變成記憶控制迴路
這裡有一個關鍵轉換,讓我覺得這個方向真正有意思的地方:
當記憶是圖結構的時候,agent 在推理每一步時,可以主動決定:要展開哪些節點、要剪掉哪些岔路。
這不是 retrieval 決定推理,而是推理在控制 retrieval。
舉個具體例子:agent 問「這個用戶上次提到的工作問題後來怎樣了?」
傳統做法:找跟「工作問題」最相似的向量,撈三筆,塞進去,然後推。
圖遍歷做法:從「工作問題」這個 cue 出發,發現有兩個 tag 連接到它,一個是「壓力」,一個是「時間管理」。走「壓力」這條路,發現連到另一段關於家庭的對話,這時候 agent 可以判斷「這個方向不相關,剪掉」,繼續走「時間管理」,找到更近期的一段對話說用戶換了工作。
這個過程裡,推理在引導記憶訪問,記憶訪問的結果又更新推理狀態。這才是真正的 memory-reasoning integration。
為什麼不能無限展開?
圖遍歷有一個很現實的問題:combinatorial explosion。
如果你有 1000 段記憶,每段有 5 個 tag,每個 tag 連到 10 個其他節點,那你展開幾層之後就是天文數字。
所以剪枝策略和展開預算設計很重要。我實驗的方向是讓 agent 維護一個「當前推理目標」,只展開跟這個目標相關的節點,並且對展開深度設置硬性限制。
嚴格來說,這其實是把 memory access 轉成了一個 constrained search 問題:在有限的 token budget 和展開步數內,找到最有用的記憶組合。
這比「撈最相似的 top-k」難實作,但效果好很多,特別是在那種「問題本身就很模糊,你不知道你要找什麼」的情境。
對現有架構的幾個省思
VectorDB + RAG 這條路我不是要否定它,在知識庫問答這個場景它非常適合。問題是很多人直接把這個架構搬到 agent memory,然後發現效果差,就以為是 embedding model 不夠好,或是 chunk 策略需要調整。
其實根本問題是:知識庫查詢和記憶系統是兩種不同的需求。
知識庫的查詢對象是靜態的、結構穩定的文件。記憶系統的查詢對象是動態積累的、高度個人化的、而且往往是碎片化的對話。
這兩個場景需要不同的設計。把 RAG 架構直接用在 agent memory 上,就像是拿搜尋引擎來管你的個人日記,找得到但沒有辦法理解上下文關係。
圖結構的記憶讓「context」真的有意義,因為它讓你可以追蹤記憶之間的關係,而不只是記憶本身。
研究生的日常就是這樣:覺得某個方向理論上對,然後花幾個月驗證它在 benchmark 上確實也對,但同時開始想下一個更根本的問題。🧠
作者:陳思維