跳转到主要内容
Agent 记忆系统

Agent 记忆系统学习笔记(二):从 Zep 看懂时间图谱记忆

AI 辅助写的

用 Zep / Graphiti 作为第二个样本,理解 Context Graph、episode、fact、valid_at / invalid_at、Context Block 和时间图谱召回链路。

写在前面:为什么第二篇看 Zep

上一篇我用 Mem0 拆了一遍 Agent 记忆系统的基本链路:对话如何被抽取成 memory,memory 如何被存储,未来又如何通过语义、关键词、实体、时间、衰减和 rerank 找回来。

这一篇换一个视角:如果记忆不是一条条事实,而是一张会随时间变化的图,会发生什么?

这就是 Zep 最值得分析的地方。它的核心不是“给 memory 加一个图增强”,而是把用户、业务数据和 Agent 工作记录组织成一张 temporal Context Graph。在这张图里,实体是节点,事实和关系是边,原始数据是 episode,边上还带着事实何时开始有效、何时失效的时间信息。

所以这篇不是 Zep 接入教程,而是继续这个系列的学习目标:用 Zep 作为样本,理解时间图谱记忆这条技术路线。


一、Zep 的核心问题:长期记忆为什么需要图

在 Mem0 那篇里,我把 Mem0 理解成一个“记忆排序系统”。它的基本单元是 memory fact:一条被抽取出来、未来可以召回的事实。

但有些问题用单条 fact 很难表达清楚。

比如:

用户原来在 A 公司。
用户后来加入 B 公司。
用户和 Alice 一起负责 Q1 路线图。
Alice 后来转去负责定价项目。
Q1 路线图因为预算问题延期。

如果只是把这些都存成独立 memory,当然也能搜。但真正的难点是:这些事实之间有关系,而且关系会随时间变化。

你问:

用户现在和 Alice 还在同一个项目吗?

这不是单纯的语义相似问题。系统需要知道:

用户是谁
Alice 是谁
他们之前有什么关系
这个关系从什么时候开始
是否已经结束
Q1 路线图和定价项目分别是什么
哪个事实代表当前状态

Zep 的答案是:不要只存一堆 memory,而是构建一张带时间的 Context Graph。

Zep 的核心位置:从消息到 Context Graph,再到 Context Block
Zep 不只是保存 memory fact,而是为每个用户或对象维护一张时间知识图谱,并把检索结果组装成可直接放进 prompt 的上下文。
输入数据
聊天消息、业务 JSON、文本、历史记录
Context Graph
实体、关系、事实、episodes、时间有效性
Context Block
用户摘要 + 相关事实 + 其他上下文,可直接注入 LLM

从这个角度看,Zep 不是单纯的 memory store,而是一个把交互数据、业务数据和历史状态持续编织成图的系统。


二、Zep 的核心抽象:Context Graph

Zep 文档里反复提到一个概念:Context Graph

可以把它理解成某个用户、对象或业务主体的一张时间知识图谱。

这张图里主要有三类东西:

Zep 的图数据模型
Context Graph 里最核心的不是 memory 文本,而是实体节点、事实边和原始 episode。
Entity Nodes
人、组织、地点、产品、项目、概念;节点上维护实体摘要。
Entity Edges / Facts
实体之间的关系;fact 存在边上,并带 valid_at / invalid_at。
Episodes
原始消息、文本、JSON;保留来源证据,支持引用和追溯。

Entity Nodes:实体节点

节点代表实体,比如用户、公司、地点、项目、产品、账号、概念。

和普通图谱不同的是,Zep 的实体节点不只是一个名字。它还会维护围绕这个实体的叙事摘要,比如这个用户是谁、这个项目发生了什么、这个账号有哪些历史状态。

Entity Edges / Facts:事实边

边代表两个实体之间的关系,而 fact 存在边上。

比如:

Emily 的账号因为支付失败被暂停。

这里可能有两个实体:

Emily 的账号
支付失败

关系边上保存事实:

User account Emily0e62 has a suspended status due to payment failure.

更重要的是,这条 fact 不是一个静态字符串,它带时间属性。

Episodes:原始证据

Episode 是开发者交给 Zep 的原始数据:聊天消息、文本片段、JSON 记录、邮件、工单、CRM 数据等。

这点很重要。Zep 并不是抽取完 fact 就把原始数据扔掉。Episode 会被保留,作为事实背后的证据。以后如果 Agent 需要引用原话、找上下文、追溯来源,就可以回到 episode。

所以 Zep 的结构不是:

对话 → memory

而更像:

episode → entities + facts + summaries + observations → context block

三、写入链路:episode 如何变成时间图谱

Zep 支持几种数据入口。

一种是聊天消息:

thread.add_messages

另一种是业务数据:

graph.add(message / text / JSON)

这说明 Zep 的目标不只是“记住聊天”,还包括把业务系统里的动态数据也放进同一张图。

Zep 写入链路:episode 如何变成时间图谱
Zep 的写入重点不是把对话压成一条 memory,而是从 episode 中抽取实体、关系和带时间生命周期的 fact。
01
添加数据
thread.add_messages 或 graph.add,输入 message / text / JSON
02
保存 Episode
原始数据保留为证据,不因抽取结果而丢失
03
抽取实体和关系
识别人、项目、组织、状态、动作和关系
04
写入 Fact Edge
把精确事实作为边属性写入图谱
05
设置时间属性
created_at、valid_at、invalid_at、expired_at
06
更新图结构
新事实加入,过时事实被 invalidated,但历史保留

这里最关键的概念是 episode。

每次你往 Zep 里加一段消息、文本或 JSON,它都会先成为一个 episode。然后系统从 episode 里抽取实体、关系和事实,并把它们写进 Context Graph。

举个例子,输入一条业务 JSON:

{
  "event": "payment_failed",
  "account": "Emily0e62",
  "reason": "Card expired",
  "amount": 99.99,
  "created_at": "2024-09-15T00:00:00Z"
}

Zep 可能从里面抽出:

账号 Emily0e62 发生了一笔 99.99 的失败交易。
失败原因是 Card expired。
这个事件发生在 2024-09-15。

这些不是简单存在一个列表里,而会进入图:账号、交易、失败原因可能成为实体或关系的一部分,事实被挂在边上,时间字段也进入事实生命周期。

这就是 Zep 和普通 memory fact 系统的第一个大差异:Zep 从源头上把记忆当成图结构来生成,而不是事后给 memory 做实体连接。


四、Zep 的时间模型:事实不是覆盖,而是失效

Zep 最值得学习的设计,是它对事实变化的处理。

很多系统遇到状态变化时会做 update:

旧事实:用户喜欢 Adidas
新事实:用户喜欢 Puma

然后旧事实被覆盖。

但 Zep 的思路不是简单覆盖,而是让事实带生命周期。

Fact 的时间生命周期
Zep 最重要的设计之一:事实不是简单覆盖,而是在边上记录什么时候有效、什么时候失效。
旧事实
Kendra 喜欢 Adidas 鞋
事实失效
Adidas 鞋坏了,旧偏好不再代表当前状态
新事实
Kendra 喜欢 Puma 鞋;旧事实保留 invalid_at
created_at
Zep 何时学到这个事实
valid_at
事实在现实中何时开始为真
invalid_at
事实在现实中何时不再为真
expired_at
Zep 何时知道该事实失效

Zep 的 fact 边上有四个时间字段:

时间字段含义
created_atZep 什么时候知道这个事实
valid_at这个事实在现实中什么时候开始为真
invalid_at这个事实在现实中什么时候不再为真
expired_atZep 什么时候知道这个事实失效

这个设计非常重要,因为它区分了两个时间:

现实中什么时候发生
系统什么时候知道

比如用户 6 月 1 日搬家,但 6 月 10 日才告诉 Agent。那么:

valid_at   = 6 月 1 日
created_at = 6 月 10 日

如果 9 月 1 日又搬走,但 9 月 5 日才告诉 Agent:

invalid_at = 9 月 1 日
expired_at = 9 月 5 日

这比“最新事实覆盖旧事实”细得多。它让系统可以回答:

用户现在住在哪里?
用户 8 月时住在哪里?
系统是什么时候知道用户搬家的?

这也是 Zep / Graphiti 路线最有价值的地方:它不是只记住事实,而是记住事实的时间边界。


五、召回链路:Zep 怎么把图谱变成上下文

Zep 的召回和 Mem0 很不一样。

Mem0 更像:

query → 搜 memory → 返回 top memories → 应用注入 prompt

Zep 更像:

query 或最近消息 → 搜 Context Graph → 选择多种 context type → 组装 Context Block → 直接放进 prompt
Zep 召回链路:从图谱搜索到 prompt-ready context
Zep 的召回不是只搜 facts;auto search 会跨 facts、entities、episodes、observations、thread summaries 做全局选择。
当前 thread 最近消息
默认 Context Block 会用最近对话判断相关性
User Graph / Graph
在用户图或独立图中检索
Context Block
按字符预算打包,可直接放入 prompt
底层信号
Semantic Similarity
用 embedding 找概念相关内容
BM25 Full-text
用关键词匹配精确术语和名称
Graph / BFS
可用图连接和起点节点影响相关性
Cross-scope Rerank
auto search 跨多种 context type 全局重排
Temporal Filters
按 valid_at、invalid_at 等时间字段过滤 edge
Character Budget
控制 Context Block 大小,避免 prompt 膨胀

Zep 有两种常见使用方式。

第一种是高层方法:

thread.get_user_context()

它会根据当前 thread 最近几条消息,在整个 User Graph 里找相关上下文,然后返回一个 prompt-ready 的 Context Block。

第二种是低层方法:

graph.search()

你可以指定要搜 facts、entities、episodes、observations、thread summaries,或者使用 scope="auto" 让 Zep 自己跨类型选择并组装上下文。

这背后用到几类信号:

semantic similarity:语义相似
BM25 full-text:关键词精确匹配
Breadth-first search:从指定节点出发做图连接相关性
RRF / MMR / cross encoder / node distance 等 reranker
时间过滤:created_at、valid_at、invalid_at、expired_at

所以 Zep 的召回不是简单“图遍历”,而是混合检索:语义、关键词、图距离、时间过滤、跨类型 rerank 一起工作。


六、Context Types:Zep 召回的不只是事实

Zep 还有一个很值得学习的点:它把上下文拆成多种形态。

Zep 召回的不只是 facts,而是多种 Context Types
不同问题需要不同形态的上下文:精确事实、实体摘要、原始证据、线程摘要、长期观察。
Facts
带时间范围的精确关系,适合回答具体事实。
Entities
实体节点及其叙事摘要,适合理解某个人或项目。
Episodes
原始消息、文本、JSON,适合引用和追溯。
Thread Summaries
单个会话的增量摘要,适合恢复过去会话。
Observations
跨多个事实形成的稳定模式、承诺、状态转变。
User Summary
用户的基线画像,默认 Context Block 总会包含。

这比“只返回 memory list”更细。

Facts:精确事实

适合回答具体问题,比如:

用户账号为什么被暂停?
用户什么时候开始使用某个工具?
某个项目的决定是什么?

Facts 的优势是精确,而且有时间范围。

Entities:实体摘要

适合回答围绕某个实体的问题,比如:

我们知道 Alice 的哪些信息?
这个项目过去发生过什么?
这个账号的整体状态是什么?

实体摘要不是某一条 fact,而是围绕一个节点的叙事。

Episodes:原始证据

适合需要原话和来源的时候。

比如 Agent 不能只说“用户投诉过登录问题”,它还需要引用当时用户怎么说的,就应该回到 episode。

Thread Summaries:会话摘要

适合恢复某个历史会话。

它回答的是:这条 thread 里发生过什么,而不是全局用户画像。

Observations:跨事实形成的稳定模式

这是 Zep 比较有意思的一层。Observation 不是单条 fact,而是从多个 facts 和 entities 中总结出的稳定模式、承诺、决策或状态变化。

比如:

用户经常在账单失败后需要人工支持。
用户对支付可靠性非常敏感。
某个项目持续被预算问题影响。

这种信息如果只看单条 fact,会显得很碎;但作为 observation,就能表达“长期模式”。

User Summary:用户基线画像

Zep 的默认 Context Block 会包含 user summary。它提供一个用户的长期基线画像,让 Agent 不用每次都从零理解用户是谁。

这套 context type 的拆分,说明 Zep 的目标不是简单召回,而是做 context assembly:根据当前问题选择合适形态的上下文。


七、Context Block:Zep 不只是返回检索结果

Zep 的一个关键产品化设计是 Context Block。

它不是让你拿到一堆搜索结果后自己拼 prompt,而是直接返回一个可以塞进系统提示词的字符串。

典型结构类似:

<USER_SUMMARY>
用户是谁、长期偏好是什么、当前账户状态如何……
</USER_SUMMARY>

<FACTS>
- 某条相关事实。(Date range: from - to)
- 某条相关事实。(Date range: from - to)
</FACTS>

这件事很重要,因为很多 memory 系统只解决“搜到什么”,但没有解决“怎么把搜到的东西喂给模型”。

而 Zep 直接把召回和 prompt 组装连在一起:

检索结果 → context selection → 格式化 → 字符预算控制 → Context Block

这对应用开发者很友好,但也有取舍:默认 Context Block 更方便,定制性则需要通过 context template 或 advanced context construction 来做。

我理解它的定位是:

Zep 不只是 graph retrieval,而是面向 Agent 的上下文工程系统。


八、和 Mem0 的关键差异

看完 Zep,再回头看 Mem0,两者差异会很清楚。

Mem0 和 Zep 的核心差异
两者都做长期记忆,但一个更像多信号 memory ranking,一个更像时间知识图谱和上下文组装。
Mem0
核心单元
memory fact
图的角色
实体连接作为检索排序 boost
召回目标
返回最相关 memories,再注入 prompt
Zep / Graphiti
核心单元
temporal Context Graph
图的角色
实体、关系、事实和时间生命周期是主结构
召回目标
组装 prompt-ready Context Block
维度Mem0Zep / Graphiti
核心抽象memory facttemporal Context Graph
图的角色实体连接,用于 ranking boost图是主要记忆结构
时间处理retrieval ranking 中的时间信号fact 边自带 valid_at / invalid_at
原始证据memory 和历史记录episode 是一等对象
召回结果memoriesContext Block / facts / entities / episodes 等
更适合个性化偏好、长期事实、轻量 memory 层关系变化、状态演化、多源业务数据、时间查询

简单说:

Mem0 更像一个产品化 memory ranking layer。
Zep 更像一个 temporal graph + context assembly layer。

这不是谁更好,而是抽象层不同。

如果你的应用主要是记用户偏好、历史任务、简单事实,Mem0 的 mental model 更直接。

如果你的应用里有大量实体关系、业务事件、状态变化、跨系统数据,Zep 的图谱模型会更自然。


九、我对 Zep 的判断

我觉得 Zep 最值得学习的不是“用了知识图谱”,而是它把长期记忆里的几个难题都放进了图模型:

第一,事实不是孤立的,而是实体之间的关系。

第二,事实不是永远为真,而是有时间边界。

第三,原始数据不能丢,episode 要作为证据保留。

第四,召回不是单一结果类型,而是按任务选择 facts、entities、episodes、observations、summary。

第五,最终目标不是返回数据库记录,而是组装出 LLM 能直接使用的 Context Block。

它适合的场景是:

客户支持:账号、工单、支付、历史问题持续变化
销售 / CRM:人、公司、机会、会议、承诺之间关系复杂
医疗 / 教育:用户状态和偏好随时间演化
企业 Agent:业务系统 JSON、文档、聊天记录要汇入同一上下文
长周期项目管理:项目状态、负责人、决策和风险不断变化

它不一定适合的场景是:

只需要轻量用户偏好记忆
不需要关系建模和时间有效性
希望完全自托管且不想接入托管服务
需要自己控制每个图数据库细节

另外要注意一点:Zep 是商业托管产品,Graphiti 是它背后的开源 temporal knowledge graph 框架。研究技术路线时可以把 Zep 当成“产品化图谱记忆”,把 Graphiti 当成“开源图谱引擎”。如果后面要自建,Graphiti 会是更值得深入看的部分。


十、这一篇之后,我的分析框架更新了

上一篇 Mem0 让我看到一个 memory layer 需要回答:

怎么抽取 memory?
怎么多信号召回?
怎么排序?
怎么注入上下文?

这一篇 Zep 让我加上几个新问题:

这个系统有没有显式实体和关系?
事实是否有时间生命周期?
原始证据是否被保留?
它返回的是 memory list,还是 prompt-ready context?
它能否表达跨事实的长期模式?

所以,到目前为止,这个系列里可以形成一个初步判断:

轻量长期记忆看 memory ranking;复杂长期记忆看 temporal graph;真正面向 Agent 的系统,最后都要走向 context assembly。

下一篇我会看 Letta / MemGPT。它和 Mem0、Zep 又不一样:它关注的是 Agent 如何主动管理自己的 core memory 和 archival memory。


参考资料

评论

目录

Agent 记忆系统学习笔记(二):从 Zep 看懂时间图谱记忆 | Yu的赛博工位