乐于分享
好东西不私藏

AI Agent 两种不同Memory系统实现:Mem0 vs Mem9

AI Agent 两种不同Memory系统实现:Mem0 vs Mem9

引言

Mem0 与 Mem9 都是针对 AI Agent 核心痛点 —— 记忆系统打造的开源记忆层,二者却采用截然不同的技术思路。本文将通过以下例子,对两种不同的记忆系统设计理念和技术实现,展开学习:

历史记忆:"User lives in Beijing"新消息:"我搬到上海了"


一、Mem0:写入极简,检索融合,异步清理

1.1 设计理念

Mem0核心思路是:

写入时 LLM 只做一件事——提取事实。绝不修改已有记忆。清理和去重推迟到检索时和离线批处理。

v2 时代 Mem0 也是 LLM 实时判断 ADD/UPDATE/DELETE,v3 彻底放弃了这个路线。原因很简单:让 LLM 在写入时判断一条事实该增、改还是删,是个容易出错的分类问题。一旦判断错(比如误把 "Moved to Shanghai" UPDATE 成 "Lives in Shanghai",原始时间信息就丢了),错误不可逆。

v3 的核心变化:ADD-only + 多信号检索 + 异步 Dream 清理

1.2 写入流程

关键代码 —— LLM 提取只做 ADD:

# Mem0/memory/main.py:725system_prompt = ADDITIVE_EXTRACTION_PROMPT# prompt 核心约束:# "Your sole operation is ADD: identify every piece of memorable#  information and produce self-contained factual statements."# "Do NOT re-extract information already captured."response = self.llm.generate_response(messages=[    {"role""system""content": system_prompt},    {"role""user""content": user_prompt},  # 包含已有记忆 + 新消息])# 解析结果 → 只有 ADD 事件extracted_memories = json.loads(response).get("memory", [])# [{"id": "0", "text": "User moved to Shanghai as of 2025-05-23"}]

关键代码 —— 实体链接作为跨记忆桥梁:

# Mem0/memory/main.py:866 (Phase 7)# 从记忆文本提取实体entities = extract_entities_batch(["User moved to Shanghai as of 2025-05-23"])# → [("PROPER", "Shanghai"), ("NOUN", "User")]# 每个实体独立存入 entity_store# "User" 实体已存在 → 追加 mem_2 到 linked_memory_idsentity_store.update(    vector_id="ent_user",    payload={"linked_memory_ids": ["mem_1""mem_2"]}  # 桥梁)

1.3 检索流程

查询: Where does the user live?
预处理lemmatize + spaCy 提取实体
embed 查询向量
语义搜索过拉取 60 条候选
BM25 关键词搜索过拉取 60 条候选
实体搜索'user' → entity_store命中 → linked: [mem_1, mem_2]
融合打分score = (semantic + bm25 + entity) / max_possible
排序结果:1. mem_2 'Moved to Shanghai' (0.73)2. mem_1 'Lives in Beijing' (0.72)两条都返回,上海排第一

关键代码 —— 多信号融合:

# Mem0/utils/scoring.py:60-121defscore_and_rank(semantic_results, bm25_scores, entity_boosts, threshold, top_k):    has_bm25 = bool(bm25_scores)    has_entity = bool(entity_boosts)    max_possible = 1.0if has_bm25:        max_possible += 1.0if has_entity:        max_possible += 0.5# ENTITY_BOOST_WEIGHTfor result in semantic_results:        semantic_score = result["score"]       # 如 mem_2: 0.72, mem_1: 0.65        bm25_score = bm25_scores.get(mem_id, 0)  # mem_2: 0.62, mem_1: 0.67        entity_boost = entity_boosts.get(mem_id, 0)  # 两条都 0.49        combined = (semantic_score + bm25_score + entity_boost) / max_possible# mem_2: (0.72 + 0.62 + 0.49) / 2.5 = 0.732 ✓# mem_1: (0.65 + 0.67 + 0.49) / 2.5 = 0.724

北京被召回的原因: entity_boost 对两条记忆相同(共享 "User" 实体),BM25 上 mem_1 甚至略高("lives" 更匹配 "where...live")。差距仅来自语义搜索的 0.07 分。agent 同时看到两条,自己判断上海是最新的。

1.4 异步清理:Dream Consolidation

写入时不做 UPDATE/DELETE,记忆会逐渐膨胀。Mem0 用一个独立的 LLM agent(Dream)定期清理:

  • • 触发条件:距上次 ≥ 24h、积累 ≥ 5 个会话、记忆 ≥ 20 条
  • • 处理逻辑:拉全量记忆 → 识别重复 → MERGE/REWRITE → DELETE 过期/噪声/凭据
  • • TTL 规则:操作记忆 7 天、项目记忆 90 天

这相当于把 v2 的实时 CRUD 判断变成了"定期审计员"——看到全量记忆再决策,质量更高。


二、Mem9:写入对账,检索 RRF,实时 CRUD

2.1 设计理念

Mem9核心思路是:

每次写入都对账——LLM 提取事实后,和已有记忆逐一比对,实时决定 ADD/UPDATE/DELETE。记忆库始终处于"最新状态"。

Agent 插件是薄客户端,只负责发 HTTP 请求。embedding、向量搜索、LLM 调用全部在服务端完成。

2.2 写入流程

关键代码 —— Phase 2 对账 prompt:

// Mem9/server/internal/service/ingest.go:991-1064systemPrompt := `You are a memory management engine.## Actions- ADD: New info not in any existing memory.- UPDATE: Replaces the same attribute/slot of the same entity only.- DELETE: Explicitly contradicts an existing memory.- NOOP: Already captured by an existing memory.## Rules1. Reference existing memories by integer ID ONLY.2. UPDATE only when the fact targets the same entity AND attribute slot.   A new attribute → ADD, not UPDATE.3. When a new fact conflicts with an existing memory on the same topic,   older memories are more likely outdated.`

关键代码 —— 反幻觉 ID 映射:

// Mem9/server/internal/service/ingest.go:969-984// 传给 LLM 的是整数 ID,防止 LLM 编造 UUIDtype memoryRef struct {    IntID int`json:"id"`    Text  string`json:"text"`    Age   string`json:"age,omitempty"`}refs := []memoryRef{{IntID: 0, Text: "User lives in Beijing", Age: "11 months ago"}}idMap := map[int]string{0"real-uuid-mem_1"}  // 执行时映射回真实 ID

执行动作 —— UPDATE/DELETE 都受保护:

// Mem9/server/internal/service/ingest.go:1131-1221case"DELETE":if memories[intID].MemoryType == "pinned" {// pinned 记忆禁止自动删除,跳过continue    }    memories.SetState(realID, StateDeleted)  // 逻辑删除case"UPDATE":if memories[intID].MemoryType == "pinned" {// pinned 记忆降级为 ADD        addInsight(...)continue    }    updateInsight(realID, newText, ...)

2.3 检索流程

关键区别: mem_1(北京)在 SQL 查询层就被 WHERE state = 'active' 过滤掉了,根本不会进入候选池。agent 只看到唯一的答案。


三、共同点

1. 定位一致。 都是 AI Agent 的持久化记忆层,解决"跨会话遗忘"问题。

2. LLM 做事实提取。 写入时都不存原始对话,而是 LLM 提取原子化、自包含的事实后再存储。都要求消除代词、保留原语言、丢弃问候/噪声。

3. 混合检索。 都认识到纯语义搜索不够用,同时走「向量 + 关键词」两路再融合。Mem0 用加权求和,Mem9 用 RRF。

4. query_intent 过滤。 都识别并丢弃用户的"搜索/查询类问题"("帮我查一下 nginx 配置"),只保留关于自身的陈述("我们生产用 nginx 1.24")。

5. 多 Agent 平台集成。 都覆盖了 Claude Code、OpenClaw、Codex 等主流 agent 运行时。


四、核心不同点:记忆管理的两种哲学

以下只聚焦记忆管理的设计差异,不讨论部署方式、存储引擎、可插拔性等工程层面的区别。

4.1 写入哲学:ADD-only vs 实时 CRUD

这是最根本的分歧。

Mem0 
Mem9
写入动作
只 ADD
ADD / UPDATE / DELETE / NOOP
LLM 调用次数
1 次(提取)
2 次(提取 + 对账)
旧记忆处理
不动,关联到实体
覆盖或标记删除
信息保真
历史全保留
旧版本可能丢失
记忆库状态
逐渐膨胀
实时精简

北京→上海的例子最直观:

  • • Mem0:两条都在,"上海"排第一、"北京"排第二,agent 自己判断
  • • Mem9:写入时"北京"被 DELETE,库里只有"上海"

Mem0 的思路是「写入时宁可多存,检索时再精确筛选」。Mem9 的思路是「每次写入对账,库里始终是最新状态」。

4.2 检索:多信号融合 vs RRF + 查询分类

Mem0
Mem9
融合方式
加权求和 (semantic + bm25 + entity) / max
RRF(Reciprocal Rank Fusion)
信号种类
3 种(语义 + BM25 + 实体链接)
2 种(语义 + FTS)
查询意图理解
无,统一处理
有:枚举型/精确型/开放型,不同策略
置信度模型
无,直接按分数截断
多因子:RRF + 向量 + 双命中 + 时间 + 来源 + 证据

Mem0 的检索相对轻量,依赖于实体链接作为差异化信号。Mem9 的检索极其复杂(recall.go 1800+ 行),对查询做 NLP 分类后选择不同策略。

4.3 跨记忆关联:实体桥梁 vs LLM 对账覆盖

Mem0
Mem9
关联机制
spaCy 提取实体 → entity_store 做桥梁
Phase 2 LLM 语义判断后直接操作数据库
关联持久化
entity_store 的 linked_memory_ids
不存在——旧版本被覆盖/删除
检索增强
entity_boost 加分
UPDATE 保证库里只有最新版

Mem0 用实体做间接关联:「User」实体链接了北京和上海两条记忆 → 查询提到 User 时两条都被 boost。Mem9 直接让 LLM 判断北京已过时 → DELETE → 检索时不存在。

4.4 清理机制:异步批处理 vs 写入时实时

Mem0
Mem9
方式
Dream agent 异步批处理
Phase 2 对账实时处理
触发
≥24h + ≥5 会话 + ≥20 条
每次 smart ingest 自动执行
范围
全量记忆(看到全貌再合并)
增量(只对本次事实做对账)
TTL
硬规则(7 天/90 天)
无自动 TTL,有状态机可手动归档

Mem0 的 Dream 能看到全量,合并决策更准,但清理不及时。Mem9 每次写入即时清理,但受限于对账时的候选范围(最多 60 条),可能看不到跨时间窗口的全局重复。

4.5 记忆保护机制

Mem9 有 pinned 类型——用户手动创建的规则/配置被标记为 pinned 后,LLM 对账时不能自动 UPDATE/DELETE(代码层硬拦截,pinned UPDATE 降级为 ADD,pinned DELETE 直接跳过)。Mem0 没有这个概念,所有记忆平等对待。


五、小结

Mem0:  写入极简(只ADD) ─→ 检索融合(语义+BM25+实体) ─→ 异步清理(Dream)Mem9:  写入对账(ADD/UPDATE/DELETE) ─→ 检索RRF(查询分类+置信度) ─→ 实时清理本质区别:Mem0 = 把复杂度推迟到检索时,写入只管捕获,历史全保留Mem9 = 把复杂度前置到写入时,每次对账清理,库始终最新

如果你的场景需要完整的历史追溯、agent 有能力自己从多条候选中推理出当前状态,Mem0 的 ADD-only 更适合。如果你希望记忆库始终是精确的最新快照、不希望 agent 看到过时信息,Mem9 的实时 CRUD 更合适。

Mem9 的记忆管理操作,恰恰是 Mem0 v2 放弃的路线(写入时 LLM 判断 CRUD),并在工程化上做了强化(反幻觉 ID 映射、pinned 保护、查询形状分类)。


参考文档:Mem0: https://Mem0.aiMem9:https://Mem9.ai