同一個詞在不同框架裡指不同的東西:Letta 的 archival memory 是可搜尋的長期向量儲存,Mem0 的 long-term memory 是抽取出來的事實條目,OpenClaw 的 long-term memory 是一個 Markdown 檔案。不先固定詞彙,橫向比較就只是在比較各家的行銷用語。 這一篇建立本專欄之後一律遵守的座標系,並在每個維度上標出各框架落在哪裡。

分類本身不是目的。它的用途是在設計新專案時,讓「我需要哪幾種記憶」變成一個可以逐項回答的問題,而不是一個含糊的「要不要加記憶」。


第一個維度:時間尺度

這個維度回答「這份資料活多久、由誰管理」。

Working memory(工作記憶)——當前這一次推理看得到的東西,也就是 context window 裡的內容。它的生命週期是一次 LLM 呼叫。它不是儲存,是一個算出來的結果:每一輪都由某段程式從各處組裝出來。多數框架不把它叫做記憶,但它是所有記憶最終必須抵達的地方——任何記憶如果沒有進到 working memory,它對這一輪推理就等於不存在。

Short-term / session memory(短期/會話記憶)——單一會話或單一 thread 內的狀態,跨輪但不跨 session。LangGraph 的 checkpointer 是這一層最清楚的實作:以 thread_id 為範圍持久化圖的狀態,讓一個中斷的對話可以續接。OpenClaw 的每日日誌(memory/YYYY-MM-DD.md)介於短期與長期之間——它持久化,但被設計成 append-only 的流水帳而非精煉事實。

Long-term memory(長期記憶)——跨 session、跨 thread、可能跨專案存在的東西。這是所有「記憶框架」真正在競爭的層次,也是分歧最大的地方。

關鍵的區分不在時間長度,在管理主體。 working memory 由組裝邏輯管理(開發者寫的程式),short-term 由 runtime 管理(自動持久化),long-term 需要有人決定「什麼值得留下」——而那個決策要交給誰,是整個領域最核心的分歧點。Letta 交給 agent 自己(self-editing)、Mem0 交給一條 LLM 管線、LangMem 讓你選(hot path 或 background)、OpenClaw 與 Hermes 交給一個週期性的自我審視、Claude 的 memory tool 交給模型的工具呼叫。


第二個維度:內容型態

這個維度源自認知心理學,被 LangMem 等框架直接採用為 API 概念,因此值得精確使用。

Semantic memory(語意記憶)——去脈絡化的事實。「使用者是素食者」「這個 repo 用 pnpm 不用 npm」「部署到 Cloudflare Pages」。它的特徵是可以脫離發生的場景獨立成立,因此適合被抽取成條目、去重、更新。Mem0 的整條管線本質上只處理這一型。

Episodic memory(情節記憶)——特定事件及其脈絡。「上週三我們討論過要不要換 ORM,結論是先不換,理由是遷移成本」。它的價值在於保留了「當時為什麼」,因此不能被壓縮成一句事實而不損失資訊。Zep/Graphiti 的 episode 是這一型的明確實作:episode 是不可變的原始事件,抽取出的實體與關係則指回它作為 provenance。

Procedural memory(程序記憶)——怎麼做某件事。它有兩種很不一樣的形態:規則型CLAUDE.md 這種必須遵守的約定,舊專欄的 規則記憶:Agent 的工作憲法 完整處理了這一型)與技能型(Hermes 與 Letta 都會把重複成功的流程沉澱成可重用的 skill)。LangMem 把 procedural memory 定義為「agent 自己的系統指令」,也就是規則型。

這三型需要不同的儲存與召回策略,混在一個表裡是常見的設計錯誤。 語意事實適合向量檢索與去重更新;情節記憶適合時序索引與 provenance 追溯,而且不該被更新(事件發生過就是發生過,改變的是它是否仍然有效);程序記憶適合全量載入或按任務類型精確載入,向量相似度在這裡幾乎沒用——你不會希望「怎麼發版」這條規則因為語意相似度不足而沒被召回。

一個實務上的檢查:如果你的系統把三型塞進同一個 memories 表、用同一條 embedding 檢索,那你在語意記憶上會表現尚可,在情節記憶上會遺失脈絡,在程序記憶上會隨機漏規則。


第三個維度:主體歸屬

User memory(使用者記憶)——關於使用者的:身份、偏好、歷史、目標。它的所有權屬於使用者,因此必須可查看、可修改、可刪除(這既是產品需求也是法規需求)。ChatGPT 的 saved memories 是最直接的例子:條目化、可在設定裡逐條刪除。

Agent memory(代理人記憶)——關於 agent 自己的:它學到的做法、它對某個 codebase 的理解、它累積的技能。它的所有權屬於系統。Hermes 明確把這兩者分開成兩個檔案——USER.md 存對使用者的理解,MEMORY.md 存跨 session 學到的事實;EverOS 則把它稱為 dual-track memory。

這個分離的實務理由有三個,每一個都足以單獨成立

權限與刪除語意不同。 使用者要求刪除自己的資料時,你需要能精確地只刪 user memory;如果 agent 學到的做法與使用者事實混在一起,刪除會連帶破壞系統能力。

信任等級不同。 user memory 來自使用者輸入,是不可信來源;agent memory 來自系統自己的反思,信任度較高但也可能自我強化錯誤。兩者混在一起,記憶投毒的攻擊面會從 user 側直接擴散到 agent 的行為規則——見 實作陷阱

共享範圍不同。 agent memory 常需要跨使用者共享(一個 codebase 的知識對所有使用者都適用),user memory 絕對不能。這一條在多租戶產品裡是硬性的隔離邊界。

多 agent 系統還需要第四個歸屬層:shared memory(共享記憶)——多個 agent 共用的知識。Letta 的 block 共享是目前最明確的實作(同一個 block_id 掛在多個 agent 上)。它引入了併發寫入與一致性問題,而這一塊在整個生態裡幾乎是空白的。


第四個維度:儲存形態

這一維度決定了你能問什麼問題,因此是選型時最不可逆的決定。

檔案(Markdown / 純文字)——OpenClaw、Hermes、Claude 的 memory 目錄。可讀、可 diff、可 Git 化、可被人類直接編輯。代價是沒有結構化查詢能力,規模化之後只能靠額外索引(OpenClaw 用 SQLite + FTS5 + embedding 補上)。

向量——Mem0 的主要後端,也是幾乎所有框架的一部分。適合語意近似召回。代價是無法表達關係,也無法回答「這件事什麼時候變成真的」

知識圖——Zep/Graphiti、Cognee、Mem0 的圖變體。能表達實體間關係,因此能做多跳推理。代價是寫入成本高(每次寫入都要 LLM 抽取實體與關係、比對衝突),而且圖的品質完全取決於抽取品質。

KV / 關聯式——LangGraph 的 Store(namespace + key)、Letta 的 block(block_id)。適合精確定位的取回,適合 procedural 與畫像類記憶。代價是需要事先知道 key。

這四者不是互斥選項,而是幾乎所有成熟方案都會混用。 真正的設計決策是「哪一份資料放哪一層、以哪一層為真源(source of truth)」——這正是 可複用藍圖 要回答的問題。


把四個維度合起來用

一份記憶在這個座標系裡的位置,由四個座標決定,而位置決定了它的技術處理方式

資料範例時間尺度內容型態主體建議儲存
「使用者叫 Tommy」長期語意userKV/向量,可條目化刪除
「這個 repo 用 rtk 前綴指令」長期程序(規則)agent/專案檔案,全量載入
「上週決定不換 ORM,因為遷移成本」長期情節shared圖/時序索引 + provenance
「目前正在改第 3 個檔案」短期情節sessioncheckpointer
「發版流程:先跑測試再 tag」長期程序(技能)agent檔案/skill 定義,按任務載入
「使用者上個月說偏好深色主題,本月改口」長期語意(有時效)user需要雙時間軸——見 Zep/Graphiti

這張表的最後一列,就是新專案設計記憶架構的實際起點。 先列出你的系統要記住的十到二十件具體的事,逐一標上四個座標,你需要的架構會自己浮出來——而且你會發現多數專案不需要六種記憶,只需要兩到三種。


一個必要的釐清:memory 不是 RAG

這兩者在實作上大量重疊(都用向量檢索、都注入 context),但目的不同,因此驗收標準也不同

RAG 檢索的是外部的、既存的、不因對話而改變的知識庫。它的品質問題是「有沒有找到正確的文件」。

Memory 處理的是由互動產生、會隨時間改變、需要被寫入的狀態。它多出三個 RAG 沒有的問題:該不該寫(重要性判斷)、寫進去要不要覆蓋舊的(衝突解決)、什麼時候該失效(時效性)。

這三個問題正是本專欄所有框架差異的來源。 一個只做召回不做寫入決策的系統,不論它自稱什麼,工程上是 RAG。舊專欄的 長上下文、RAG 與記憶的分工 從另一個角度處理了同一件事,值得對照。


相關