Agent 記憶不是一個向量庫:Session、State、Profile 與 Memory Bank

Agent 記憶不是接一個向量庫就結束。把記憶拆成 Session、State、Profile、Memory Bank 四層,才能分清對話暫存、結構化中間結果、跨對話使用者事實,以及可語意檢索的長期經驗——也可治理、可忘記。

Agent 記憶四層架構抽象示意

一講到「給 Agent 加記憶」,很多人第一反應是接一個向量資料庫。向量檢索很有用,但它只是長期記憶裡的一種技術選項,不是記憶系統的全貌。若你把所有東西——當輪對話、結構化中間結果、使用者偏好、跨週的經驗——都塞進同一坨 embedding,系統會在三個地方同時失敗:上下文膨脹、狀態不可追溯、以及「明明說過是素食者,新對話又推薦牛排」。

這篇文章是「Agent 系統工程」系列的第一篇。我想先把記憶拆成四層可實作的概念:Session、State、Profile、Memory Bank。框架可以是 Google ADK 或你自己的 runtime;分層邏輯是通用的。

為什麼「只有聊天紀錄」不夠

聊天機器人把歷史訊息往 context 裡堆,短期看起來很直覺。Agent 一旦開始呼叫工具、改狀態、跨步驟執行,就會暴露三個問題:

  1. 重讀成本:模型不該為了找一個確認碼,重讀十五則訊息。
  2. 邊界模糊:哪些是「這次行程的暫存」,哪些是「這個人長期是素食者」?
  3. 重啟即失憶:行程重開、容器重啟、新開一個對話 ID,若沒有持久層,一切歸零。

所以記憶不該是單一儲存,而是依生命週期與讀取型態分層。

第一層:Session——對話串本身

Session 就是一段連續對話,像手機裡的一個聊天室。同一 Session 內,Agent 應看得到先前輪次;新開 Session 則預設是陌生人——除非你另外接了跨 Session 的層。

常見翻車 Demo:使用者說「幫我規劃兩天里斯本之旅」,代理排好第一天;第二輪「規劃第二天」若開了新 Session,代理會忘掉城市與第一天內容。修好的方式往往不是換更大模型,而是在多輪之間重用同一個 Session ID

Session 裡通常有兩類東西:

元件 比喻 內容
Events 逐字稿 使用者訊息、代理回覆、工具結果、甚至圖片等媒體輸入
State 便利貼 結構化、可快速讀取的中間結果(餐廳名、確認碼、已選項目)

Events 提供完整上下文;State 提供高效索引。你不會希望每次推理都從長篇對話裡「考古」一個欄位。

寫 State 的硬規則

很多框架會給 tool 一個 context。正確做法是透過框架提供的 API 寫入 state,讓變更進入事件日誌,之後才能被持久化與還原。

反模式:在工具函式裡直接改 session 物件。變更可能當下看得到,卻沒進 log,重啟或換儲存引擎後就消失,除錯時像鬧鬼。

短期 Session 記憶很像 RAM:快、暫存、預設不保證跨行程存活。

第二層:可插拔的 Session 儲存——讓對話活過重啟

「同一對話不要斷」與「同一個人換聊天室還認得你」是兩件事。先解決前者:把 In-memory Session Service 換成寫進磁碟或資料庫的 Session Service。

儲存型態 特性 典型用途
記憶體內 快、重啟清空 本機實驗
資料庫/檔案 自管、重啟可載入完整歷史與最新 state 要可靠的產品雛形
雲端托管 Session 與企業記憶/合規能力綁定 正式環境

工程上理想狀態是:代理邏輯不變,只抽換底下的 Session Service。用 get_or_create_session(session_id):有就接續,沒有就新建。使用者帶著同一個 Session ID 回來時,歷史與 state 一併載入,對話從斷點繼續。

這層回答的問題是:「這個對話關掉重開,還在不在?」

第三層:Profile——跨對話的結構化事實

新的 Session ID、下週再來一句「嗨,幫我規劃行程」——此時沒有舊對話可讀。你需要的不是把所有舊 chat 塞進 prompt,而是一個精簡、可查詢的使用者檔案

  • 飲食:素食
  • 偏好:博物館多於夜店
  • 交通:寧可公共交通

實作上可以很樸素:一張表,(user_id, key, value)。代理配兩個工具:

  1. Recall:對話開始先讀該使用者全部(或相關)偏好。
  2. Save:使用者說出新事實時,結束前寫回。

再用 instruction 把行為寫死成腳本:先 recall → 規劃時使用 → 呈現方案 → 問是否有新偏好要記 → 有則 save。

Demo 很有說服力:第一輪沒有偏好,規劃完主動問;使用者說「請記住我是素食者」→ save;模擬重啟、全新 Session ID 再來,代理一開場 recall 到 dietary=vegetarian,直接個人化。

這層回答的問題是:「這個人換了一個對話,我是否仍知道不該忘記的事實?」

Profile 適合穩定、低基數、高確定性的鍵值;不適合把整段旅遊日記當 value 亂塞。

第四層:Memory Bank——跨時間、跨媒體的語意封存

Session 管活躍對話;Profile 管結構化真相;當你需要的是:

  • 三週前那張建築照片裡的細節
  • 某次直播音訊裡提過的約束
  • 「兩輪車」能搜到腳踏車相關筆記(語意,不只關鍵字)

你需要的是 Memory Service/Memory Bank 這類能力:從對話與媒體中抽取事實、做 embedding、語意檢索,並在新對話用預載工具把相關記憶帶進來。

Session Service Memory Service
角色 活躍對話的現場 長期封存的檔案櫃
讀法 依 Session ID 載入時間序 依查詢語意/主題檢索
失敗模式 錯 ID、未持久化 檢索噪音、抽事實錯誤、隱私邊界不清

簡易 in-memory memory 只適合本機測;生產環境通常要雲端儲存、權限、以及比關鍵字更好的檢索。重點是心智模型:這不是「再貼一張 raw chat 表」,而是「處理內容 → 抽出可重用事實 → 可搜尋」的服務。

四層怎麼一起用(避免疊床架屋)

一個可執行的預設策略:

當輪推理
  ← Session Events(必要上下文)
  ← State(結構化中間結果,優先於重讀長對話)

開場 / 需要個人化時
  ← Profile.recall(user_id)

需要「以前某次經驗」或媒體細節時
  ← Memory Bank 語意檢索(可預載進 system/tool 結果)

對話中出現穩定新事實
  → Profile.save

步驟完成、工具結果
  → 經 context 寫入 State,並進 Event log

對話結束或檢查點
  → Session 持久化;可選擇將摘要/事實匯入 Memory Bank

反模式速查

  • 只用向量庫裝當輪 state → 延遲高、一致性差
  • 只靠超長 context 當長期記憶 → 貴、易丟中間、重啟即死
  • Profile 與 Memory 不分 → 素食者這種硬約束被語意檢索「差不多」掉
  • Tool 偷改 session → 幽靈 bug

與治理的關係(為什麼這對你很重要)

在多代理或人機協作裡,記憶分層也是責任分層

  • Session/State:這次任務的可追溯過程
  • Profile:對使用者的承諾式事實(改了要可審計)
  • Memory Bank:可檢索的經驗,必須可過期、可刪、可標敏感級

「Agent 有記憶」若沒有分層,最後通常變成不可治理的黑盒子:說不清記得什麼、從哪來、能否忘記。

結語

向量庫可以是 Memory Bank 的引擎之一,但它不是記憶架構的全部。先問四個問題:

  1. 這是這次對話還需要的嗎?→ Session/Events
  2. 這是步驟間要快速讀的結構化值嗎?→ State
  3. 這是換對話仍應成立的使用者事實嗎?→ Profile
  4. 這是跨時間、需語意找回的經驗嗎?→ Memory Bank

把這四層劃清,Agent 才從「會聊天的模型」變成「可連續服務的系統」。

系列下一篇,我們會談另一個正交維度:當任務不是一輪對話,而是跨天、等人、等 webhook 的長流程——如何休眠、打檢查點、以及為什麼不能讓代理自己打滿分。

延伸閱讀


我是江中喬,專注於 AI Agent Architecture、Memory Governance 與 Cognitive Diversity,持續研究如何打造能夠長期協作、可信任且可治理的 AI 系統。