世界太吵,來原聲聽播客

Hugging Face

Agent 的記憶不是聊天記錄,而是一套信息檢索系統

LLM 無狀態,記住你不能靠拉長聊天歷史。長期記憶要外置於會話:MEM0 用三個存儲、每輪抽取,檢索時把向量、關鍵詞、實體分加權,只取最相關的幾十條。

Agent 架構長期記憶向量檢索MEM0
拆開 MEM0 的存儲、攝取、檢索三層:三個庫怎麼分工、抽取和召回各做什麼、2.5 分公式怎麼算。全是可直接照做的架構細節,不是概念課。

核心論點 · 點時間戳可跳到原聲

1:04

長期記憶不是聊天記錄拉長

LLM 本身是無狀態機器,兩個連續 prompt 之間沒有任何殘留。普通 agent 的「記性」只是把整段對話歷史隨每個新請求重發給模型,這叫 conversational memory;一旦開新會話,所有上下文清零。因此真正的長期記憶必須另建一個獨立服務,跨會話保存用戶偏好、agent 學到的知識和流程,並允許同一個用戶在多 agent 間共享同一份記憶。判斷標準很簡單:它不能和 session 存在同一個存儲裡。這段給後文定下「外置記憶系統」的前提。

4:07

實體單獨成庫,檢索才有捷徑

MEM0 的信息架構是三件套。主存儲是一個向量庫,容納每條「記憶」本體(一句話或一小段)及其元數據:創建/更新日期、歸屬用戶還是 agent、內容 hash、詞形還原版本等。第二個是實體庫,也是向量庫,但每個點是一個人/地名等實體,實體元數據裡鏈接一到多條主記憶——提到巴黎就會牽出所有跟巴黎有關的記憶。第三個是 SQLite,不存回憶,只記兩條流水:所有變更歷史的日誌,以及最近 10 條消息。前兩個庫負責語義關聯,SQLite 負責支撐短期上下文和審計。

10:12

記憶靠 LLM 抽取,不全量照存

Ingestion 在 agent 每回合結束後跑,MEM0 的默認方式是 infer=true:把本回合消息餵給一個專門做「記憶提取」的 LLM,prompt 裡帶上 user summary、相關舊記憶和最近消息,讓模型輸出結構化 JSON,裡面是要存檔的獨立記憶。另有 procedural 模式和 infer=false 兩種:前者複述整段操作過程,作者認為已不常用;後者把消息向量化後直接入庫,太粗糙。抽取式才是區分「記住關鍵信息」和「留存全文」的分水嶺。

12:12

沒最近消息,抽取會丟代詞

抽取 prompt 的上下文不是只有當前消息。管線先把本輪消息拼成一個整體嵌入,去主向量庫裡找相關舊記憶;同時從 SQLite 調出最近 10 條消息。這樣當用戶說「它很棒」「他這方面很擅長」時,上一兩句中的先行詞才能被 LLM 找到。沒有這個模塊,抽取模型會丟失指代,抽出來的記憶就是「it is great」這樣沒法用的廢記錄。此外 prompt 還會列出對話日期和當前日期。要點:單條消息不足以成為記憶,記憶要放在最近的對話流裡才成立。

20:18

檢索先撈多,再重排裁少

Retrieval 無論走 agent 顯式工具調用還是每回合自動注入,管線相同。query 先嵌入向量庫做相似度搜索,但這裡有個反直覺設計:第一輪不直接返回 top K。MEM0 會先取 max(top_k×4, 60) 條候選,再在這個粗池上做重排。作者舉例:你要 top K=10,向量搜索實際會拉回 60 條。原因是後續還有兩輪不同維度的打分,如果一開始只找回 10 條,重排模型沒有足夠的池子可挑。據作者判斷,MEM0 默認沒有 query rewriting,想要更好效果應在 harness 端自己加改寫。

23:20

實體關聯越少,檢索加分越高

三道評分之一是 entity boost。系統先從 query 裡抽實體,去實體庫檢索;命中的實體各自鏈接到若干主記憶。如果當初只有 2 條記憶綁在這實體上,很可能正是當前要找的,加分靠近 0.5;如果綁了 1000 條,這個實體幾乎沒有區分度,加分趨近 0。換句話說,實體加分獎勵的是「冷門但精確」的線索,懲罰大而全的泛主題。這解釋了為什麼用「巴黎」搜索時,系統不會返回百科式的巴黎常識,而是把你存在庫裡的那條「最愛巴黎瑪黑區」頂到前面。

24:20

檢索排序不能只信 embedding

最終分數是把每條候選的三項證據相加:向量相似度歸一到 0-1,BM25 關鍵詞詞形重疊歸一到 0-1,實體加分只有 0-0.5,理論上限正好 2.5。對粗池裡每條記憶都算這個總和,再除以 2.5,得到 0 到 1 的最終分數,按它切出真正的 top K 返回給 agent。BM25 輸入的關鍵詞版本在寫入時就算好存了詞形還原結果,所以重排時不用現場做 NLP。三分加權說明 MEM0 默認信任語義、關鍵詞、實體三種信號疊加,而不是只靠 embedding 一把梭。

26:22

跑通全套用不到大模型

這套架構最容易被低估的一點:開銷最大的「記憶提取」反而簡單,1B 到 12B 參數的模型就足夠,再小除非自己微調;作者示例推薦 Qwen 3 8B。Embedding 模型可以在 Hugging Face 按 feature extraction 任務篩選,再對照專門的 embedding benchmark 看多語或醫學等細分榜單。如果長期做,建議針對自己的場景微調抽取模型。MEM0 的嵌入默認用閉源模型,但每個環節都能換成本地開源組件——這也是它適合做個人記憶底座的原因。

原話 · 已逐字校驗

So remember that LLMs are stateless machines. In other words, when you send the prompt to your LLM, your LLM is going to process it and give you a completion. If you send another prompt to it after that, it will not remember anything about the previous prompt or the previous completion.

記住:LLM 是無狀態機器。換句話說,你發一個 prompt,LLM 處理完就給出結果;之後你發另一個 prompt,它對之前的 prompt 和剛才的輸出沒有任何記憶。

And before I forget, just remember that for long form memory to work, you're going to have to make it external to your conversation.

趁我還沒忘:長期記憶要能工作,你必須讓它外置於對話。

So um the ingestion part is going to be executed or it's going to be run after every agent turn.

攝取這一步在 agent 每一回合結束後執行。

And this one right here is very useful to identify and to figure out what pronouns mean for example in that particular new message.

這個機制非常有用:它能識別並弄清代詞在新消息裡指什麼。

So if you set a top k of 10, you're going to actually get 60 messages from this vector search.

如果你把 top k 設為 10,向量搜索實際上會拉回 60 條消息。

the score is going to be higher if there are less memories associated to that particular entity.

實體關聯的記憶越少,這項加分就越高。

I would recommend that you add a query rewriting system right here on the side of your harness.

我會建議你在 harness 這一側加一個查詢改寫系統。

數字與實體

初步候選池大小max(top_k×4, 60) 條20:18
top_k=10 時的實際召回60 條候選20:18
SQLite 保留的最近消息數10 條12:12
記憶提取推薦模型規模1B-12B 參數26:22
檢索最高總分(歸一化前)2.5 分24:20
實體 boost 的加分範圍0-0.5 分24:20

術語

BM25BM25 算法
稀疏關鍵詞檢索打分,按詞項重疊給出相關分;配合向量檢索做重排。
query rewriting查詢改寫
檢索前把用戶原始問題改寫得更利於召回;MEM0 默認沒有這一步。
lemmatized version詞形還原版本
把單詞轉成詞典原形後再入庫,供 BM25 做關鍵詞精確匹配。
entity store實體存儲
存放從記憶中抽出的人名/地名的向量庫;每個實體掛載關聯記憶。

收聽指南

誰該聽

正在為產品 Agent 做用戶長期記憶或多 agent 共享記憶的工程師;想評估用 MEM0 還是自建的架構決策者。

可跳過

不打算自己部署模型的話,26:22 之後的模型推薦與收尾可跳過。