Zep - Temporal Knowledge Graph Memory · essay
03. Zep:用时序知识图谱做 Agent Memory
论文:ZEP: A Temporal Knowledge Graph Architecture for Agent Memory
链接:https://arxiv.org/pdf/2501.13956
代码:https://github.com/getzep/zep
本地原文:
![[papers/03-Zep-2501.13956.pdf]]
论文定位与阅读路径
核心定位
Zep 把 Agent 记忆从“文本片段列表”升级成“带时间的知识图谱”,特别适合处理用户事实变化、关系变化和长期会话。
原文结构导读
| 原文章节 | 读法 | 你要抓住的工程问题 |
|---|---|---|
| 1 Introduction | 看问题定义 | 为什么普通向量记忆难处理变化事实 |
| 2 Knowledge Graph Construction | 精读,论文核心 | 会话如何变成图结构 |
| 2.1 Episodes | 看原始事件层 | 为什么不能直接丢掉原始会话证据 |
| 2.2 Entities and Facts | 看实体/关系抽取 | 节点、边、事实三者如何拆分 |
| 2.2.3 Temporal Extraction and Edge Invalidation | 重点 | 事实何时生效、何时失效,怎样避免旧事实污染 |
| 2.3 Communities | 看图聚合 | 多个事实如何形成更高层主题 |
| 3 Memory Retrieval | 精读 | search + reranker 如何从图里取证据 |
| 4 Experiments | 看 Deep Memory Retrieval 和 LongMemEval | 图记忆是否比片段记忆更能处理长历史 |
| Appendix Prompts | 复现时再读 | LLM 抽取实体、事实、时间的 prompt 边界 |
Zep 的阅读重点是“证据链可追溯”。一条回答最好能回到具体 episode,再回到抽取出的 entity/fact/temporal edge。否则图谱看起来高级,实际只是把向量库换了个名字。
证据链阅读
| 证据点 | 读什么 | 工程判断 |
|---|---|---|
| Graph construction | episode、entity、fact、community 如何生成 | 构图过程是否可追溯、可重跑 |
| Temporal invalidation | 旧事实如何失效而不是被删除 | 解决长期记忆里最常见的“过时事实污染” |
| Deep Memory Retrieval | 图检索是否改善深层历史查询 | 判断图结构是否带来真实召回收益 |
| LongMemEval 对比 | 与 MemGPT/长记忆 baseline 的差异 | 看 Zep 强在事实变化还是长文本压缩 |
研究背景与问题定义
为什么需要图谱
向量库擅长找“语义相似文本”,但不擅长表达关系。
比如:
代码块收起展开
小明现在住在广州。
小明以前住在深圳。
小明是小红的同学。
小红喜欢摄影。这些信息如果只是文本,很难回答:
- 小明现在住哪里?
- 小明和小红是什么关系?
- 小明以前的住址是否还有效?
知识图谱会把它们变成节点和边:
graph TD
A[小明] -->|现在住在| B[广州]
A -->|曾经住在| C[深圳]
A -->|同学| D[小红]
D -->|喜欢| E[摄影]
方法框架与核心机制
核心结构
Zep 的记忆大致分三类:
| 记忆 | 工程解释 |
|---|---|
| Episodic Memory | 某次对话或事件发生了什么 |
| Semantic Memory | 抽取出的事实和关系 |
| Community Memory | 图中一组相关节点形成的主题社区 |
工作流程
flowchart TD
A[会话消息] --> B[抽取实体和关系]
B --> C[写入时序图谱]
C --> D[检测冲突和重复]
D --> E[旧边失效或降权]
F[用户新问题] --> G[混合检索]
G --> H[语义相似度]
G --> I[BM25关键词]
G --> J[图遍历]
H --> K[Rerank]
I --> K
J --> K
K --> L[构造成上下文]
关键概念
1. Temporal
Temporal 就是“带时间”。
记忆不是永远正确的,事实会变。
例如:
代码块收起展开
2024: 用户在深圳工作
2026: 用户在广州读书如果没有时间和失效机制,Agent 可能把旧事实当成当前事实。
2. Edge Invalidation
当新事实和旧事实冲突时,不一定物理删除旧边,而是把旧边标记为失效。
这很像数据库里的软删除:
代码块收起展开
valid_from, valid_to, is_active3. Hybrid Search
Zep 不只用 embedding,还组合:
- 语义相似度。
- BM25 关键词。
- 图遍历。
- reranker。
这很工程化,因为单一检索经常不稳。
概念校准
向量库像“按意思找相似笔记”。知识图谱像“人物关系表 + 时间轴”。如果你要做一个真的长期认识你的 Agent,只靠相似文本不够,它还需要知道事实之间的关系和时效性。
工程映射与实践启发
工程落地方案
Java 后端可以这样拆:
| 模块 | 作用 |
|---|---|
EntityExtractor | 从对话中抽实体 |
RelationExtractor | 抽取实体关系 |
GraphMemoryStore | 保存节点和边 |
TemporalValidator | 判断新旧事实冲突 |
HybridRetriever | 向量 + BM25 + 图搜索 |
ContextConstructor | 把图结果转成 prompt 文本 |
表结构可以先简化:
代码块收起展开
memory_node(id, user_id, name, type)
memory_edge(id, user_id, from_node, to_node, relation, valid_from, valid_to, active)相关工作与方法对比
它和 Mem0 的关系
Mem0-G 也走图增强记忆路线。Zep 的特色更强调时序知识图谱和图社区。
| 维度 | Zep | Mem0 |
|---|---|---|
| 核心 | 时序知识图谱 | 生产级长期记忆框架 |
| 强项 | 关系、时间、冲突 | 记忆抽取、更新、API 化 |
| 适合 | 用户事实变化、复杂关系 | 工程接入和产品化 |
局限、对比与阅读检查
局限与边界
- 图谱构建依赖抽取质量。
- LLM 抽实体和关系有成本。
- 图数据库和向量检索结合增加系统复杂度。
- 冲突判断不总是简单。
阅读重点
- 它如何把对话转成图。
- 它如何处理时间和事实失效。
- 它如何混合检索。
- 它如何把图结果构造成上下文。
- 它和普通向量库记忆相比强在哪里。
实践启发
如果你的 Agent 需要长期记住“人、项目、偏好、关系、时间变化”,就不要只做 memory_text + embedding。至少要考虑:
- 实体。
- 关系。
- 时间。
- active/invalid 状态。
- 多路召回。
原文精读补充
1. 为什么 Zep 强调 Temporal
普通知识图谱可能只表达:
代码块收起展开
UserA -> uses -> FrameworkA但真实世界里的事实有时间:
代码块收起展开
2026-07-04:用户 A 从 FrameworkA 项目转向 Agent 学习。
2026-07-05:用户 A 要求 Agent Memory 论文逐篇拆解。如果没有时间维度,Agent 会把旧事实、新事实、阶段性偏好混在一起。
2. 三层图结构
Zep/Graphiti 的核心可以按三层理解:
| 层 | 存什么 | 类比 |
|---|---|---|
| Episodic | 原始事件、消息、JSON、交互片段 | 日记 |
| Semantic | 从事件抽取出的实体和关系 | 知识图谱 |
| Community | 高连接实体的聚类摘要 | 章节总结 |
这比单纯向量库强在:它能把“发生过什么”和“抽象事实是什么”同时保留。
3. 边的失效比删除更重要
长期记忆最容易遇到冲突:
代码块收起展开
用户以前住在 A 城。
用户现在搬到 B 城。如果直接删除旧事实,会丢失历史;如果两个都 active,会答错。
Zep 的启发是:
代码块收起展开
保留历史事实,但给关系加有效期/失效标记。后端里这通常对应:
代码块收起展开
valid_from, valid_to, invalidated_at, invalidated_by4. 检索为什么要混合
Zep 的检索不是单一路线。你可以理解成:
- 语义向量找相似内容。
- BM25 找关键词强匹配。
- 图遍历找相关实体关系。
- Reranker 决定最终上下文。
这比单一 embedding 更适合长期记忆,因为很多问题是实体关系问题,不只是文本相似问题。
5. 实验怎么看
重点看 LongMemEval 这类长期记忆 benchmark。你读时问:
- 问题是否需要跨会话回忆。
- 是否涉及时间变化。
- 图结构是否比纯向量检索更准。
- 构图成本和查询延迟是否值得。
6. 工程落地提醒
如果以后做 Obsidian Agent,不要一开始就把所有关系都塞进复杂图数据库。可以先做轻量结构:
代码块收起展开
entity(name, type)
memory_event(time, source, content)
memory_relation(subject, predicate, object, valid_from, valid_to)先把时间和失效机制做对,比盲目上知识图谱更重要。
深度精读:Zep/Graphiti 的关键是“时间化的事实图”
1. 为什么企业记忆需要 temporal graph
企业或长期个人场景中,事实经常变化:
代码块收起展开
用户以前在项目 A。
用户现在切到项目 B。
某 API 过去用 v1,现在迁移到 v2。
某人以前负责后端,现在转去算法。普通向量库只会存多段文本,很难判断哪个事实当前有效。Temporal KG 的价值是把事实表示成:
代码块收起展开
(subject, relation, object, valid_from, valid_to, source)这样系统可以回答:
代码块收起展开
现在是什么?
当时是什么?
这个事实从什么时候开始有效?
哪个新事实让旧事实失效?这就是 Zep 比普通 memory store 更强的地方。
2. Episodic -> Semantic -> Community 的流水线
Zep 的三层对应记忆抽象层级:
代码块收起展开
Episodic: 原始事件
Semantic: 从事件抽出来的事实关系
Community: 高层主题/群组摘要例子:
代码块收起展开
Episodic:
2026-07-05 用户说“论文讲解太短,要更深入”
Semantic:
UserA -> prefers -> deep paper explanations
UserA -> dislikes -> shallow summaries
Community:
AI Agent 学习偏好:要求原文支撑、逐章拆解、工程落地这比直接把用户原话放向量库更适合长期使用。
3. 构图为什么需要 LLM
从文本到图,需要做几件事:
- 识别实体。
- 识别关系。
- 判断关系类型。
- 提取时间。
- 判断是否与旧边冲突。
长期对话很口语,LLM 更灵活。但风险也大:
| 风险 | 后果 |
|---|---|
| 实体抽错 | 图节点污染 |
| 关系抽错 | 检索走错路径 |
| 时间抽错 | 旧事实被误判有效 |
| 冲突判断错 | 新旧事实同时存在 |
所以生产系统必须保留 source episode,方便回查。
4. 边失效机制比删除高级
假设:
代码块收起展开
2025:用户使用 MySQL。
2026:用户项目迁移到 PostgreSQL。删除 MySQL 这条边会丢历史;保留两条 active 又会答错。正确做法:
代码块收起展开
UserA -> uses -> DatabaseA valid_to = 2026-xx-xx
UserA -> uses -> DatabaseB valid_from = 2026-xx-xx这对学习记录也有用:
代码块收起展开
“当前在学后端框架” 后来会变成 “当前转向 AI Agent 工程”5. 检索不是“图 or 向量”,而是混合
长期记忆问题有多种形态:
| 问题 | 更适合的召回 |
|---|---|
| “我之前说过什么?” | 语义向量 |
| “某个项目现在用什么数据库?” | 实体图 |
| “上周做了哪些任务?” | 时间过滤 |
| “和 MCP 有关的笔记有哪些?” | 关键词 + 图邻居 |
一个工程版检索可以是:
代码块收起展开
candidate = vector_search(query)
+ keyword_search(query)
+ graph_expand(entities)
+ time_filter(range)
rerank(candidate)6. Constructor 为什么重要
图检索返回的是节点和边,但 LLM 需要读文本。所以要有 constructor 把结构化结果变成上下文:
代码块收起展开
Entity: UserA
Current relation: studies -> AI Agent
Evidence: 2026-07-04 started backend-to-Agent route
Invalidated relation: studies -> backend framework only如果 constructor 写得差,检索再准,模型也可能读不懂。
7. 工程最小版本
不需要一开始上 Neo4j。可以用关系表模拟:
代码块收起展开
memory_episode(
id, -- episode ID,对应一次对话、事件或资料片段。
source, -- 来源,例如 chat、note、tool_result。
content, -- 原始事件文本。
happened_at -- 事件发生时间,不一定等于写入时间。
);
memory_entity(
id, -- 实体 ID。
name, -- 实体名称,例如人名、地点、项目名。
type -- 实体类型,用于过滤和消歧。
);
memory_edge(
id, -- 关系边 ID。
subject_entity_id, -- 主体实体。
predicate, -- 关系谓词,例如 lives_in、works_on。
object_entity_id, -- 客体实体。
source_episode_id, -- 这条关系来自哪次 episode。
valid_from, -- 关系开始生效时间。
valid_to, -- 关系失效时间;为空表示当前仍有效。
confidence, -- 抽取可信度。
status -- active / superseded / deleted 等状态。
);等关系复杂后,再迁移图数据库。
8. 读实验时的真实关注点
Zep 这类系统分数高时,你还要问:
- 构图成本是多少。
- 每次写入是否都要调 LLM。
- 查询延迟能否接受。
- 错误记忆如何人工修正。
- 多租户权限如何隔离。
这是开发岗读论文必须补的工程问题。
论文精读主线:问题-方法-验证-局限-启发
1. 核心结论
Zep/Graphiti 把长期记忆建成带时间属性的知识图谱,让 Agent 不只保存事实,还能表示事实何时成立、何时失效、被什么新事实取代。
本篇的阅读重心是:长期记忆中的事实关系必须带时间和证据,否则旧事实会污染当前回答。读完后要能说清它改变的是 runtime、存储、检索、更新还是评测层;否则只是记住了论文名。
2. 问题背景
向量记忆能找到相似文本,但难以表达实体关系和事实变化。用户偏好、项目状态、学习方向都会随时间变化;如果旧事实和新事实同权存在,长期记忆越多越容易答错。
把问题具体化为失败样例:用户项目从 A 技术栈迁到 B 技术栈后,向量库仍召回旧记录;系统无法判断哪个事实现在有效。用这个样例反推论文动机,比泛泛说“上下文不够”或“记忆不准”更有判断力。
3. 核心方法
系统从 episode 中抽取实体和关系,写入 temporal knowledge graph。关系边带 valid_from、valid_to、evidence 等字段。检索时结合图搜索、向量搜索和关键词搜索,既找语义相似片段,也沿实体关系找最新事实。
flowchart TD
E[Episode 原始事件] --> X[实体/关系抽取]
X --> G[Temporal KG]
G --> V[有效期与失效边]
Q[查询] --> H[Hybrid Search]
H --> G
H --> I[向量/关键词索引]
G --> R[带时间和证据的结果]
方法部分按数据流读:episode 保存原始证据,抽取实体和关系,写入带 valid_from/valid_to 的边,检索时结合图路径和最新有效边。每一步都要能标出输入、输出、控制点和失败后该记录的日志。
4. 技术细节
技术重点是边的生命周期。普通图只说 A 和 B 有关系;Zep 还要记录关系从何时开始、是否仍有效、由哪条 episode 支持、被哪条新事实失效。这样才能同时支持“当前是什么”和“过去发生过什么”。
代码块收起展开
final class TemporalGraphMemoryService {
private final EpisodeStore episodeStore; // 保存原始对话/事件,作为所有图事实的证据源
private final EntityResolver entityResolver; // 实体消歧,判断 AI Agent、Agent 工程等是否同一概念
private final RelationWriter relationWriter; // 写入带 validFrom/validTo 的关系边,并处理旧边失效
private final HybridRetriever hybridRetriever; // 融合图扩展、向量相似和关键词精确匹配
MemoryResult handle(MemoryInput input) {
// 1. 保存原始对话/事件,作为所有图事实的证据源
// 2. 实体消歧,判断 AI Agent、Agent 工程等是否同一概念
// 3. 写入带 validFrom/validTo 的关系边,并处理旧边失效
// 4. 融合图扩展、向量相似和关键词精确匹配
// 返回结果时必须带 evidence、confidence、traceId,方便以后追查记忆为什么生效。
return MemoryResult.withTrace(input.traceId());
}
}字段检查重点是 entityId、predicate、validFrom、validTo、supersededBy、episodeId 是时序图谱的核心。这些字段不是装饰,它们决定后续能否检索、审计、回滚、限权和做离线评测。
5. 实验验证
验证要分层看:实体抽取是否准,关系边是否准,失效机制是否返回最新事实,最终回答是否因此更对。只看最终 QA 分数不够,因为提升可能来自更强 prompt 或数据集实体关系天然清晰。
实验阅读重点:不要只看 QA 分数;要单独看实体消歧、关系抽取、事实失效、时间查询四个环节。如果论文没有直接给出这层证据,复现时就要自己补机制级指标。
6. 局限与启发
构图成本高,LLM 抽取错误会结构化放大。启发是 Obsidian Agent 可以先对少量关键实体建图:用户、项目、笔记、主题、偏好、任务;不要一开始把所有文本都强行图谱化。
迁移到你的项目时,结论是:Obsidian Agent 可以先为用户、项目、技术栈、任务建立小图;不必把所有笔记一次性图谱化。先把边界做对,再追求更复杂的模型或更大的记忆规模。
7. 关键术语表
| 术语 | 在论文里的意思 | 工程落地时要看什么 |
|---|---|---|
| Temporal KG | 带时间属性的知识图谱 | 节点、边、有效期、证据源 |
| Episode | 原始事件或对话片段 | 图事实的可追溯证据 |
| Edge invalidation | 旧关系失效机制 | valid_to、invalidated_by 字段 |
| Hybrid search | 多检索方式融合 | 图、向量、BM25 互补 |
8. 复现难点
实体消歧是最大坑,合并错会污染图,不合并会证据分散。关系 schema 也要受控,否则 predicate 会膨胀。失效判断要区分覆盖、补充、阶段变化和冲突待确认。
复现入口应从失败集开始:构造“旧偏好被新偏好覆盖”“项目路径变更”“同名实体歧义”三类 case,检查返回的是当前事实还是历史事实。只有先证明旧方案会坏,才知道新机制是否真的解决了问题。
9. 代码框架建议
小规模先用关系表模拟图:entity_node、relation_edge、episode、edge_evidence。写入时先生成 GraphMutation,再 normalize,再 apply;不要让 LLM 直接写数据库。
代码验收门槛:LLM 只能提出 GraphMutation,真正写库前要做 schema 约束、实体归一化和冲突确认。这条门槛应写进测试或 trace 字段里,避免实现只停留在 demo。
10. 相关论文对比
Zep 和 Mem0 都关心生产记忆,Mem0 强调操作决策,Zep 强调时序事实图。A-MEM 更像卡片网络,HippoRAG 更偏多跳检索,Zep 更适合用户事实随时间变化的场景。
横向比较时抓这个角度:Zep 强在事实演化;A-MEM 强在记忆卡片链接;HippoRAG 强在图路径辅助召回。比较对象不是谁“更先进”,而是谁负责哪一层失败。
11. 批判性思考
结构化事实看起来更可信,也更危险。一条错误边会影响很多查询。必须保留证据、时间、置信度和撤销能力,否则图谱会把模型猜测伪装成数据库事实。
批判边界:图谱会给错误事实披上结构化外衣;没有证据、置信度和撤销日志的边不能进入核心记忆。这不是否定论文,而是把论文结论放回真实系统的成本、权限和恢复约束里。