最近社群討論 Agent Memory 的聲量明顯變大,光 GitHub 上至少就有 12 個開源專案在解同一道題。框架越開越複雜: 向量資料庫、知識圖譜、混合檢索、時序推理… 看著看著,新手很容易以為「做記憶系統就是上向量資料庫」。

但小編這幾週讀了不少資料後,越來越覺得: 大多數做 agent 的人,其實根本不需要向量檢索

理由很簡單,先看一個事實: 目前最成熟、用戶最多的幾個記憶產品 — ChatGPT 的記憶功能、Claude Code 的記憶設計、Claude API 的 Memory 功能 — 都沒有用向量檢索。

先建立框架: 記憶到底是什麼?

在進入產品案例前,先建立一個共通的整體框架。這邊融合三篇值得收藏的綜述: Leonie Monigatti 的《Making Sense of Memory in AI Agents》、LangChain 的《Memory for Agents》,以及 Philipp Schmid 的《Memory in Agents, Make LLMs remember》,把領域裡反覆出現的核心觀念整理一下:

🔹 記憶不是 LLM 內建的能力: LLM 本身是無狀態的,每次互動都是從零開始。所謂「記憶」是工程上刻意設計進去的能力。

🔹 上下文內 vs 上下文外: 最高層次的分類就兩種 — 放在 LLM 上下文視窗裡的 (短期)、放在外部儲存的 (長期)。所有花俏的架構都是這兩類的變形。

🔹 CoALA 框架的四種記憶: Working、Semantic、Episodic、Procedural — 借用人類認知科學的分類。LangChain 也採用了類似的三類分法 (拿掉 Working): Procedural 對應 LLM 權重、agent 程式碼、system prompt,實際運作中很少更新; Semantic 是長期累積的事實知識,做個人化用 (用戶偏好、專案規範); Episodic 是過去行為紀錄,常以少樣本範例引導 agent 行為一致。但 Letta 的 Sarah Wooders 也提醒過,LLM 是「token 進 token 出」的函數,不是大腦,過度擬人化的類比未必合適。Letta 自己就改用 Message Buffer、Core Memory、Recall Memory、Archival Memory 這套架構導向的分法。不過小編覺得這套分類比較適合通用記憶框架 — 對專門場景的 agent 來說,硬套這四種類型反而會增加不必要的複雜度。當作整體框架看看就好,實作時還是要從你的場景出發

🔹 四個核心操作: ADD、UPDATE、DELETE、NOOP — 不只是「存」,更包含「該不該存、要不要更新、什麼時候該忘」。

🔹 Hot path vs background: 記憶更新的兩種時機。Hot path (顯式) 是 agent 在回應前自己決定要記什麼,記憶即時可用,但會增加延遲,也讓記憶邏輯跟 agent 邏輯耦合在一起。Background (隱式) 是背景任務在對話中或結束後跑,沒有延遲懲罰,但記憶不是即時更新的,可能短時間內過時。

🔹 記憶設計是場景導向的: LangChain 那篇的核心訊息很關鍵 — 不要把記憶當成一個單一龐大的概念,要根據你的使用體驗跟業務需求,選擇你的應用真的需要哪幾種記憶、用什麼時機更新。這也就是為什麼通用框架不一定適合你 — 因為它們得對所有場景都妥協。

🔹 上下文工程才是更大的框架: Philipp Schmid 那篇把記憶放回更大的脈絡 — 上下文工程 (context engineering),也就是「在有限的上下文視窗裡,用對的格式、在對的時機,放對的資訊跟工具」。記憶是這個工程裡最重要的資訊來源之一,但不是全部。他歸納了三個記憶的核心挑戰: 相關性問題 (檢索到不相關或過時記憶會降低任務表現)、記憶膨脹 (什麼都記等於什麼都沒記)、需要遺忘 (資訊有時效性,淘汰機制很難設計)。這三個問題的回饋迴路很長,有缺陷的策略要過很久才看得出來,也讓記憶系統很難迭代。

把這幾點放在腦子裡,再看具體產品的設計選擇就會清楚很多。Leonie 那篇最後點出: 真正的核心挑戰不是檢索,而是「如何在 LLM 的上下文視窗跟外部儲存之間流動資訊」,並決定要用 hot path 還是 background,同時克服延遲跟「如何遺忘過時資料」這兩個關鍵難題。

ChatGPT 的記憶系統,沒有 RAG

最近有人逆向工程了 ChatGPT 的記憶系統,結果出乎意料的簡單。它的上下文結構是這樣四層:

  1. Session Metadata (工作階段環境資訊): 開場注入一次 (裝置、瀏覽器、訂閱級別、活躍度),session 結束就丟掉
  2. User Memory (長期用戶事實): 像姓名、職業、偏好,每次對話都注入
  3. Recent Conversations Summary (最近對話摘要): 輕量摘要,只是「時間戳 + 標題 + 用戶訊息片段」
  4. Current Session Messages (當前對話): 滑動視窗

然後就是用戶的最新訊息。

整個系統沒有向量資料庫、沒有 RAG、沒有跨歷史對話的向量檢索。逆向作者實測,他的 ChatGPT 大概記得 33 條長期事實,加上 15 個最近對話的摘要,就這樣。新訊息進來不會去搜尋過去訊息,純粹靠這幾個區塊的固定注入。

編按: 「不是所有東西都需要叫做記憶」這句話蠻一針見血的。環境資訊在運行時適應裝置情境,明確事實跨 session 持久化,對話摘要提供連續感,當前 session 維持連貫性。各司其職,不需要花俏的檢索。

Claude Code 的記憶: 檔案系統 + 索引

Claude Code 的設計也是純檔案系統。社群整理過原始碼的細節 (這篇這篇),幾個設計亮點蠻啟發的:

🔹 記憶是索引,不是儲存: MEMORY.md 永遠載入,但它只是指標 (每行約 150 字元),實際知識存在外部檔案,需要時才讀取。

🔹 三層設計,注重頻寬: 索引永遠載入、主題檔案按需載入、原始對話紀錄從不讀只用 grep 搜尋。

🔹 嚴格的寫入紀律: 先寫到檔案,再更新索引 — 絕不把內容直接傾倒進索引,避免資訊熵增加跟脈絡污染。

🔹 背景做「記憶重寫」(autoDream): 合併、去重、移除矛盾、把模糊改成絕對、積極修剪。記憶是被持續編輯的,不是只追加。

🔹 可推導的東西不存: 程式碼結構、除錯 log、PR 歷史一律不記,因為這些都可以從原始碼重新算出來。

這套設計的精神其實很簡單: 把記憶當成索引,不要當成知識本身。「過期」是核心概念 — 如果記憶跟現實不一致,記憶就是錯的。

Claude API 的 Memory Tool,也是檔案系統

Anthropic 也在類似的方向。Claude API 的 Memory Tool 就是一個檔案系統,agent 透過 view、create、str_replace、insert、delete、rename 這幾個指令直接讀寫 /memories 目錄。這是 client-side 工具 — Claude 發出工具呼叫,實際儲存由你的應用實作 (檔案、資料庫、雲端都可以)。沒有向量檢索、沒有相似度搜尋,agent 自己決定什麼時候去翻檔案。

Anthropic Cookbook 給的入門實作三種範本,都跟向量沒關係:

  • SimpleMemory: 一張可讀寫的便條紙
  • CompactifyMemory: 對話太長時讓 AI 自己決定壓縮
  • FileBasedMemory: 檔案系統階層式組織

另外 Anthropic 在 Managed Agents 上還有一個獨立的 Memory Store 功能 (跟上面的 Memory Tool 不一樣,不要搞混)。Memory Store 是 server-side 託管的,由 Anthropic 自動掛載成 session container 裡的目錄 (/mnt/memory/),agent 用標準檔案工具操作,每次寫入都會產生不可變的版本紀錄供稽核。它建議「把記憶切成多個小型聚焦檔案,而不是少數大檔」,每筆記憶上限 100KB,版本歷史自動保留 30 天。

不管是 client-side 的 Memory Tool 還是 server-side 的 Memory Store,本質都一樣 — 仍然是檔案、仍然是用路徑存取、仍然沒有向量。

OpenAI 自己的 cookbook 也選狀態式記憶

更有意思的是,OpenAI 自己最新的 Context Engineering for Personalization cookbook,做了一個旅遊管家 agent 的範例,明確選擇狀態式記憶 (state-based) 而不是檢索式記憶 (retrieval-based)。原文這段論點蠻有料的:

旅遊管家需要在機票、飯店、保險等決策上一致地套用一份「當下、連貫」的用戶狀態 (會員計畫、座位偏好、預算、簽證限制)。檢索式記憶把過去互動當成鬆散文件,對措辭很脆弱、容易漏掉覆寫、無法調解衝突。狀態式記憶把用戶知識編碼成有明確優先順序 (全域 vs session) 的結構化欄位,支援信念更新而非事實累積。

整個實作就是一個 TravelState 的資料類別,分四個欄位:

  • profile (個人檔案): 結構化的用戶資料 (客戶 ID、姓名、會員等級、座位偏好、簽證、保險等),通常從你內部的 CRM 或資料庫帶入
  • global_memory.notes (全域記憶筆記): 長期累積的偏好,每條一句話,例如「使用者通常偏好走道座位」、「短程旅行偏好不託運行李」、「偏好市中心可步行的住宿區域」
  • session_memory.notes (當次工作階段筆記): 暫存筆記,例如「這趟旅行想睡覺所以要靠窗」這種一次性偏好
  • trip_history (旅行歷史): 最近幾次旅行的精簡紀錄,幫 agent 抓最近的行為模式

整個生命週期分四個觸發點:

🔹 Session 開始時注入: hook 把 profile 渲染成 YAML 開頭區塊、把 global_memory.notes 渲染成 Markdown 條列,包進 <user_profile><memories> 標籤後接到 system prompt。優先順序規則也用一個 <memory_policy> 區塊寫清楚: 用戶當前訊息 > session 記憶 > 全域記憶,避免舊記憶誤導當下決策。

🔹 對話進行中寫入 (hot path): agent 透過一個 save_memory_note(text, keywords) 工具主動寫入 session 記憶。工具的說明文字寫得很細 — 只能寫「持久、可付諸行動、明確」的內容,速食寒暄、推測、敏感個資一律不收。例如使用者說「記得我是素食者」就會觸發寫入。

🔹 上下文被裁掉時補注: 對話太長被裁切時,hook 會在下一輪自動把 session 記憶重新注入 system prompt,避免重要短期上下文隨對話歷史消失。

🔹 Session 結束時整合 (background): 用一個 LLM 呼叫把 session 筆記合併進全域記憶,明確處理三件事 — 去重 (語意上相同的合併成一條)、衝突調解 (有矛盾時用 last_update_date 最新的勝出)、過濾一次性筆記 (含「this time」、「this trip」字眼的就丟掉)。

舉個具體例子: 用戶說「我是素食者」會寫入 session 記憶,session 結束時被推上全域記憶; 但「這次想要靠窗座位睡覺」也寫進 session,整合時就被識別為一次性而丟棄。

整個系統從頭到尾就是 JSON + Python list,連 SQLite 都沒用。記住的東西結構化、精煉、可控,每一條都有明確的生命週期跟優先順序。

Mastra 的 Observational Memory

最近 Mastra 推出的 Observational Memory,在 LongMemEval 這個目前最難的記憶基準測試上拿到 94.87% (GPT-5-mini),是目前可驗證的最高分。它的開頭一句話是這樣寫的:

沒有向量資料庫、沒有圖資料庫、沒有壓縮機制。只有兩個背景 agent 跟一個簡單的想法: 觀察重要的東西,讓其餘的褪去。

它的做法叫「觀察者 + 反思者 (observer + reflector)」: 一個輕量模型 (例如 Gemini Flash) 在背景把對話壓縮成帶時間戳的觀察條目 (附 🔴 🟡 🟢 ✅ 優先級 emoji),達到 token 閾值後反思者再做合併、去重、移除不相關。整個架構就是一份結構化的觀察列表注入到 system prompt,搭配 prompt caching。

對話 30k tokens 觸發一次觀察,最多壓到 5-40 倍。這就是 SOTA 的做法,沒有任何向量檢索

真正難的不是檢索,是「治理」

到這裡可以下個小結了: 把心力放在向量檢索其實是搞錯重點。中文圈最近有篇《浅谈 Agent Memory》講得很好,記憶系統真正難的是「治理」,不是存與取:

  • 寫入: 該記不該記? 短暫寒暄、瞬時狀態、低價值的雜訊不能無差別進入記憶。寫髒比讀錯更致命
  • 整理: 合併去重、衝突調解、版本替換、衰減歸檔。一個只累積不整理的系統,早晚被自己的過去壓垮
  • 讀取: 優先順序規則 — 用戶當下訊息 > session 記憶 > 全域記憶,遇到衝突要會問澄清問題
  • 遺忘: 不會忘的系統會被舊版本困住,用失效的理解解釋現在

這幾件事,沒有一件需要向量檢索。它們是 prompt 工程、是資料結構設計、是生命週期編排,是工程治理問題。

LangChain 的 Agent Builder 團隊也分享過 (小編之前整理過這篇),他們有一個人全職在做記憶相關的 prompt 設計。幾乎所有記憶行為的問題 — agent 該記的沒記、不該記的亂記、寫到錯的檔案 — 都是改 prompt 解決的,不是改檢索演算法。

Memory as Reasoning: 一個非常有啟發的觀點

Plastic Labs 那篇《Memory as Reasoning》小編讀完覺得非常有啟發。它的核心主張很簡單但有力: 不要把記憶看成「資料儲存問題」,要看成「推理與脈絡建模問題」

傳統的 agent 記憶太常停在「抽取事實 → 向量化 → 儲存 → 檢索」這個流程裡。但作者指出,真正有價值的不是把事實存下來,而是根據對話資料推論出使用者的偏好、意圖、穩定模式、矛盾與更新,並在推論時重新組裝成有用的上下文。這個轉換很關鍵。下面拆成幾個對開發 agent 直接有用的啟發:

🔹 不要只存「事實」,要存「可追溯的推論」

不是只存「使用者喜歡 Ruby on Rails」,而是存成: 「使用者在多次對話中提到 Rails、顧問案、教學與開發,因此可以推論他在評估技術方案時,會重視成熟度、可維護性、開發效率與商業落地,而不只是技術新穎性。」這種記憶比單一事實有用得多,因為它能幫 agent 在新任務中做判斷。

但它也更危險,因為是推論不是事實。所以記憶結構應該明確區分這幾類:

  • 明確事實: 使用者說過的、可直接引用的資訊
  • 推論觀察: 根據多次互動歸納出的傾向
  • 工作偏好: 回覆風格、語言、格式、深度
  • 暫時脈絡: 只適用於當前專案或近期任務
  • 待驗證假設: agent 猜測但信心不足,需要未來確認

這比單純「長期記憶 + 向量搜尋」更適合做高品質 agent。

🔹 寫入記憶不該只是抽取,而應該是推理步驟

很多記憶實作會在對話結束後問模型: 「這段對話有什麼值得記住?」這容易產生瑣碎記憶。更好的 prompt 應該要求模型判斷「未來是否會改變回答品質」。例如:

請只記錄未來三個月以上仍可能影響回答的資訊。不要記錄一次性任務、短期狀態、情緒性抱怨、過度私人資訊。若是推論,請標註證據、信心度、適用範圍、可能失效條件。

這樣記憶寫入的輸出就不是筆記,而是決策紀錄

🔹 召回記憶不該只是相似度搜尋,而是「針對當前任務選擇可用的推論」

向量搜尋通常會找語意相近內容,但不一定找得到「對現在決策最有用」的記憶。舉例: 使用者問「幫我改一篇提案」,最有用的記憶可能不是過去的提案內容,而是「使用者偏好中性、精煉、顧問式語氣」、「不喜歡太多條列」、「常需要台灣商業語境」。這些不一定跟查詢字面相似。

所以召回可以改成兩階段: 先取回候選記憶,再讓模型判斷「哪些記憶會改變本次回答的措辭、判斷、優先順序或風險提醒?」這比單純「找相關記憶」準很多。

🔹 意外與矛盾處理 — 當新行為跟舊記憶不一致時

文章引用認知科學說人類會根據預測誤差更新內在模型。對 agent 也一樣 — 當使用者新行為跟舊記憶衝突時,不應該直接覆蓋,也不應該並存不處理,而是要觸發記憶調解

例如舊記憶是「使用者偏好簡短回答」,但近期多次要求「深入研究」、「請比較」、「請再次確認」。這時記憶系統應該產生更新: 「使用者一般偏好精煉,但在技術研究、採購比較、法規與時事議題上偏好查證與深入分析。」這種帶條件的修正比單純覆蓋更接近真正的個人化。

🔹 把 prompt 拆成「任務 prompt」+「身份/脈絡建模 prompt」

一般 prompt 只處理當下任務,但更高階的 agent 應該在任務前先建構一段工作上下文: 「根據目前任務、使用者長期偏好、近期專案脈絡,產生本次回答策略。」這有點像 agent 先問自己: 我現在是在幫誰? 他通常重視什麼? 這次任務的成功標準是什麼? 哪些既有記憶應該暫時忽略?

這會讓 prompt 不只是指令,而是上下文組裝

🔹 一個五層架構參考

順著上面的邏輯,比較實用的記憶架構大概長這樣:

  1. 原始事件: 保留原始對話片段或摘要
  2. 原子記憶: 抽出事實與偏好
  3. 推理觀察: 把多個原子記憶推論成可操作的使用者模型
  4. 任務當下的上下文: 每次回答前動態選擇目前真正需要的脈絡
  5. 更新迴圈: 回答後根據新訊息更新、合併、降權或刪除

這也呼應了之前小編寫過的 harness engineering — 記憶不只是儲存元件,而是 agent 迴圈裡的推理框架,會影響規劃、工具選擇、prompt 風格、評估標準跟自我改進。記憶、技能、評估、線上回饋其實是連在一起的。

🔹 一個需要保留的懷疑

這篇文章把「LLM 可以比人類更好地做身份推理」講得很強,但實務上 LLM 對人的推論很容易過度詮釋。尤其是人格傾向、心理狀態這類資訊,沒有嚴格的結構、信心度、證據跟使用者可控的修正機制,很容易變成一套看似聰明但其實冒犯或錯誤的用戶側寫系統。

所以小編會把它轉成比較保守的工程原則: 不要讓 agent 自以為「懂這個人」。要讓 agent 維護一組「可驗證、可撤銷、可降權的工作假設」


整篇文章最值得吸收的精神是這句話: 記憶不是「把過去塞回上下文」,而是「從過去推理出這次該怎麼做」

這會直接改變 agent 的設計。你不只是做一個記憶讀寫工具,而是做一個會形成、使用、修正工作模型的 agent 框架。回到本文主題: 這跟向量檢索有沒有關? 完全沒關。它需要的是推理、結構、生命週期,跟向量資料庫一點關係都沒有。

那什麼時候需要向量檢索?

當然不是說向量完全沒用,而是要看你在哪一層。小編看下來大概是這幾種情況:

  1. 真的要做通用記憶框架: mem0、Letta 那種要服務不同場景的基礎設施,需要模糊匹配
  2. 跨用戶大規模知識共享: 企業多租戶 agent,每個租戶幾百萬條記憶要管,這時 Oracle 那篇《Agent Memory: Why Your AI Has Amnesia and How to Fix It》講的「向量 + 圖譜 + 關聯式」才有意義 — 他們把 agent memory 當成資料庫問題在解,強調 row-level security、多租戶隔離、GDPR 與 EU AI Act 10 年稽核軌跡這些企業需求,並主張用收斂式資料庫 (一個引擎同時跑向量、圖、關聯式) 取代「Pinecone + Neo4j + Postgres 三套系統拼裝」的常見作法
  3. 記憶量真的爆掉了: 某些長期客服或個人助理場景,記憶累積到萬條等級,這時搭配向量做候選召回是合理的

但這些都是少數場景。大多數人在做的是專用 agent — 一個寫週報的、一個做 PR review 的、一個處理客服單的。這類 agent 的記憶量永遠不會大到需要向量檢索。

小結

ChatGPT 的設計、Claude Code 的 MEMORY.md 架構、Claude API 的 Memory Tool、OpenAI 自己的 cookbook、Mastra 拿到 SOTA 的 Observational Memory,全都不用向量檢索。這不是巧合,是因為一旦你的場景明確,「結構化的小型知識 + 檔案系統 + 適當的注入策略」就足夠了。

唯一要補充一個觀點: 「不需要向量」不等於「不需要設計」。真正要花心力的,是想清楚這幾件事:

  • 我的 agent 要記住什麼? 偏好、規則、人物、流程、踩過的坑?
  • 什麼算「持久」值得長期保留,什麼是「一次性」的?
  • 怎麼合併、整理、讓舊記憶退場? (這比寫入更難)
  • 注入策略是什麼? 優先順序規則怎麼設?
  • 更新是 hot path 還是 background?

這些都不是框架能幫你決定的,得從場景出發自己想。記憶不是把過去保存下來,而是治理過去如何進入現在,並且不毀掉未來。真正難的從來不是容量,而是判斷: 留下什麼、刪掉什麼、相信什麼,又允許什麼繼續影響現在