Mem0 源码阅读笔记

Mem0 源码阅读笔记

版本:mem0 main 分支,commit dd5f7e39


1. 核心概念与大致架构

mem0_arch

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 → 抽取/生成用的 LLM
  • RerankerFactory(可选)→ 召回后重排
  • SQLiteManager → 历史库

1.2 一条 memory 的数据模型

MemoryItem(configs/base.py)与向量库 payload 对应:

id, memory(data), hash, score, created_at, updated_at, metadata

写入时 payload 还会塞进:text_lemmatized(BM25 用)、roleactor_iduser_id/agent_id/run_id、可选 attributed_toexpiration_date 等。

1.3 「entity」在 mem0 里有两重含义(务必区分)

  1. 作用域 ID(scoping entity)user_id / agent_id / run_id
    它们是分区键,不是从文本里抽出来的。add() / search()filters 必须至少带其中一个,所有读写都按它隔离数据。代码里 _build_filters_and_metadata 就是用它构造隔离条件。
  2. 知识实体 + 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() 关键步骤:

  1. 校验:memory_type 只允许传 PROCEDURAL,传 SEMANTIC/EPISODIC 直接抛 Mem0ValidationError。不传则当作通用事实记忆。
  2. 归一化 messages(str/dict/list[dict] 三种入参),构造 processed_metadataeffective_filters(来自 user_id/agent_id/run_id)。
  3. agent_id + PROCEDURAL → 走 _create_procedural_memory(agent 操作型记忆)。
  4. 否则进入核心:_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_tolinked_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

  1. vector_store.get(memory_id) 取旧记忆;不存在抛 ValueError
  2. 重组 payload:保留 created_at,刷新 updated_at / hash / text_lemmatized;合并新 metadata(身份键 user_id/agent_id/run_id 等不可变,由 _strip_identity_keys 过滤)。
  3. re-embed 新文本 → vector_store.update
  4. db.add_history(..., "UPDATE", ...) 留痕。
  5. 若文本变了:先 _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)

  1. 校验:filters 必含 user_id/agent_id/run_id 之一;threshold∈[0,1]top_k≥0
  2. 高级元数据算子 AND/OR/NOT/eq/ne/gt/in/contains/icontains/..._process_metadata_filters 翻译成向量库能吃的格式(含 $or/$not 透传)。
  3. _search_vector_store——混合检索核心

_search_vector_store 核心步骤

  • Step 1-2:query 做 BM25 lemmatize + embed。
  • Step 3 语义召回vector_store.search,但 over-fetch internal_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 里的轻量实现:

  1. query 抽实体,上限 8 个
  2. 批量 embed 这些实体文本。
  3. 对每个实体去 entity store 检索 top_k=500,保留相似度 ≥0.5 的命中。
  4. 命中实体的 linked_memory_ids 里每一条记忆得到 boost:
    boost = similarity * ENTITY_BOOST_WEIGHT(=0.5) * memory_count_weight,后者随「该实体关联的记忆数」轻微衰减,避免热门实体过度加权。
  5. 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.mdxtemporal-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 三类记忆完整落地:三类记忆类型商业版全部可用。 代码addSEMANTIC/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 的 addparse_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_krecent_messages_kentity_boost_caphybrid_overfetch_multiplierbm25_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 + FTSElasticsearch / OpenSearchMilvus 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 行业」这类事实显式化,支撑真正的多跳推理与可解释召回。
  • 落地路径
    1. 抽 relation:在抽实体时顺带抽 (source, relation, destination)。代码里已有雏形——mem0/memory/utils.pysanitize_relationship_for_cypher / remove_spaces_from_entities 就是为 Cypher 准备的,可直接接 Neo4j / Neptune / Apache AGE(与 pgvector 同库)
    2. 换存储:把 entity store 从「向量 collection」换成真正的图存储。节点 = 实体(带 entity_type / ontology class),边 = typed relation + co-occurrence 权重。
    3. 检索增强(知识图谱如何配合 search):在 mem0 现有 search 里,向量(语义)+ BM25(关键词)两路召回后,还有一路 entity_boost——它靠 entity store 的 linked_memory_ids(共现反查)给候选记忆加权。图谱把这一路从「单层共现」升级为「多层关系遍历」:
      • 第三路召回:query 先抽「实体 + 关系」→ 在图上做多跳遍历(如 (Alice)-[manages]->(?) 拿到 Bob / Cara,再取它们关联的记忆),得到「图召回候选集」,与向量、BM25 并列成第三路召回。
      • 融合方式:图召回集与向量语义集做 union,按来源归一化后并入 score_and_rankcombined;图路径越短权重越高(路径长度作衰减因子),关系类型可设优先级(如 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 重排。
    4. 本体约束(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 规定 managessh:class 必须是 PersonOrgShape 规定 belongsTosh:class 必须是 Industry。LLM 抽出 (s, r, o) 三元组后、写入图之前跑一次 SHACL 校验:违例(类型错配、缺必填、值域越界)的三元组直接丢弃或回炉让 LLM 重抽,等于给抽取层加一道廉价护栏,把 LLM 的噪声档在写入前。这比"抽了就写、写错再修"成本低得多。
      • 注意区分校验与推理(修正原表述):SHACL 的本职是"写入时保证结构合法"(约束 / 校验);而"传递性、对称性"这类自动推理(如由 A 管理 B、B 管理 C 推得 A 管理 C)是 OWL 或规则层的职责,不是 SHACL 能做的。本路线里分工应为:SHACL 做写时 guard(挡脏数据),OWL / 规则做读时推理(补隐含关系),两者不要混为一谈。
  • 收益与风险:收益是实体中心 / 多跳问题召回更准、更可解释;风险是「向量 + 图」双写的一致性 / 事务要处理好

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、双周迭代」
    • 持久状态:「用户对乳糖不耐受」「用户家住上海」
  • 需要的核心能力
    1. 事实抽取与归一化:从对话里抽「是什么」,存成统一的事实表述而非原句,并做去重合并(「我住上海」和「我在上海生活」应归并)。
    2. 稳定度判断:区分「一次性随口一说」和「长期成立的事实」——只有后者才进语义层,避免把临时上下文当永久知识。
    3. 冲突消解:新事实与旧事实矛盾时以新覆盖旧(「我从北京搬到了上海」要更新居住地,而不是新增一条)。
    4. 跨会话持久召回:不依赖当前对话上下文即可被语义检索命中。
  • 为什么需要这些能力:语义记忆是 Agent「认识用户」的基础——每次开新对话都要知道用户是谁、喜欢什么,但不该每次重新问。它的难点不在「存」,而在「抽得准、合并对、冲突处理得对」,这正是 OSS 默认 untyped 路径已经在做、只是没显式打 SEMANTIC 标签的那部分。落地思路是承认它是什么:给既有事实记忆补上类型标识,让检索 / 过滤能按语义维度区分,而不是和情景、程序混在一起。

② EPISODIC(情景记忆:带时间锚的个人经历)

  • 属于这类记忆的例子
    • 「2025-03-10 用户完成 Q1 复盘会,决定砍掉 A 功能」(带绝对时间锚的事件)
    • 「上周用户和供应商大吵一架,至今没和解」(相对时间 + 近期事件)
    • 「用户上周刚离职,现在是自由职业」(状态变更事件)
    • 「用户昨天提了上线事故,被记了 P0」(具体事件)
    • 「用户说下周二要去体检」(未来计划事件)
    • 「三年前用户创业失败」(远时间事件)
  • 需要的核心能力
    1. 时间锚定:每条事件记录「何时发生」,而非「何时写入 Mem0」——回填历史时尤其关键。
    2. 相对时间解析:「上周」「下个月」「去年」要能映射到绝对时间锚点,否则检索不可复现。
    3. 时间窗检索与排序:「最近发生了什么」「去年同期」要按时间线而非纯语义相似度返回。
    4. 事件 vs 事实的回流:事件本身是情景,但事件里抽出的稳定事实(如「用户离职了」)应回流到语义层,避免重复存。
  • 为什么需要这些能力:情景记忆回答「当时发生了什么、什么顺序」,这是语义记忆覆盖不了的。用户问「上次吵架后你们和好了吗」,要的是带时间线的事件,而不是一个孤立事实。它的难点在于时间锚 + 相对时间 + 时间窗排序这一整套机制——这正是平台 Temporal Reasoning 独占、OSS 完全缺失的部分(见 3.1)。OSS 若想自建,本质是把这层时间感知在本地重建一遍:写入时带事件时间,检索时按时间窗排序加权。

③ PROCEDURAL(程序性记忆:怎么做的技能与规程)

  • 属于这类记忆的例子
    • 工作流偏好:「用户喜欢先把 PR 描述写好再写代码」
    • 操作序列:「调用某内部 API 要先拿 token 再调用,顺序不能反」
    • 流程规程:「用户团队发版前必须跑回归并通知 QA」
    • 技能步骤:「写 SQL 时用户要求先 EXPLAIN 再看执行计划」
    • 故障套路:「遇到 CI 失败,用户习惯先 git stash 再 rebase」
  • 需要的核心能力
    1. 操作序列抽取:从执行历史里抽象出「先 A 后 B」的可复用步骤,而非存单次对话片段。
    2. 场景匹配:新任务来了,判断「这次该套哪条规程」——按任务类型 / 上下文匹配,而不是无差别套用。
    3. 持续更新:规程被证明有效或失败后要做改写,而不是一次性写入就不变。
    4. 与 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_params
  • mem0/utils/entity_extraction.py —— spaCy 实体抽取(PROPER / QUOTED / TOPIC / IDENTIFIER)
  • mem0/configs/base.py —— MemoryConfig / MemoryItem
  • mem0/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.mdxdocs/platform/features/temporal-reasoning.mdx

附:参考


  1. Tulving, E. (1972). Episodic and semantic memory. In E. Tulving & W. Donaldson (Eds.), Organization of Memory (pp. 381–403). Academic Press. ↩︎

  2. Tulving, E. (1983). Elements of Episodic Memory. Oxford University Press. ↩︎

  3. Tulving, E. (1985). Memory and consciousness. Canadian Psychology, 26(1), 1–12. ↩︎

  4. 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. ↩︎

  5. Anderson, J. R. (1982). Acquisition of cognitive skill. Psychological Review, 89(4), 369–406. ↩︎

  6. Anderson, J. R. (1983). The Architecture of Cognition. Harvard University Press. ↩︎

  7. Squire, L. R., & Zola-Morgan, S. (1988). Memory: Brain systems and behavior. Trends in Neurosciences, 11(4), 170–175. ↩︎

  8. 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.) ↩︎

  9. Squire, L. R., & Zola, S. (1996). Structure and function of declarative and nondeclarative memory systems. PNAS, 93(24), 13515–13522. ↩︎

  10. Hermes Persistent Memory Architecture — Episodes, Facts & Compression. https://openclawdatabase.com/hermes/memory (访问 2026-07-24) ↩︎

  11. How Hermes Agent Memory Works — 3-Layer System Explained. https://hermes-agent.ai/blog/hermes-agent-memory-system (访问 2026-07-24) ↩︎

  12. Hermes 三层记忆 + 外部 Provider(含 mem0)拆解. https://sotasync.com/reader/2026-05-14-hermes-agent-masterclass (访问 2026-07-24) ↩︎

posted @ 2026-07-25 21:58  Lhfcws  阅读(4)  评论(0)    收藏  举报