<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mem0 on YennJ12 Engineering Blog</title><link>https://yennj12.js.org/yennj12_blog_V4/tags/mem0/</link><description>Recent content in Mem0 on YennJ12 Engineering Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0800</lastBuildDate><atom:link href="https://yennj12.js.org/yennj12_blog_V4/tags/mem0/feed.xml" rel="self" type="application/rss+xml"/><item><title>Mem0 Intro Part 1 — 全景架構 — 為什麼 Agent 需要一個記憶層而不是更長的上下文</title><link>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part1-architecture-overview-zh/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0800</pubDate><guid>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part1-architecture-overview-zh/</guid><description>大多數人給 Agent 加記憶的方式，是把對話歷史一路 append 進 prompt，等到爆 context 了再加一個摘要函式。 真正的答案是：記憶不是「更長的上下文」，它是一個獨立的儲存與檢索問題——你需要決定什麼值得記、記成什麼形狀、怎麼在下一次對話中只取回相關的那幾條。 把歷史全塞進去只需要一行 messages.append()。記憶層需要一整套子系統。 這個系列拆解的是後者。
前言：這個系列要做什麼 Mem0 （Apache-2.0，2023-06 開源）是目前最多人用的開源 AI Agent 記憶層：GitHub 上約 6.5 萬顆星、7.6 千 fork，官方定位是「drop-in memory infrastructure for AI agents and apps」。
它值得逐層讀完，理由不是星星數，而是：它把「Agent 記憶」這個很虛的詞，收斂成了兩個非常具體的工程問題——什麼進來、什麼出去。 而且它的答案在 2026 年做過一次大幅度的自我否定（v2 → v3），把原本論文裡的兩階段 ADD/UPDATE/DELETE 管線整個換掉。讀懂那次改動為什麼發生，比讀懂任何單一功能都有價值。
這個系列分成五篇：
Part 主題 對應原始碼 Part 1（本篇） 全景架構、兩條路徑、作用域模型、部署形態 mem0/memory/main.py、mem0/configs/base.py Part 2 寫入路徑：ADD-only 萃取管線的八個階段 Memory._add_to_vector_store()、configs/prompts.py Part 3 讀取路徑：語意 + BM25 + 實體的多訊號融合 Memory._search_vector_store()、mem0/utils/scoring.py Part 4 儲存層與後端選型：三個 store、25 種向量庫、自架 server mem0/vector_stores/、mem0/memory/storage.py、server/ Part 5 生產部署：OSS vs Platform、進階能力、評測與踩坑 docs/、evaluation/ 本篇的目標很單純：讀完之後，你能在腦中畫出 Mem0 的方塊圖，說得出一句「我喜歡靠走道的位置」是怎麼變成一筆可被檢索的記憶，並且知道 user_id / agent_id / run_id 這三個參數各自該放什麼。</description></item><item><title>Mem0 Intro Part 2 — 寫入路徑 — ADD-only 萃取管線與那次自我否定</title><link>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part2-add-extraction-pipeline-zh/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0800</pubDate><guid>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part2-add-extraction-pipeline-zh/</guid><description>大多數人設計「讓 LLM 自己管理記憶」的方式，是給它一組工具——新增、更新、刪除——然後相信它會做對的事。 真正的答案是：LLM 在「該不該覆蓋一條既有事實」這件事上的判斷力，遠低於你的期待；而它判斷錯的代價是不可逆的——被刪掉的記憶不會回來。 Mem0 自己寫過那個版本，也自己把它拆掉了。 這一篇講的是拆掉的理由，以及換上去的東西。
前言 Part 1 建立了地圖，也留下一個伏筆：Mem0 v3 的萃取管線只有 ADD，沒有 UPDATE 和 DELETE。
這件事違反直覺。一個記憶系統怎麼可能不支援更新？使用者說「我搬到柏林了」，難道不該把「使用者住在里斯本」改掉嗎？
答案是：不該——至少不該由萃取階段的那次 LLM 呼叫來決定。 這一篇解釋為什麼，並逐行拆解取而代之的八階段管線。
本篇的目標：讀完之後，你能算出一次 add() 花了幾次 LLM 呼叫、幾次 embedding、幾次資料庫往返，知道什麼時候該用 infer=False，並且明白你必須自己補上哪一塊治理邏輯。
一、核心問題：讓 LLM 管記憶會出什麼事 1.1 v2 的做法：四動作模型 Mem0 的原始論文 描述的是一個兩階段管線：
階段一：萃取 對話 ──LLM 呼叫 #1──▶ 候選事實清單 [&amp;#34;使用者住在柏林&amp;#34;, &amp;#34;使用者喜歡爵士樂&amp;#34;] 階段二：更新（這是問題所在） 對每個候選事實： 向量檢索出相似的既有記憶 ──LLM 呼叫 #2──▶ 從四個工具裡選一個： ADD 新增一條 UPDATE 改寫某條既有記憶 DELETE 刪掉某條既有記憶 NOOP 什麼都不做 這個設計很優雅，而且在論文的 LOCOMO 評測上表現不錯（相對 OpenAI 的記憶方案有 26% 的 LLM-as-a-Judge 相對提升）。程式碼至今還留在 repo 裡——DEFAULT_UPDATE_MEMORY_PROMPT 與 get_update_memory_messages() 仍然在 mem0/configs/prompts.</description></item><item><title>Mem0 Intro Part 3 — 讀取路徑 — 語意、關鍵字與實體的三訊號融合</title><link>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part3-hybrid-retrieval-zh/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0800</pubDate><guid>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part3-hybrid-retrieval-zh/</guid><description>大多數人做記憶檢索，是「把查詢嵌入，向量庫 top-5，塞進 prompt」，然後在召回不準的時候去調 embedding 模型。 真正的答案是：記憶檢索的失敗有三種完全不同的成因——語意相近但講的不是同一件事、要找的是一個專有名詞而向量模型覺得同類詞都很像、以及答案根本沒有出現在查詢的字面裡。 一條訊號解決不了三種問題。 這一篇拆的是那三條訊號怎麼算、怎麼合。
前言 Part 2 的結論留下一個很重的包袱：v3 的萃取是 ADD-only，舊事實不會被覆蓋。 「使用者住在里斯本」和「使用者住在柏林」會同時存在記憶庫裡。
這把壓力完全轉移到了讀取路徑。如果檢索沒辦法讓正確的那一條排在前面，整個 ADD-only 的設計就崩了。
這一篇拆解 Mem0 怎麼做這件事。好消息是：讀取路徑完全不呼叫 LLM，所以它快（~100ms）又便宜；壞消息是：它因此只能靠純演算法的訊號，而那些訊號的權重是寫死在原始碼裡的常數——你調不動，但你可以理解並繞過。
本篇的目標：讀完之後，你能看懂 explain=True 吐出來的每一個欄位，能解釋為什麼某條記憶沒被撈到，並且知道在中文語料上這套機制會退化成什麼樣子。
一、核心問題：純向量檢索在記憶場景的三個盲點 1.1 記憶檢索 ≠ 文件檢索 先講清楚為什麼不能照搬 RAG 的做法。
文件 RAG 記憶檢索 ───────────────────────────────────────────────────────── chunk 有幾百 token 記憶只有一句話（10–30 token） → 語意向量資訊量足 → 向量很容易和同類句子撞在一起 chunk 之間大多獨立 記憶之間高度相關且可能互相矛盾 → 取 top-5 就好 → 要判斷哪條才是「現在的」 查詢通常是完整問句 查詢常常是一個名字或一個詞 → 語意匹配有效 → 需要精確匹配 語料是靜態的 記憶是持續累積且單調成長的 → 索引可以離線優化 → 舊的同義記憶會擠掉新的 第一行是最根本的差異。一句 20 個 token 的事實，嵌成 1536 維向量之後，資訊密度非常低——「使用者喜歡爵士樂」和「使用者喜歡古典樂」的餘弦相似度可能高達 0.</description></item><item><title>Mem0 Intro Part 4 — 儲存層與後端選型 — 三個 store、25 種向量庫與那個關鍵能力差異</title><link>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part4-storage-backends-zh/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0800</pubDate><guid>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part4-storage-backends-zh/</guid><description>大多數人選向量庫的方式，是看 benchmark 的 QPS 數字，或者「團隊已經在用 Postgres 了就用 pgvector」。 真正的答案是：在 Mem0 這套架構下，向量庫的選擇會直接決定你的三訊號檢索能不能運作——25 個支援的後端裡，有 10 個根本沒實作關鍵字檢索，選到它們，你的 BM25 訊號會靜默歸零。 這不是效能差異，是功能差異。 而它不會有任何錯誤訊息。
前言 Part 2 拆了寫入、Part 3 拆了讀取。兩條路徑上的每一次 insert、search、keyword_search 都委託給後端——Mem0 自己不存任何東西。
這一篇下到那一層：三個 store 各自存什麼、schema 長什麼樣、25 種向量庫的能力矩陣、以及那個影響最大卻最少被討論的差異——誰有 keyword_search，誰沒有。
本篇的目標：讀完之後，你能對著自己的規模、語言、與已有基礎設施，選出一組後端，並說得出每個選擇放棄了什麼。
一、核心問題：一份記憶被拆成三個地方存 1.1 為什麼不能只用一個資料庫 Part 1 給過結論，這裡補上原因。一條記憶要支援的查詢有五種，而它們對索引結構的要求互相衝突：
一條記憶：「Alice 推薦了中山區那家拉麵店」 查詢類型 需要的索引 衝突點 ────────────────────────────────────────────────────────────── 「他喜歡什麼食物」 HNSW / IVF 向量索引 近似最近鄰，不保證精確 「拉麵」 倒排索引 + 分詞 需要詞彙統計（IDF） 「關於 Alice」 實體 → 記憶 反向索引 Alice 不在這條記憶的文字裡 「按時間列出」 B-tree on created_at 向量庫的排序能力普遍很弱 「這條哪來的」 稽核日誌 需要 append-only 的事件表 第三行是關鍵：「Alice」這個詞完全沒有出現在記憶文字裡，所以無論向量索引還是倒排索引都撈不到它。只有一個從實體指回記憶的反向索引能解決。</description></item><item><title>Mem0 Intro Part 5 — 生產部署 — OSS 與 Platform 的分界線在哪裡</title><link>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part5-production-oss-vs-platform-zh/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0800</pubDate><guid>https://yennj12.js.org/yennj12_blog_V4/posts/mem0-intro-part5-production-oss-vs-platform-zh/</guid><description>大多數人評估「要用開源版還是託管版」的方式，是比較功能清單的長度，然後選比較便宜的那個。 真正的答案是：功能清單只告訴你今天缺什麼，不告訴你半年後你會自己寫出什麼。 Mem0 的 OSS 與 Platform 之間那條線，剛好切在「記憶治理」上——而 ADD-only 架構保證了你遲早要處理它。 你不是在選要不要付錢，是在選要不要自己寫。
前言 前四篇拆完了 OSS 的全貌：全景架構 、ADD-only 寫入 、三訊號檢索 、儲存層選型 。
一路上反覆出現同一句話：「這個在 Platform 有，OSS 沒有。」 圖記憶、時序推理、記憶衰減、背景整合、自訂分類、webhook。
這一篇把那條線畫清楚，並且回答一個更實際的問題：如果你選 OSS，你必須自己補哪些東西，以及那大概要花多少工。
本篇的目標：讀完之後，你能做出一個有依據的 OSS/Platform 決策，能完成 v2→v3 的遷移，能用官方開源的框架測自己的資料，並且有一份可以照抄的生產檢查清單。
一、核心問題：那條線切在哪裡 官方文件對這件事相當坦白，開宗明義寫著兩者「run the same core extraction and retrieval logic」，差別在三類東西：
┌────────────────────────────────────────────────────┐ │ 完全相同的部分 │ │ · add / search / get / get_all / update / │ │ delete / delete_all / history 全套 API │ │ · user_id / agent_id / run_id 作用域 │ │ · AND / OR / NOT 過濾組合、萬用字元 │ │ · 實體感知排序（兩邊都抽實體、都用共享實體加權） │ │ · 多模態輸入、記憶過期、reranking、程序記憶 │ │ · custom_instructions │ │ · Python / JS SDK + REST API │ └────────────────────────────────────────────────────┘ │ ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ 差異一：託管 │ │ 差異二：搜尋排序 │ │ 差異三：管理面 │ │ │ │ （v3-only） │ │ │ │ · 誰跑向量庫 │ │ · Graph Memory │ │ · 組織／專案 │ │ · 誰負責擴充 │ │ · Memory Decay │ │ · app_id 租戶隔離 │ │ · 有沒有 │ │ · Temporal │ │ · 專案級事件流 │ │ dashboard │ │ Reasoning │ │ · 自訂分類 │ │ │ │ · Dream │ │ · Webhook │ │ ← 這是錢的問題 │ │ ← 這是工程量問題 │ │ ← 這是產品面問題 │ └───────────────┘ └──────────────────┘ └──────────────────┘ 差異一是最不重要的那個。 自架一套 pgvector 不難，Part 4 講過。</description></item></channel></rss>