Mem0 源码阅读笔记
Mem0 源码阅读笔记
版本:mem0
main分支,commitdd5f7e39
1. 核心概念与大致架构

1.1 定位与三段式数据流
Mem0 把自己定位为「AI agent 的记忆层(memory layer)」。一条记忆(memory)的写入与检索,本质是三段式流水线:
原始对话/文本
│ (1) LLM 抽取事实
▼
事实片段(fact)
│ (2) Embedder 向量化
▼
向量库(Vector Store) ── payload 中还带 hash / text_lemmatized / created_at 等
│
├── Entity Store(实体库,复用向量库的独立 collection)
│ 抽取 person/place/concept 等实体,建立「实体 → 记忆」反查,检索时给记忆加权
│
└── SQLite(history_db_path)记历史变更(ADD / UPDATE / DELETE),支撑 get_history()
在 Memory 的 __init__ 里一次性装配以下组件(前四个是工厂,Reranker 可选;SQLiteManager 单独负责历史库):
EmbedderFactory→ embedding 模型(OpenAI / Ollama / HuggingFace …)VectorStoreFactory→ 向量库(Qdrant / pgvector / Chroma / Milvus …,20+)LlmFactory→ 抽取/生成用的 LLMRerankerFactory(可选)→ 召回后重排SQLiteManager→ 历史库
1.2 一条 memory 的数据模型
MemoryItem(configs/base.py)与向量库 payload 对应:
id, memory(data), hash, score, created_at, updated_at, metadata
写入时 payload 还会塞进:text_lemmatized(BM25 用)、role、actor_id、user_id/agent_id/run_id、可选 attributed_to、expiration_date 等。
1.3 「entity」在 mem0 里有两重含义(务必区分)
- 作用域 ID(scoping entity):
user_id/agent_id/run_id。
它们是分区键,不是从文本里抽出来的。add()/search()的filters必须至少带其中一个,所有读写都按它隔离数据。代码里_build_filters_and_metadata就是用它构造隔离条件。 - 知识实体 +
entity_type(graph entity):从记忆文本中抽取出来的「人名 / 地名 / 概念 / 专有词」,存在独立的 entity store 里。
entity_type取自mem0/utils/entity_extraction.py的 spaCy 抽取结果,分四类:PROPER(专有名词)、QUOTED(引号短语)、TOPIC(名词短语)、IDENTIFIER(技术标识符)。
entity store 的 payload 形如{data, entity_type, linked_memory_ids, user_id...},linked_memory_ids把「提到同一实体的多条记忆」串起来——这就是后面 entity_boost 的来源。
本文后面提及的 Entity 主要指知识实体。
2. 流程介绍
2.1 从 Memory 进入的 add 写流程(含 entity / entity_type 落点)
入口 add() 关键步骤:
- 校验:
memory_type只允许传PROCEDURAL,传SEMANTIC/EPISODIC直接抛Mem0ValidationError。不传则当作通用事实记忆。 - 归一化
messages(str/dict/list[dict] 三种入参),构造processed_metadata与effective_filters(来自user_id/agent_id/run_id)。 - 若
agent_id+PROCEDURAL→ 走_create_procedural_memory(agent 操作型记忆)。 - 否则进入核心:
_add_to_vector_store。
核心写入 = V3 PHASED BATCH PIPELINE(代码注释是这么命名):
| 阶段 | 做什么 | 关键步骤 / 函数 |
|---|---|---|
| Phase 0 | 取该 scope 最近 10 条消息;parse_messages 把对话拼成一段文本 |
_add_to_vector_store 入口 |
| Phase 1 | 用拼好的文本向量,检索 top_k=10 已有记忆(UUID 映射成整数「防幻觉」,避免 LLM 编出不存在的 id) | 语义检索 + UUID→int 映射 |
| Phase 2 | 单次 LLM 调用(ADDITIVE_EXTRACTION_PROMPT),输出 memory 列表(每条含 text、可选 attributed_to、linked_memory_ids) |
LLM 加性抽取 |
| Phase 3 | embed_batch 批量向量化所有抽取文本(失败回退逐条 embed) |
embedding_model.embed_batch |
| Phase 4-5 | 逐条 CPU 处理 + MD5 hash 去重(与已有记忆 hash、批内 hash 比对,重复跳过);同时 lemmatize_for_bm25 生成 text_lemmatized |
hash 去重 + lemmatize |
| Phase 6 | 批量 insert 向量库 + 批量写 history(ADD 事件) | vector_store.insert + db.batch_add_history |
| Phase 7 | 批量实体抽取(extract_entities_batch)+ 实体库 upsert:先全局去重实体,批量 embed,再 search_batch 找已有实体(exact match 或相似度 ≥0.95),否则新插入;payload 即 {data, entity_type, linked_memory_ids, user_id...} |
extract_entities_batch + 实体 upsert |
| Phase 8 | 存原始消息,返回 {"results": [...]} |
db.save_messages |
开源版 mem0 1.x 中不再有图数据库支持,entity store 也由向量数据库实现。知识实体并不会构建成知识图谱等形式,只用来反查 memory record,更像是基于实体的某种程度的倒排概念。
2.2 update 流程
公开 update(memory_id, text, metadata, expiration_date, data) → _update_memory:
vector_store.get(memory_id)取旧记忆;不存在抛ValueError。- 重组 payload:保留
created_at,刷新updated_at / hash / text_lemmatized;合并新 metadata(身份键user_id/agent_id/run_id等不可变,由_strip_identity_keys过滤)。 - re-embed 新文本 →
vector_store.update。 db.add_history(..., "UPDATE", ...)留痕。- 若文本变了:先
_remove_memory_from_entity_store(把该 memory_id 从所有相关实体的linked_memory_ids摘掉,列表空了就删实体),再_link_entities_for_memory对新文本重新抽实体并回链。
这一步保证「改了记忆内容,知识实体的链接也跟着更新」,是 entity store 与向量库一致性的关键收敛点。
2.3 search 流程(混合检索 + entity_boost)
入口 search(query, top_k=20, filters, threshold=0.1, rerank, explain, show_expired):
- 校验:
filters必含user_id/agent_id/run_id之一;threshold∈[0,1]、top_k≥0。 - 高级元数据算子
AND/OR/NOT/eq/ne/gt/in/contains/icontains/...经_process_metadata_filters翻译成向量库能吃的格式(含$or/$not透传)。 - 调
_search_vector_store——混合检索核心。
_search_vector_store 核心步骤:
- Step 1-2:
query做 BM25 lemmatize + embed。 - Step 3 语义召回:
vector_store.search,但 over-fetchinternal_limit = max(top_k*4, 60),留足打分池。 - Step 4 关键词召回:
vector_store.keyword_search(BM25)。不支持该能力的库(未覆写keyword_search)会在__init__里 warning 并退化为纯语义。 - Step 5:BM25 原始分用 logistic sigmoid 归一化到
[0,1],sigmoid 的midpoint/steepness随 query 词数自适应(见get_bm25_params)。 - Step 6:
entity_boosts(见下)。 - Step 7-8
score_and_rank加性打分:
combined = (semantic + bm25 + entity_boost) / max_possible,其中max_possible随启用的信号自适应(仅语义=1.0,+BM25=2.0,+entity=2.5)。threshold 先卡语义分,低于阈值的候选直接丢,再谈加总。 - Step 9:格式化成
MemoryItem,并把user_id/agent_id/role/actor_id/attributed_to/expiration_date等 promote 到顶层;explain=True时附带score_details(语义/BM25/entity 各项明细)。
entity_boost 详解(_compute_entity_boosts)——这正是平台文档里说的「Graph Memory powers retrieval」在 OSS 里的轻量实现:
- query 抽实体,上限 8 个。
- 批量 embed 这些实体文本。
- 对每个实体去 entity store 检索
top_k=500,保留相似度 ≥0.5 的命中。 - 命中实体的
linked_memory_ids里每一条记忆得到 boost:
boost = similarity * ENTITY_BOOST_WEIGHT(=0.5) * memory_count_weight,后者随「该实体关联的记忆数」轻微衰减,避免热门实体过度加权。 - 用
ThreadPoolExecutor(max_workers=4)并发多个实体的检索。(异步版对应方法名为_compute_entity_boosts_async,同样并发检索。)
举例:query 提到「Alice」,最多 500 个历史里提到 Alice 的记忆,都会因为共享实体而被选中。链接的 memory 条目数越多,memory_count_weight 越低,避免热词带来过大的权重。这个思想类似逆文档频率 idf,都是为了突出特征明显的词,降低过于通用的词的影响。
3. 商业版与开源(OSS)功能差异
3.1 仅商业版支持的 feature
| 仅在商业版支持的 feature | 证据来源(官方文档 / 代码) | 备注 |
|---|---|---|
Temporal Reasoning(时间推理):让记忆理解 last week / upcoming 这类相对时间短语,检索时按时间锚排序而非仅语义相似度。 |
代码:add(timestamp=...) / search(reference_date=...) 直接 raise;文档:platform-vs-oss.mdx、temporal-reasoning.mdx |
OSS 不支持相对时间解析;EPISODIC 因此仅占位(见 4.5) |
| Memory decay(渐进衰减):陈旧记忆自然降权但不硬删,作为硬 TTL 的补充。 | 代码:全路径无 decay 逻辑,仅 per-memory expiration_date(硬 TTL);文档:platform-vs-oss.mdx |
与硬 TTL 互补 |
| Organization / Project / 多租户:组织与项目级隔离、多租户管理。 | 代码:Memory.project / AsyncMemory.project 返回 _OSSProject / _AsyncOSSProject 桩,仅 update() |
OSS 只到 user/agent/run 维度 |
| Webhooks:记忆增删改时向外部系统推送事件回调。 | 文档:platform-vs-oss.mdx |
OSS 需自行实现 |
| Memory export(记忆导出):将记忆批量导出。 | 文档:同上 | OSS 需 DIY |
| Dashboard(可视化看板):记忆的图形化管理界面。 | 文档:同上 | OSS 无图形界面 |
| Built-in Analytics(内置分析):开箱即用的使用 / 统计看板。 | 文档:同上 | OSS 需 DIY |
| Auto-scaling / HA(自动扩缩容 / 高可用):托管环境的弹性与可用性保障。 | 文档:同上 | OSS 自托管需自己运维 |
| Graph Memory(原生零基础设施图谱):原生内置、零基础设施的知识图谱存储与可视化(typed relation)。 | 文档:features/graph-memory.mdx;代码佐证:Memory 主类无任何 neo4j/图库引用 |
商业版 = 原生内置;OSS 仅向量 entity store 轻量共现,无 typed relation |
| SEMANTIC / EPISODIC 三类记忆完整落地:三类记忆类型商业版全部可用。 | 代码:add 传 SEMANTIC/EPISODIC 即抛 Mem0ValidationError;文档:商业版支持全类型 |
仅 PROCEDURAL 在 OSS 落地;详见 4.5 |
3.2 平台增强型(OSS 有但能力受限)
- Custom categories:用产品自己的术语替换默认标签,让每条记忆的
categories标签贴合业务词表(平台为模型驱动的自动打标,OSS 需自己写分类器或靠 metadata 手工标)。平台 ✅ / OSS Limited(自定义分类维度弱)。 - Memory filters v2:平台 ✅ / OSS 经 metadata(OSS 已支持
AND/OR/NOT/eq/ne/...高级算子,见 2.3,但托管版 UI / 完整性更强)。
3.3 两者共享的能力(OSS 也有)
User & Agent 记忆、去重、语义检索、update、多语言 SDK、多模态、advanced retrieval、reranker、自定义 instructions、REST API、自托管可选 20+ 向量库 / 15+ LLM。
4. 建议的二次开发优化方向
4.1 chunking / 长文本分块(解决被悄悄截断)
- 现状痛点:OSS 的
add用parse_messages把整段对话直接拼成一段文本喂给 LLM 与 embed,没有按 token 预算分块。超长输入会被 LLM 自身上下文窗口截断,且截断是「静默」的——用户必须自己控制消息长度,否则超出部分直接丢失、没有任何报错或提示。 - 建议:
- 引入显式 Chunker:按 embedding / LLM 的 token 预算切分长对话 / 长文档,多块并行抽取再合并去重,避免长文本被悄悄截断。
- 支持常见的默认 Chunker 算法(定长 / 按句边界 / 递归字符 / 语义切分等)。
- 切分后每块独立走 V3 流水线,最后按 hash 去重合并,保证「长文档 = 多块记忆」而非「长文档被砍头」。
4.2 配置项硬编码收敛(魔法数不可配 → 可配化)
- 现状痛点:大量魔法数散落在代码里、写死不可配,调参必须改源码:已有记忆召回
top_k=10、最近消息last_k=10、entity_boost 实体上限8、语义 over-fetch 倍数×4、BM25 sigmoid 参数表(midpoint/steepness随 query 词数自适应)等。 - 建议:把这些常量收敛为
MemoryConfig的可配字段,例如retrieval_top_k、recent_messages_k、entity_boost_cap、hybrid_overfetch_multiplier、bm25_params,让用户 / 运维调参不必改源码、可针对业务做召回量与性能的权衡。
4.3 混合检索下沉数据库
- 现状:语义(向量)+ BM25(keyword)+ entity_boost 全在应用层 Python 合并打分(
score_and_rank)。一次 search 至少发两次查询(vector search + keyword_search),再在内存里归并;entity_boost 还要额外 N 次 entity store 检索。延迟与 CPU 随top_k放大,调权重要改代码。 - 下沉思路:若所选向量库原生支持 hybrid(如 pgvector + FTS、Elasticsearch / OpenSearch、Milvus hybrid),把 BM25 召回与融合(RRF 或加权)下推到 DB 一次返回。
- 下沉前 vs 下沉后对比:
维度 下沉前(应用层) 下沉后(DB-native) 网络往返 多次(语义 + 关键词 + N×实体) 单次 延迟 / 扩展性 随 top_k 放大,应用层归并吃 CPU DB 内融合,延迟更低更稳 可调性 / 可解释 高( explain能看到各项分;权重随意调)低(融合策略被 DB 绑定,RRF vs 加权不可自由选;explain 变粗) 跨库一致性 自管,逻辑统一 依赖具体 DB 的 hybrid 语义,跨库行为不一致 - 实务建议:保留应用层打分作为「可解释 / 可调」的默认路径,对延迟敏感场景提供
db_native_hybrid开关;或用「DB 召回候选 + 应用层轻量重排」折中。
4.4 结合 Ontology(本体)与知识图谱
- 现状短板:OSS 的 entity store 只做「实体 → 共享记忆」的 co-occurrence(共现)链接;平台 native graph 也明确不赋 typed relation(文档原话:不会记录「manages」这类带标签边,连接靠共现推断)。这导致:弱关系无法表达(「谁管理谁」「属于哪类」「时间约束」),多跳推理与可解释性受限。
- 为什么引入 Ontology:本体(类—属性—关系 schema)能把扁平实体升级成带类型的知识图谱,让「Alice 管理 Bob」「Acme 属于 SaaS 行业」这类事实显式化,支撑真正的多跳推理与可解释召回。
- 落地路径:
- 抽 relation:在抽实体时顺带抽
(source, relation, destination)。代码里已有雏形——mem0/memory/utils.py的sanitize_relationship_for_cypher/remove_spaces_from_entities就是为 Cypher 准备的,可直接接 Neo4j / Neptune / Apache AGE(与 pgvector 同库)。 - 换存储:把 entity store 从「向量 collection」换成真正的图存储。节点 = 实体(带
entity_type/ ontology class),边 = typed relation + co-occurrence 权重。 - 检索增强(知识图谱如何配合 search):在 mem0 现有 search 里,向量(语义)+ BM25(关键词)两路召回后,还有一路 entity_boost——它靠 entity store 的
linked_memory_ids(共现反查)给候选记忆加权。图谱把这一路从「单层共现」升级为「多层关系遍历」:- 第三路召回:query 先抽「实体 + 关系」→ 在图上做多跳遍历(如
(Alice)-[manages]->(?)拿到 Bob / Cara,再取它们关联的记忆),得到「图召回候选集」,与向量、BM25 并列成第三路召回。 - 融合方式:图召回集与向量语义集做 union,按来源归一化后并入
score_and_rank的combined;图路径越短权重越高(路径长度作衰减因子),关系类型可设优先级(如manages强于co_occurs)。逻辑上向后兼容——若只抽了共现没抽 relation,图退化为现有 entity_boost 行为。 - 典型搜索模式:「Alice 管理的人相关的事」→ 关系约束多跳;「属于 SaaS 行业的竞品动态」→ ontology class(类型)约束检索;「Alice 去年负责的项目」→ 图 + 时间窗过滤(呼应 3.1 temporal reasoning / 4.5.2)。
- 与现有管道衔接:现有 entity_boost 的「抽 query 实体 → 查
linked_memory_ids→ 加权」流程不变,linked_memory_ids的来源从向量共现换成图遍历结果;若 4.3 混合检索下沉到 DB,可在库内统一融合后再送 reranker 重排。
- 第三路召回:query 先抽「实体 + 关系」→ 在图上做多跳遍历(如
- 本体约束(SHACL 校验):前三步让图"有类型",但 LLM 抽 relation 不可靠——可能把
manages指到一家 Industry 节点、漏抽必填属性、或凭空造关系。SHACL(Shapes Constraint Language,W3C 2017 推荐标准) 是给 RDF 知识图谱做"模式校验"的标准语言,角色相当于 JSON Schema 之于 JSON、XSD 之于 XML——只不过它校验的是图(RDF)数据。一个 shape(约束形状) 描述「某类节点必须满足哪些约束」:sh:minCount/sh:maxCount(必填与基数)、sh:datatype(值类型,如 string/date)、sh:class(值域,规定某属性指向的必须是 Person 还是 Industry)、以及sh:closed(封闭 shape,禁止意料外的属性)。- 怎么接进抽取 / 写入流程:为 ontology 每个类定义 shape,例如
PersonShape规定manages的sh:class必须是Person、OrgShape规定belongsTo的sh:class必须是Industry。LLM 抽出(s, r, o)三元组后、写入图之前跑一次 SHACL 校验:违例(类型错配、缺必填、值域越界)的三元组直接丢弃或回炉让 LLM 重抽,等于给抽取层加一道廉价护栏,把 LLM 的噪声档在写入前。这比"抽了就写、写错再修"成本低得多。 - 注意区分校验与推理(修正原表述):SHACL 的本职是"写入时保证结构合法"(约束 / 校验);而"传递性、对称性"这类自动推理(如由
A 管理 B、B 管理 C推得A 管理 C)是 OWL 或规则层的职责,不是 SHACL 能做的。本路线里分工应为:SHACL 做写时 guard(挡脏数据),OWL / 规则做读时推理(补隐含关系),两者不要混为一谈。
- 怎么接进抽取 / 写入流程:为 ontology 每个类定义 shape,例如
- 抽 relation:在抽实体时顺带抽
- 收益与风险:收益是实体中心 / 多跳问题召回更准、更可解释;风险是「向量 + 图」双写的一致性 / 事务要处理好
4.5 完善 SEMANTIC / EPISODIC 等 MemoryType 实现与 decay 策略
4.5.1 理论来源:这套分类直接来自认知心理学(附引用)
mem0 的 MemoryType(SEMANTIC / EPISODIC / PROCEDURAL) 借用了认知科学对长时记忆的经典三分法。要点与原始文献:
- Episodic vs Semantic(情景 vs 语义) —— Tulving (1972) 在 Organization of Memory 书章中首次提出两者区分:语义记忆是「关于世界的一般知识(事实、概念、词)」,情景记忆是「特定时间-地点锚定的个人经历」。[1] 他在 Tulving (1983) Elements of Episodic Memory 进一步细化,并引入现象学维度:情景记忆伴随 autonoetic(自我觉知) 意识、语义记忆伴随 noetic(知道) 意识 [2];Tulving (1985) 专文讨论 memory & consciousness [3]。
- Declarative vs Procedural(陈述性 vs 程序性) —— Cohen & Squire (1980) 通过遗忘症病人实验提出「knowing that(陈述 / 事实)」与「knowing how(程序 / 技能)」的解离:病人学不会新事实,却能正常习得镜像阅读等技能 [4]。术语上 "procedural" 借自 AI(Winograd 1972; Anderson 1982/1983 的 ACT* 把知识先以陈述形式表示、再经 编译(compilation) 转成程序性步骤 [5][6])。
- Nondeclarative 总括 —— Squire & Zola-Morgan (1988, 1991) 把程序性记忆、习惯、知觉启动、经典条件化等归到「nondeclarative(非陈述性)」大伞下,与陈述性记忆(= 语义 + 情景)并列 [7][8];Squire & Zola (1996) 在 PNAS 给出完整脑系统分类图 [9]。
| 认知科学概念 | mem0 MemoryType |
含义 | 代码落地 |
|---|---|---|---|
| 语义记忆 (Tulving 1972) | SEMANTIC |
事实 / 偏好 | 占位(默认 untyped 路径内容近似) |
| 情景记忆 (Tulving 1972) | EPISODIC |
事件 / 时间线 | 占位(需 temporal reasoning,平台独占) |
| 程序性记忆 (Cohen & Squire 1980) | PROCEDURAL |
操作 / 技能 | 唯一实现 |
mem0 把认知科学的「语义 / 情景 / 程序性」三元组直接搬成了枚举,但工程上只实现了 PROCEDURAL 和 NONE, SEMANTIC 和 EPISODIC 只保留了枚举值占位。。
4.5.2 mem0 与 Hermes 相似的记忆分类
Hermes(Nous Research,开源 Agent 框架)同样采用了 Episodic / Semantic / Procedural 三分法,且其命名与 mem0 的枚举一一对应——说明两者都源自同一套认知科学框架。但 Hermes 把三类全部真正落地,并叠加了「自进化」闭环 [10][11]。
Hermes 的三类记忆:
| 类型 | 存什么 | 保留策略 | 检索方式 |
|---|---|---|---|
| Episodic | 原始会话日志(发生了什么、何时、顺序) | 30 天完整保真,之后压缩(聚类 + 轻模型摘要,省约 90% 存储) | 最近度 + 语义相似度 |
| Semantic | 从情景里抽出的压缩事实(名字、决策、偏好、关系) | 永久、不自动删 | 关键词 + 语义搜索 |
| Procedural | 学到的模式(哪些做法有效 / 失败、用户偏好怎么干活) | 永久,由自反思循环持续更新 | 任务开始时按任务类型匹配 |
与 mem0 的核心区别:
| 维度 | mem0 (OSS) | Hermes |
|---|---|---|
| 三类是否都实现 | 仅 PROCEDURAL 落地;SEMANTIC/EPISODIC 占位即报错 |
三类均真实落地,各有独立 store / 保留 / 检索策略 |
| Episodic 怎么处理 | 未实现 | 原始日志 + 30 天后压缩生命周期 |
| Semantic 怎么来 | 未实现 | 显式从情景抽取事实入库 |
| Procedural 怎么来 | 一次 LLM 抽取执行历史摘要 | 自反思(reflection)循环持续沉淀「哪些做法有效」,并接入 GEPA 离线进化引擎迭代技能 |
| 时间衰减 / decay | 仅硬 TTL(expiration_date),无渐进衰减 |
Episodic 有显式压缩生命周期;Semantic/Procedural 永久、靠反思更新而非衰减 |
| 自进化 | 无(只负责记忆,不负责反思) | 有(反思 → 抽象 → 技能沉淀 → 评估进化,GEPA 闭环) |
| 记忆定位 | 本身是「记忆层」,常被别的 Agent 当 backend | 自身是 Agent 框架,且把 mem0 列为可插拔外部记忆后端之一(Tier 3 External Provider)[12] |
| 周边层次 | 单一向量 store + entity store + SQLite 历史 | 还叠加 working / session 层(短期缓冲、会话全文搜索)与身份层(SOUL.md) |
4.5.3 落地思路:三种记忆类型各需要什么核心能力
下面不谈「怎么改代码」,而是把每类记忆装的是什么、为什么难、缺了会怎样讲清楚——先确认要解决的问题,再谈工程落地才有方向。
① SEMANTIC(语义记忆:关于用户与世界的一般事实)
- 属于这类记忆的例子:
- 身份事实:「用户叫李雷,是某电商公司的算法工程师」
- 稳定偏好:「用户偏好用 Python、讨厌 Java」「用户摘要习惯控制在 3 条以内、要点式」
- 领域知识:「项目代号 Atlas,目标是做推荐系统」「用户团队用 Scrum、双周迭代」
- 持久状态:「用户对乳糖不耐受」「用户家住上海」
- 需要的核心能力:
- 事实抽取与归一化:从对话里抽「是什么」,存成统一的事实表述而非原句,并做去重合并(「我住上海」和「我在上海生活」应归并)。
- 稳定度判断:区分「一次性随口一说」和「长期成立的事实」——只有后者才进语义层,避免把临时上下文当永久知识。
- 冲突消解:新事实与旧事实矛盾时以新覆盖旧(「我从北京搬到了上海」要更新居住地,而不是新增一条)。
- 跨会话持久召回:不依赖当前对话上下文即可被语义检索命中。
- 为什么需要这些能力:语义记忆是 Agent「认识用户」的基础——每次开新对话都要知道用户是谁、喜欢什么,但不该每次重新问。它的难点不在「存」,而在「抽得准、合并对、冲突处理得对」,这正是 OSS 默认 untyped 路径已经在做、只是没显式打
SEMANTIC标签的那部分。落地思路是承认它是什么:给既有事实记忆补上类型标识,让检索 / 过滤能按语义维度区分,而不是和情景、程序混在一起。
② EPISODIC(情景记忆:带时间锚的个人经历)
- 属于这类记忆的例子:
- 「2025-03-10 用户完成 Q1 复盘会,决定砍掉 A 功能」(带绝对时间锚的事件)
- 「上周用户和供应商大吵一架,至今没和解」(相对时间 + 近期事件)
- 「用户上周刚离职,现在是自由职业」(状态变更事件)
- 「用户昨天提了上线事故,被记了 P0」(具体事件)
- 「用户说下周二要去体检」(未来计划事件)
- 「三年前用户创业失败」(远时间事件)
- 需要的核心能力:
- 时间锚定:每条事件记录「何时发生」,而非「何时写入 Mem0」——回填历史时尤其关键。
- 相对时间解析:「上周」「下个月」「去年」要能映射到绝对时间锚点,否则检索不可复现。
- 时间窗检索与排序:「最近发生了什么」「去年同期」要按时间线而非纯语义相似度返回。
- 事件 vs 事实的回流:事件本身是情景,但事件里抽出的稳定事实(如「用户离职了」)应回流到语义层,避免重复存。
- 为什么需要这些能力:情景记忆回答「当时发生了什么、什么顺序」,这是语义记忆覆盖不了的。用户问「上次吵架后你们和好了吗」,要的是带时间线的事件,而不是一个孤立事实。它的难点在于时间锚 + 相对时间 + 时间窗排序这一整套机制——这正是平台 Temporal Reasoning 独占、OSS 完全缺失的部分(见 3.1)。OSS 若想自建,本质是把这层时间感知在本地重建一遍:写入时带事件时间,检索时按时间窗排序加权。
③ PROCEDURAL(程序性记忆:怎么做的技能与规程)
- 属于这类记忆的例子:
- 工作流偏好:「用户喜欢先把 PR 描述写好再写代码」
- 操作序列:「调用某内部 API 要先拿 token 再调用,顺序不能反」
- 流程规程:「用户团队发版前必须跑回归并通知 QA」
- 技能步骤:「写 SQL 时用户要求先 EXPLAIN 再看执行计划」
- 故障套路:「遇到 CI 失败,用户习惯先 git stash 再 rebase」
- 需要的核心能力:
- 操作序列抽取:从执行历史里抽象出「先 A 后 B」的可复用步骤,而非存单次对话片段。
- 场景匹配:新任务来了,判断「这次该套哪条规程」——按任务类型 / 上下文匹配,而不是无差别套用。
- 持续更新:规程被证明有效或失败后要做改写,而不是一次性写入就不变。
- 与 agent 场景绑定:procedural 通常出现在带
agent_id的「代用户执行」场景,而非纯聊天——普通用户对话不应走这条,避免把「用户事实」误标成「操作过程」。
- 为什么需要这些能力:程序性记忆是「knowing how」,它让 Agent 越用越顺手——同样的活第二次不用重新教。它和语义的区别在于:语义是「用户是什么样的人」(静态事实),程序性是「遇到 X 该怎么做」(动态技能)。难点在于「从执行历史抽象出可复用步骤」以及「规程要随反馈迭代」——OSS 目前只用一次 LLM 抽取历史摘要,缺少持续反思更新(对照 Hermes 的 reflection 闭环,见 4.5.2)。
④ decay(软降权,补硬删除的盲区)
开源版现有只有 expiration_date 这种到点硬删。更贴近真实记忆的思路是引入时间衰减——陈旧记忆自然降权但保留,越常被访问越保活。关键判断是类型决定该不该衰减:事实 / 事件会过时(用户偏好、旧项目状态),宜衰减;操作规程长期有效,通常不该衰减。把衰减作为一个加权信号融入现有打分即可,不必另起一套排序。
附:核心模块清单(按职责,便于回查)
mem0/memory/main.py——Memory/AsyncMemory主类,add/search/update/delete写入与检索流水线mem0/memory/utils.py——_strip_identity_keys、Cypher 关系抽取雏形(sanitize_relationship_for_cypher/remove_spaces_from_entities)mem0/utils/scoring.py——score_and_rank/ BM25 sigmoid 归一化 /ENTITY_BOOST_WEIGHT/get_bm25_paramsmem0/utils/entity_extraction.py—— spaCy 实体抽取(PROPER / QUOTED / TOPIC / IDENTIFIER)mem0/configs/base.py——MemoryConfig/MemoryItemmem0/configs/enums.py——MemoryType(SEMANTIC / EPISODIC / PROCEDURAL)mem0/configs/prompts.py——ADDITIVE_EXTRACTION_PROMPT/PROCEDURAL_MEMORY_SYSTEM_PROMPT- 文档 ——
docs/platform/platform-vs-oss.mdx(平台 vs 开源对照)、docs/platform/features/graph-memory.mdx、docs/platform/features/temporal-reasoning.mdx
附:参考
Tulving, E. (1972). Episodic and semantic memory. In E. Tulving & W. Donaldson (Eds.), Organization of Memory (pp. 381–403). Academic Press. ↩︎
Tulving, E. (1983). Elements of Episodic Memory. Oxford University Press. ↩︎
Tulving, E. (1985). Memory and consciousness. Canadian Psychology, 26(1), 1–12. ↩︎
Cohen, N. J., & Squire, L. R. (1980). Preserved learning and retention of pattern-analyzing skill in amnesia: Dissociation of "knowing how" and "knowing that". Science, 210(4466), 207–210. ↩︎
Anderson, J. R. (1982). Acquisition of cognitive skill. Psychological Review, 89(4), 369–406. ↩︎
Anderson, J. R. (1983). The Architecture of Cognition. Harvard University Press. ↩︎
Squire, L. R., & Zola-Morgan, S. (1988). Memory: Brain systems and behavior. Trends in Neurosciences, 11(4), 170–175. ↩︎
Squire, L. R., & Zola-Morgan, S. (1991). Structure and function of declarative and nondeclarative memory systems. In Principles of Neural Science (3rd ed.). (亦见 Squire & Zola, 1996, PNAS 93(24):13515–13522.) ↩︎
Squire, L. R., & Zola, S. (1996). Structure and function of declarative and nondeclarative memory systems. PNAS, 93(24), 13515–13522. ↩︎
Hermes Persistent Memory Architecture — Episodes, Facts & Compression. https://openclawdatabase.com/hermes/memory (访问 2026-07-24) ↩︎
How Hermes Agent Memory Works — 3-Layer System Explained. https://hermes-agent.ai/blog/hermes-agent-memory-system (访问 2026-07-24) ↩︎
Hermes 三层记忆 + 外部 Provider(含 mem0)拆解. https://sotasync.com/reader/2026-05-14-hermes-agent-masterclass (访问 2026-07-24) ↩︎

浙公网安备 33010602011771号