项目: mem0ai/mem0 — AI Agent 的记忆层基础设施
Star: 64.9K+ | Commits: 2600+ | 许可: Apache 2.0
核心定位: 为 AI Agent 提供持久化的、可检索的、多层级的记忆能力
1. 系统定位与设计哲学
Mem0 将自身定义为 “The Memory Layer for Personalized AI”,不是一个 RAG 框架,不是向量数据库,而是一个面向 Agent 的记忆中间层。它的核心命题是:AI 助手应该像人一样”记住”对话上下文、用户偏好和历史事实,而不是每次会话都从零开始。
设计哲学有三个关键原则:
- 记忆即一等公民:记忆不是对话历史的副产品,而是独立的、结构化的、可检索的知识单元
- 推理时提取而非存储时索引:不是把整段对话塞进向量库,而是用 LLM 从对话中提取离散事实
- 多信号融合检索:语义相似度 + BM25 关键词 + 实体匹配,三路信号并行打分后融合
2026 年 4 月的新算法(V3)在 LoCoMo 基准上从 71.4 提升到 92.5,LongMemEval 从 67.8 到 94.4,核心改进是将原来的 add/update/delete 三步 LLM 调用简化为单次 ADD-only 提取——记忆只增不删,依赖检索排序来衰减过时信息。
2. 核心抽象:MemoryBase 接口
class MemoryBase(ABC):
@abstractmethod
def get(self, memory_id): ...
@abstractmethod
def get_all(self): ...
@abstractmethod
def update(self, memory_id, data): ...
@abstractmethod
def delete(self, memory_id): ...
@abstractmethod
def history(self, memory_id): ...
基类极其精简——只有 5 个抽象方法,定义了记忆的完整 CRUD + 历史追溯能力。这是一个教科书式的接口隔离原则实现:上层只依赖这 5 个方法,底层实现可以是 OSS 版本、平台版本、或任何自定义后端。
值得注意的是 history() 方法——它意味着每条记忆都有完整的变更审计轨迹,这对企业级场景(合规、溯源)是必要的。
3. 核心类 Memory 的初始化架构
class Memory(MemoryBase):
def __init__(self, config: MemoryConfig = MemoryConfig()):
self.embedding_model = EmbedderFactory.create(...)
self.vector_store = VectorStoreFactory.create(...)
self.llm = LlmFactory.create(...)
self.db = SQLiteManager(self.config.history_db_path)
self.reranker = RerankerFactory.create(...) # 可选
Memory 类的构造函数暴露了 Mem0 的四大核心依赖:
| 组件 | 职责 | 工厂类 |
|---|
|——|——|——–|
| EmbedderFactory | 文本向量化 | 支持 OpenAI、Ollama、HuggingFace 等 |
|---|---|---|
| VectorStoreFactory | 向量存储与检索 | 支持 Qdrant、FAISS、Elasticsearch、pgvector 等 |
| LlmFactory | LLM 推理(事实提取) | 支持 OpenAI、Anthropic、Ollama 等 |
| SQLiteManager | 对话历史 + 审计日志 | 本地 SQLite,轻量持久化 |
| RerankerFactory | 结果重排序(可选) | 提升检索精度 |
工厂模式的统一抽象:所有组件通过 Factory 创建,配置驱动,零硬编码。用户只需修改 MemoryConfig 即可切换整个技术栈(比如从 OpenAI 切到 Ollama 本地推理)。
实体存储(entity_store)采用惰性初始化——只有在第一次需要实体链接时才创建,避免不必要的资源开销。对于 Qdrant 后端,还会共享已有的 client 实例以避免 RocksDB 锁竞争。
4. 记忆添加流程:V3 分阶段批处理管线
add() 方法是 Mem0 最核心的流程,V3 版本将其重构为 8 阶段流水线:
Phase 0: 上下文收集 — 获取最近 10 条对话消息
Phase 1: 已有记忆检索 — 用当前消息嵌入搜索 top-10 相关记忆
Phase 2: LLM 提取(单次调用) — 从对话中提取离散事实
Phase 3: 批量嵌入 — 对所有提取的记忆文本批量生成向量
Phase 4: CPU 处理 — 词形还原(BM25)、元数据填充
Phase 5: 哈希去重 — MD5 哈希对比,跳过重复记忆
Phase 6: 批量持久化 — 写入向量库 + 记录变更历史
Phase 7: 批量实体链接 — 提取实体、嵌入、搜索已有实体、合并或新建
Phase 8: 保存对话 + 返回结果
关键设计决策:
- UUID 反幻觉映射:Phase 1 将已有的 UUID 记忆映射为整数索引传给 LLM,避免 LLM 编造不存在的 ID
- ADD-only 策略:V3 不再让 LLM 决定 UPDATE/DELETE,所有提取的事实都是新增,通过检索排序自然衰减
- 批量降级:
embed_batch失败时逐条降级,insert批量失败时逐条重试——优雅降级贯穿始终 - 实体全局去重:Phase 7 跨所有记忆做实体去重,单次批量嵌入 + 单次批量搜索 + 单次批量插入
5. 搜索与混合检索机制
def search(self, query, *, top_k=20, filters=None, threshold=0.1, rerank=False, explain=False):
搜索接口支持:
- 语义相似度:向量余弦距离
- BM25 关键词匹配:需要
qdrant/elasticsearch/pgvector等支持keyword_search的后端 - 实体匹配加权:
ENTITY_BOOST_WEIGHT对包含匹配实体的结果加分 - 三路融合:
score_and_rank()函数统一归一化后加权求和 - 可选重排序:通过
RerankerFactory创建的 reranker 二次精排
过滤机制非常丰富,支持等值、范围、集合、模糊、逻辑组合等操作符:
# 示例
filters={"user_id": "u1", "metadata": {"topic": {"in": ["tech", "science"]}}}
过期记忆默认隐藏(show_expired=False),基于 expiration_date 字段过滤。
6. 多层级作用域与隔离
Mem0 的记忆通过三个维度进行作用域隔离:
- user_id:用户级记忆,跨会话持久
- agent_id:Agent 级记忆,特定 Agent 的知识
- run_id:运行级记忆,单次任务的临时上下文
三者可以组合使用,但至少需要提供一个。这是通过 _build_filters_and_metadata() 函数实现的:
def _build_filters_and_metadata(*, user_id=None, agent_id=None, run_id=None, ...):
# 验证、修剪、构建 metadata 和 filters
# 阻止调用者通过 metadata 注入身份字段
安全加固措施:
- 身份字段不可篡改:
_IDENTITY_KEYS阻止通过 metadata 注入user_id/agent_id/run_id - Entity ID 验证:
_validate_and_trim_entity_id()处理类型转换、空白裁剪、非法字符 - 敏感字段脱敏:
_SENSITIVE_FIELDS_EXACT+_SENSITIVE_SUFFIXES用于遥测数据脱敏
Agent 记忆的特殊处理:当 agent_id 存在且消息包含 assistant 角色时,使用 AGENT_CONTEXT_SUFFIX 系统提示词,使 LLM 以 Agent 视角提取事实。
7. 实体链接与知识图谱
V3 引入了实体链接机制,让记忆不仅仅是孤立的文本片段,而是通过实体形成关联网络:
def _upsert_entity(self, entity_text, entity_type, memory_id, filters):
# 1. 嵌入实体文本
# 2. 精确文本匹配已有实体
# 3. 语义相似度匹配(阈值 0.95)
# 4. 匹配到 → 更新 linked_memory_ids
# 5. 未匹配 → 新建实体记录
实体存储与记忆存储在同一个向量库的不同集合中({collection}_entities)。每个实体记录包含:
data:实体文本entity_type:实体类型(人名、地名、组织等)linked_memory_ids:关联的记忆 ID 列表
实体提取依赖 extract_entities() / extract_entities_batch() 工具函数,在 V3 批量管线中,实体去重、嵌入、搜索、插入都是批量操作,大幅降低 API 调用开销。
8. 记忆生命周期管理
过期机制
def _payload_is_expired(payload):
expiration_date = payload.get("expiration_date")
return date.fromisoformat(str(expiration_date)) < datetime.now(timezone.utc).date()
记忆支持可选的 expiration_date,过期后默认不可见,但数据不物理删除。
变更历史
SQLiteManager 维护每条记忆的完整变更历史:
history_records = [{
"memory_id": ...,
"old_memory": None,
"new_memory": text,
"event": "ADD",
"created_at": ...,
"is_deleted": 0,
}]
程序性记忆
当 memory_type="procedural_memory" 且指定 agent_id 时,走专用的 _create_procedural_memory() 路径,使用 PROCEDURAL_MEMORY_SYSTEM_PROMPT 提取操作步骤类知识。
9. 安全与工程健壮性
Mem0 在工程层面做了大量防御性设计:
| 机制 | 实现 |
|---|
|——|——|
| 配置安全深拷贝 | _safe_deepcopy_config() 处理不可序列化对象,保留运行时字段,脱敏敏感字段 |
|---|---|
| 敏感字段识别 | 分层策略:运行时白名单 → 精确黑名单 → 后缀模式匹配 |
| 身份字段保护 | _strip_identity_keys() 阻止 metadata 注入攻击 |
| 批量降级 | 每个批量操作都有逐条重试的 fallback 路径 |
| LLM 错误区分 | 不再静默吞掉 LLM 错误,向上抛出 LLMError 以便调用方区分”LLM 不可用”和”无事实可提取” |
| 遥测控制 | MEM0_TELEMETRY 开关,capture_event() 只在启用时上报 |
| 向量库格式兼容 | _vector_store_list_rows() 处理不同后端返回格式差异 |
10. 对 OpenMate 的启示
Mem0 的架构对 OpenMate 的 Agent 记忆系统设计有直接参考价值:
- 接口极简主义:
MemoryBase只有 5 个方法,但足以支撑完整记忆生命周期。OpenMate 的记忆接口也应遵循最小完备原则。
- 工厂模式 + 配置驱动:所有组件通过 Factory 创建,零硬编码。这使得同一套代码可以跑在 OpenAI 云端,也可以跑在本地 Ollama。
- ADD-only 新算法:V3 放弃了让 LLM 决定增删改的策略,只提取新事实,通过检索排序自然衰减。这大幅降低了 LLM 调用成本(单次 vs 三次)和幻觉风险。
- 多信号融合检索:纯语义搜索不够——BM25 关键词匹配 + 实体匹配 + 语义相似度的三路融合,才能覆盖不同类型的查询意图。
- 实体链接是关键增量:通过实体网络将孤立的记忆片段连接起来,可以在检索时实现”关联推理”——查一个人时自动带上相关的项目、组织、事件。
- 批量管线设计:V3 的 8 阶段管线中,每个阶段都是批量操作,失败时逐条降级。这种”批量优先、逐条兜底”的模式值得在 OpenMate 中复用。
- 作用域隔离:user/agent/run 三级作用域 + 身份字段保护,是多用户多 Agent 系统的必备安全机制。
- 过期而非删除:记忆带过期日期,默认隐藏但不物理删除,既保证了”遗忘”能力,又保留了审计追溯的可能。
*分析基于 mem0ai/mem0 main 分支源码,2026 年 9 月*
发表回复