AI Agent 為何每次都要重學?從 Hindsight 看長期記憶怎麼設計
對話歷史與 RAG 各有擅長的問題,卻不一定能讓 Agent 跨工作階段整理偏好、事件與新證據。本文以 Hindsight 的 retain、recall、reflect 流程為例,說明長期記憶的設計方式、上手步驟與部署前必須面對的治理成本。
很多 AI Agent 看起來像是「有記憶」,實際上只是把最近幾輪對話塞回 prompt,或在外部文件庫做一次向量搜尋。前者會隨對話變長而增加上下文成本,後者能找到相似文件,卻不一定知道使用者的偏好如何隨時間改變、某個決定是誰提出的、舊資訊是否已被新證據修正。當代理每次開新工作階段都得重新問一遍「你偏好哪種格式」,問題就不只是缺少更多文字,而是缺少一套可以累積、整理、檢索並修正的狀態。
我看 Hindsight 的角度,不是「再接一個向量資料庫」,而是把代理記憶視為一個獨立的資料與推理層。它把輸入轉為可檢索的記憶單元,之後再用不同路徑找回相關資訊,必要時進一步整理成較穩定的觀察或心智模型。這個方向值得看,但也要記住:記憶系統不會自動讓代理變得正確;它只是讓「過去發生過什麼」能以更有結構的方式參與下一次回答。
先分清楚:對話紀錄、RAG 與代理記憶
對話紀錄保存的是原始互動。它完整,卻可能冗長、重複,也不會自動把「我不喜歡表格」整理成日後可直接使用的偏好。把整段歷史反覆塞進上下文,簡單易做,卻很難控制成本、更新與權限;只截取最近幾輪,又容易漏掉較早但仍重要的資訊。
RAG 通常從文件集合中找出與查詢相近的片段,再交給語言模型生成答案。它非常適合產品手冊、政策文件、研究報告等相對穩定的知識來源。若有人問「公司差旅規則是什麼」,找出最新政策原文比讓模型憑印象作答可靠得多。可是個人偏好、跨多次工作形成的經驗、事件時間順序與關係推理,不一定能靠單次相似度搜尋完整處理。這不是 RAG 做錯了,而是它原本解決的問題不同。
代理記憶關心的是另一件事:系統如何記下與某個使用者、代理或專案有關的事實與經驗,如何在日後依查詢找回,如何在新證據出現後調整舊有理解。理想情況下,RAG 負責「從可信文件找答案」,記憶層負責「累積這個代理與工作脈絡的歷史」。兩者可以一起使用,不必把其中一方說成另一方的替代品。
Hindsight 的核心:retain、recall、reflect
官方文件用三個操作描述主要工作流程:retain、recall 與 reflect。我會把它們理解成「寫入、找回、推論」,但三者不是單純的資料庫 CRUD 別名。
retain:把原始輸入整理成可用記憶
呼叫 retain 時,應用程式把一段內容送入指定的 memory bank。官方文件指出,系統會透過語言模型擷取重要事實、時間資訊、實體及關係,再進行正規化,供後續搜尋使用。它區分世界事實與代理自身經驗,也會把多次輸入逐步整合成 observations。換句話說,送進去的內容不只是被原封不動地保存;系統會嘗試從中形成更有用的索引與摘要。
這帶來方便,也帶來新的風險。抽取結果可能漏掉否定、誤認時間、把不確定的說法寫成事實,或因來源內容本身錯誤而留下錯誤記憶。若應用場景要求可稽核,不能只看「有寫進去」;還要確認來源、時間、證據與修正流程是否留得住。Hindsight 文件提到 observations 會保留支持證據並在新證據到來時修訂,但團隊仍應以自己的資料和查詢方式測試這種行為,而不是把產品說明當成正確性保證。
recall:同時用多種線索找回資訊
recall 是面向查詢的檢索操作。官方 retrieval 文件列出四種並行策略:語意搜尋、關鍵字搜尋、實體關係圖與時間搜尋;結果再融合、重新排序,並依 token 預算裁切。這種設計的價值在於,使用者的問題可能靠不同線索回答:精確人名需要關鍵字,改寫過的描述需要語意,相隔數個事件的關係需要實體連結,「去年春天」則需要時間範圍。
我認為最實用的設計判斷,是不要把「找到相似段落」誤認為「已理解使用者」。如果產品需求只需從十份靜態 FAQ 找出段落,傳統檢索可能更簡單;若問題常跨越人、事件與時間,才有理由評估多路檢索和持續整理是否真的改善答案。最後仍要記錄每次回答用了哪些記憶、哪個 bank、哪個時間範圍,才方便追查「為什麼代理會這樣回答」。
reflect:針對脈絡做較深入的推理
reflect 不只是把搜尋結果原樣交回。官方文件把它描述成一個 agentic loop:模型會在 bank 設定的任務與 disposition 條件下,逐步查找記憶、補足證據,再組成回應。這比較適合需要把多條線索整理成判斷的問題,例如「這個專案最近有哪些風險」,而不是只問「使用者上次選了哪個版本」。
這裡也要把「模型推論」和「記憶內容」分開驗證。更長的推理流程代表可能增加延遲與模型成本,也可能因資料不完整而產生貌似連貫、實際上站不住腳的結論。產品團隊應設定停止條件、來源引用與人工覆核門檻;對高風險回答,應回傳支持該回答的記憶證據,而不是只給一段流暢文字。
記憶單元、observations 與 mental models
Hindsight 文件把記憶區分為 world facts、experiences、observations 與 mental models。可以把它們想成不同成熟度的資訊:世界事實描述外部狀態,experiences 描述代理或使用者曾經做過什麼,observations 是從多筆記憶整理出的證據型觀察,而 mental model 則是針對一個長期問題維護的答案,例如「這個團隊目前如何分工」。
這種分層的好處,是避免把所有內容都當成同等可信、同等新鮮的向量片段。官方 mental models 文件說明,團隊可以先定義值得長期維護的問題,系統在記憶累積後更新答案,應用程式讀取時不必每次都重新跑完整推理。這類摘要更接近「經過整理的工作狀態」,但也因此要管理它的更新頻率、適用範圍與失效條件。若模型只在某些資料變更後更新,舊答案仍可能被誤當成現況;因此我會把「最後更新時間」與來源一起呈現給下游應用。
Memory bank 則是隔離記憶的基本單位。官方文件將其描述為使用者、代理或專案各自的記憶儲存空間。實務上,bank 的邊界應由產品的租戶與授權模型決定:不要把所有人的記憶都寫進同一個 bank,再期待 prompt 替你隔離資料。正式上線前,應測試跨 bank 查詢、刪除、匯出與權限變更,並確認備份和日誌也遵循相同的資料治理規則。
如何開始:先跑通本機,再接一筆記憶
最適合的第一步不是把整個客服或 coding agent 接上去,而是建立一個沒有真實個資的測試 bank,驗證「寫入一件事、稍後用不同問法找回、查看結果是否符合預期」。官方 README 提供 Docker、Python、Node.js 與 Go 等入口;以下以 Docker 啟動 API,再用 Python client 做最小驗證。前置條件是可用的 Docker 環境,以及 Hindsight 支援的 LLM provider 設定。若使用外部模型,將必要憑證安全地注入環境,不要硬編碼在程式碼或 shell history。
# Configure the provider and key through environment variables or a secret manager first.
export HINDSIGHT_API_LLM_PROVIDER=openai
# Inject the real key securely. Do not put it in source control or shell history.
docker run -d --name hindsight \
-p 8888:8888 -p 9999:9999 \
-e HINDSIGHT_API_LLM_PROVIDER \
-e HINDSIGHT_API_LLM_API_KEY \
-v hindsight-data:/home/hindsight/.pg0 \
ghcr.io/vectorize-io/hindsight:latest這個官方 quick start 使用本機容器與持久化 volume;API 預設在 http://localhost:8888,控制介面在 http://localhost:9999。請先確認容器已啟動,並能開啟控制介面。正式環境不要直接把示範設定當成部署設計:安裝文件說明 embedded pg0 主要適合開發,production 應採用外部 PostgreSQL 與支援的向量擴充;同時還要評估備份、網路存取、工作佇列和模型供應商的資料處理條款。容器的 latest 標籤也不適合重現性要求高的部署,正式環境應鎖定經測試的版本或 digest。
接著建立 Python 專案並加入官方 client:
uv init hindsight-demo
cd hindsight-demo
uv add hindsight-client建立 main.py,先寫入一條不含真實個資的測試事實,再用不同措辭查詢:
from hindsight_client import Hindsight
client = Hindsight(base_url="http://localhost:8888")
bank_id = "demo-project"
client.retain(
bank_id=bank_id,
content="The demo team prefers short weekly status reports.",
context="project preference",
)
matches = client.recall(
bank_id=bank_id,
query="How should the weekly update be written?",
)
print(matches)
reflection = client.reflect(
bank_id=bank_id,
query="What working preferences should the demo assistant keep in mind?",
)
print(reflection)可用 uv run python main.py 執行。驗證不應只看 HTTP 成功;要看 recall 是否能從改寫後的問題找到剛寫入的內容,並確認 reflect 回答沒有把範例偏好擴大成不存在的規則。若連線失敗,先檢查容器狀態、API 埠號與 provider 設定;若檢索不到,確認 bank_id 一致、retain 已完成,並查看 server log 是否有抽取或模型呼叫錯誤。遇到模型認證問題時,到官方 installation 與 provider 文件核對環境變數名稱,不要把金鑰貼到 issue 或除錯訊息。
之後再按需求增加正式整合:若想少改既有程式,可評估 LiteLLM wrapper;若必須控制何時寫入、如何附上 metadata,則直接用 SDK 或 REST API。Coding agent 整合也可讓每個 repo 建立自己的 bank,但應先限定會被保留的內容,避免把臨時提示、測試資料或不可信網頁當成長期事實。MCP endpoint 是另一個整合入口,並不等於自動完成授權設計;仍要由呼叫端管理誰能讀取哪個 bank。
上線前先回答的四個問題
第一,什麼內容值得記? 把使用者偏好、專案決策、敏感資料與一次性對話分開。預設不要把完整 prompt、原始文件或工具回傳都寫入長期記憶;先定義 allowlist、保存期限與刪除流程。
第二,記憶錯了怎麼辦? 設計檢視、修正、撤回與重建路徑,確認舊 observation 或 mental model 不會在來源變更後繼續被引用。測試否定句、相似人名、時間修正與互相矛盾的資訊。
第三,誰可以看? 使用者隔離、租戶隔離、bank 權限、備份和觀測資料都要納入威脅模型。文件提到的 Memory Defense 是每個 bank 可選擇啟用的敏感資料掃描,而且只影響啟用後的新 retain;它是減少特定格式秘密進入記憶的保護層,不是完整 DLP,也不是「可以放心丟入任何資料」的理由。仍要先在應用程式入口做資料最小化和權限控制。
第四,成本值得嗎? 衡量的不只是向量搜尋費用,還包括 retain 的抽取、背景整理、reflect 推論、資料庫維運、備份與除錯。建立一組代表性問題集,與「只用最近對話」、「一般 RAG」和人工維護摘要比較,觀察答案可追溯性、錯誤率、延遲與總成本;用自己的任務測試,比引用單一廠商 benchmark 更能做採購決策。
我會怎麼判斷是否採用
Hindsight 適合需要跨工作階段保留狀態、使用者偏好或專案經驗,且能接受加入記憶治理層的 Agent 團隊。它也提供多種部署與 SDK 入口,讓概念驗證不必先重寫整套應用。相反地,如果你的問答只需要搜尋一批固定文件、資料不應長期保存,或每次回答都必須嚴格限定在可引用的權威文本,那就先把 RAG 或一般資料庫做好,通常更簡單。
我最看重的不是「代理會記得更多」,而是能否說清楚它記住了什麼、為何在此刻取回、來源是否還有效,以及如何讓錯誤記憶退出系統。若這些問題尚未回答,長期記憶只是把舊錯誤保存得更久;若治理與評測先到位,retain、recall、reflect 才有機會把短暫互動轉成可管理的工作脈絡。
參考資料
- Hindsight 官方 GitHub README 與原始碼
- 官方安裝文件
- 官方 Recall 檢索說明
- 官方 Reflect 說明
- 官方 RAG 與記憶比較
- 官方 Mental Models 說明
- 官方 Memory Defense 說明
