静态 RAG 把记忆当成不变的文本块,但用户的地址、职位、偏好都会变。Zep 用 Graphiti 把对话拆成实体与事实,给每条边记两套时间——世界何时为真、系统何时知道。冲突时旧事实只失效、不删除。代价是构图阶段大量 LLM 调用。
普通 RAG 隐含三个假设:语料基本不变、相关性等于语义相似、事实一旦写入就长期有效。Agent 记忆三条都撞上——对话在持续发生,业务数据在更新,用户会搬家、换工作、改偏好。
Zep 的做法是把记忆做成时序知识图谱:底层 episode 存原始消息,中间层存实体和事实边,顶层存社区摘要。每条事实边带四个时间戳,分属两条轴:T 是世界时间,T′ 是系统摄入时间。
新事实与旧事实矛盾时,旧边不删除:只把 t_invalid 写成新边的 t_valid,把 t′_expired 写成新边的 t′_created。历史可查,当前状态也清楚。
检索不是单纯向量相似:cosine + BM25 + 图上的 BFS,再重排,最后把事实与时间范围拼成上下文。代价也很明确:单会话助理类问题反而下降 17.7%,结构化业务数据的联合推理没有基准验证。
先建立一个共同认知:记忆的难点不是「找不到」,而是「不知道哪条现在还算数」。
普通 RAG 把文档切成块、向量化、按相似度取回。这套流程隐含三个假设:语料基本不变;相关性等于语义相似;一条事实一旦写入就长期有效。企业 Agent 三条都撞上——对话在持续发生,业务数据在更新,用户的地址、职位、偏好会变。
向量库里同时躺着「User 住在 Boston」和「User 住在 NYC」。两句话语义高度相似,都会被召回;但只有一条是当前事实。没有有效区间,模型只能靠上下文顺序猜,或者把两条都写进 prompt,让 LLM 自己判断。对话越长,这种冲突越多。
另一条路是把全部历史塞进上下文。LongMemEval 的平均对话长度约 115,000 token;论文的 full-context 基线在 gpt-4o 上延迟 28.9 秒。成本之外,长上下文还会稀释关键信息。于是问题变成:如何在不丢历史的前提下,让每条事实带上「何时为真」和「系统何时知道」。
这两个时间不必重合。用户可能 6 月就搬了家,Zep 7 月才从对话里得知;这段时间里,世界时间已经翻页,系统时间还停在上一页。如果系统只存一个「当前值」,它既说不清自己当时知道什么,也无法在事后审计「为什么当时答错了」。
两套方案各解决了一半:一个管上下文,一个管结构;时间维度都缺席。
主上下文之外再开一层 archival storage,靠函数调用在两层之间换页。优点是能在有限窗口里维持长期对话,DMR 上用 gpt-4-turbo 达到 93.4%。
卡在哪:记忆单元仍是文本,检索靠相似度或摘要。事实没有显式的有效区间;新信息与旧信息冲突时,也没有一条边级的失效规则。
从语料抽实体和关系,做社区检测,再用 map-reduce 生成社区摘要。优点是能回答「这批文档整体在讲什么」这类全局问题。
卡在哪:它为静态语料设计:图构建一次,社区用 Leiden 划分,事实边不带双时态,也不处理「新事实让旧事实失效」。语料一变,图谱就要重建。
普通向量 RAG 更直接:块 + 余弦相似度。它连实体都没有,更谈不上时间。三者共同的缺口是:事实的生命周期没有被建模。Zep 的切入点不是「再加一个检索器」,而是把「失效」变成图上一次显式的写操作。
Zep 的核心不是「用了知识图谱」,而是把「失效」变成一次写操作,而不是删除。
论文把时间拆成两条轴。T 是事件在现实世界发生的顺序;T′ 是 Zep 摄入数据的顺序,也就是数据库的审计轨迹。每条事实边同时带四个时间戳:
t_valid:事实开始为真的时刻。
t_invalid:事实不再为真的时刻。
回答「用户现在住哪」用它。
t′_created:这条边写入 Zep 的时刻。
t′_expired:这条边在 Zep 里被标记失效的时刻。
回答「系统当时知道什么」用它。
两者不必重合。用户可能 6 月就搬了家,Zep 7 月才从对话里得知;这段时间里,世界时间已经翻页,系统时间还停在上一页。论文的边失效规则只做两件事:
具体做法是:LLM 把新边与语义相关的旧边逐条比对,找出时间上重叠且互相矛盾的对;确认矛盾后,旧边的 t_invalid 被设为新边的 t_valid。在系统时间轴 T′ 上,旧边的 t′_expired 也在新边写入时被填上。结果是一条边从「无终点」变成「有终点」,历史仍然可查。
这也解释了为什么 Zep 要保留原始 episode:语义边会失效,但原始消息不该被改写。查询时既可以看当前有效的事实,也可以沿双向索引回到当时的原话。
下面用一组示意事实演示这套规则。切换 T 与 T′,拖动时间轴;再点「注入新事实」,观察旧边如何被写上终点。虚线表示这条边还没有进入图谱。
建议按这个顺序试:
t_invalid 和 t′_expired。不是。Boston → NYC、Coffee → Tea 的具体月份是为演示构造的,论文只给出规则,没有给出这组例子。规则本身取自论文:新边与语义相关的旧边比对,时间重叠且矛盾时,旧边的 t_invalid 被设为新边的 t_valid;系统时间轴上,旧边的 t′_expired 在新边写入时被填上;旧边保留。
模拟把「系统收到新事实」简化成一个按钮,省略了实体消解、事实抽取和 LLM 矛盾判断。真实系统里,这些步骤都可能出错:实体没对齐、关系抽错、日期解析错,都会让失效规则作用在错误的边上。
Zep 没有训练阶段;它的成本主要花在把每条消息变成实体、事实和失效判断。
一条消息进入 Zep 后,构图按固定流水线走:
n = 4 条消息一起送进 LLM,提供两轮对话上下文;说话人自动成为实体;再用一次 reflexion 式反思降低漏抽和幻觉,并生成实体摘要。1024 维向量,先做余弦检索,再对名称和摘要做全文检索;候选交给 LLM 判断是否同一实体,并生成更新后的名称与摘要。t_ref 解析绝对和相对日期(如「下周四」「两周前」);新边与语义相关旧边比对,时间重叠且矛盾时触发上面的失效规则。工程上几个刻意的选择:写图用预定义 Cypher 查询,而不是让 LLM 生成查询,保证 schema 一致;社区用 label propagation 是为了支持增量更新,代价是社区会逐渐偏离完整重算的结果,所以仍需要周期性全量刷新;构图模型用 gpt-4o-mini,嵌入和重排用 BGE-m3。
实体抽取、反思、消解、事实抽取、矛盾判断、社区摘要,每一步都是 LLM 调用。论文没有给出每条消息的调用次数或 token 成本,也没有做消融;因此无法从论文数字里拆出「双时态」单独贡献了多少。能确认的是:这是一套推理时的构图系统,不是训练出来的记忆模型。
这也决定了它的扩展性讨论方式不同于训练模型:瓶颈不在 GPU 显存,而在摄入吞吐、LLM 调用成本和图数据库写入。论文把延迟作为一等指标报告,但没有给出千万级用户下的成本曲线。
架构解决「记忆放哪」,检索解决「这一次该拿什么」。
三层各司其职:episode 存原始消息,保证可溯源;semantic 存实体和事实边,边上带四个时间戳;community 存强连通实体的摘要,负责全局性问题。三者不是三份互相独立的存储,而是同一张图 G = (N, E, φ) 的三个子图。
余弦相似度 + Okapi BM25 全文检索 + n 跳 BFS。三者分别抓语义相似、词面匹配、图上的上下文接近;返回事实边、实体、社区三类候选。
支持 RRF、MMR、episode-mentions(按提及频率加权)、node-distance(按到中心节点的距离)、cross-encoder(最贵)。
边输出事实 + t_valid/t_invalid;实体输出名称 + 摘要;社区输出摘要。时间范围会一起进入上下文。
实验里只取了 top 20 条边和实体(DMR 用 top 10)。这也是论文承认的边界:episode 与实体之间的双向索引没有被评估,检索系统的完整能力只验证了一部分。
DMR 已经饱和,真正有信息量的是 LongMemEval 的分题型结果。
| 记忆方案 | 模型 | 准确率 |
|---|---|---|
| Recursive Summarization† | gpt-4-turbo | 35.3% |
| Conversation Summaries | gpt-4-turbo | 78.6% |
| MemGPT† | gpt-4-turbo | 93.4% |
| Full-conversation | gpt-4-turbo | 94.4% |
| Zep | gpt-4-turbo | 94.8% |
| Conversation Summaries | gpt-4o-mini | 88.0% |
| Full-conversation | gpt-4o-mini | 98.0% |
| Zep | gpt-4o-mini | 98.2% |
† 为 MemGPT 论文报告的结果。Zep 在 gpt-4-turbo 上 94.8%,比 MemGPT 的 93.4% 高 1.4 个百分点,也只比 full-conversation 的 94.4% 高 0.4 个百分点。每段对话只有 60 条消息,塞得进上下文窗口;作者自己说这个基准区分度不足。
| 记忆方案 | 模型 | 准确率 | 延迟 | 延迟 IQR | 平均上下文 |
|---|---|---|---|---|---|
| Full-context | gpt-4o-mini | 55.4% | 31.3 s | 8.76 s | 115k |
| Zep | gpt-4o-mini | 63.8% | 3.20 s | 1.31 s | 1.6k |
| Full-context | gpt-4o | 60.2% | 28.9 s | 6.01 s | 115k |
| Zep | gpt-4o | 71.2% | 2.58 s | 0.684 s | 1.6k |
gpt-4o 上 60.2% → 71.2%,论文称 18.5% 提升;gpt-4o-mini 上 55.4% → 63.8%,论文称 15.2%。延迟从 28.9 s 降到 2.58 s(约 90%),平均上下文从 115k 降到 1.6k。注意口径:18.5% 是论文的相对提升;按表中数字,绝对差是 11.0 个百分点。
| 问题类型 | gpt-4o-mini | gpt-4o | ||
|---|---|---|---|---|
| Full-context | Zep | Full-context | Zep | |
| single-session-preference | 30.0% | 53.3% | 20.0% | 56.7% |
| single-session-assistant | 81.8% | 75.0% | 94.6% | 80.4% |
| temporal-reasoning | 36.5% | 54.1% | 45.1% | 62.4% |
| multi-session | 40.6% | 47.4% | 44.3% | 57.9% |
| knowledge-update | 76.9% | 74.4% | 78.2% | 83.3% |
| single-session-user | 81.4% | 92.9% | 81.4% | 92.9% |
几个值得注意的点:
45.1% → 62.4%(+38.4%);single-session-preference 在 gpt-4o 上 20.0% → 56.7%(+184%)。这正是双时态设计针对的场景。94.6% → 80.4%(−17.7%),mini 上 81.8% → 75.0%(−9.06%)。答案就在最近一段对话里时,图检索可能不如直接把原文给模型。76.9% → 74.4%(−3.36%),在 gpt-4o 上 78.2% → 83.3%(+6.52%)。论文明确说,能力较弱的模型对 Zep 的时间数据理解不足。还有一个测量细节:Zep 的延迟是在波士顿的消费级笔记本上、连接 AWS us-west-2 测的,网络往返被算进了 Zep 的延迟;基线没有这段网络开销。因此 90% 的延迟降幅对 Zep 是偏保守的估计。
论文自己列出的边界,比宣传语更有信息量。
single-session-assistant 是唯一在两个模型上都下降的类别:gpt-4o −17.7%,mini −9.06%。当答案就在最近一段对话里,图检索反而可能漏掉局部上下文。论文把这条列为「otherwise consistent improvements」的例外,并说需要更多工程工作。
knowledge-update 在 gpt-4o-mini 上不升反降(76.9% → 74.4%),在 gpt-4o 上升 6.52%。时间范围被写进上下文,不等于模型会用它;能力较弱的模型可能被多出来的时间字段干扰。
增量 label propagation 只做一步递归,新节点按邻居多数票归入社区。这样省 LLM 调用,但社区会逐渐偏离完整重算的结果。论文明确说,周期性全量刷新仍然必要。
论文的动机是「对话历史 + 结构化业务数据」的联合记忆,但实验只测了对话。作者承认,现有基准无法评估 Zep 把对话与业务数据放在一起推理的能力。这是动机与实验之间最大的一道缝。
论文没有拆开双时态、社区层、混合检索各自的贡献,也没有报告构图成本。DMR 又太短:60 条消息的对话,full-context 已经能到 94.4%。因此「涨了多少是因为时间建模」这个问题,论文没有直接回答。
一句话总结:Zep 解决的是「同样的历史里,怎么知道哪条现在还算数」,而不是「把记忆容量做大」。它适合事实会更新、需要审计「当时知道什么」的场景;如果任务只是从固定文档里找答案,普通 RAG 或长上下文可能更简单。