5038 字
25 分钟

Graphiti Temporal Memory 源码深潜:双时间、实体消歧与事实失效

Agent Memory Engineering 专栏 · 6/6。 Graphiti 研究的核心不是“用图数据库存聊天”,而是如何让事实同时拥有实体身份、来源 Episode、业务有效时间、摄取时间、冲突失效区间与可组合的混合检索信号。

专栏导航:总览与选型地图 · Codex · Claude Code · Hermes · Mem0 · Letta · Graphiti


0. 研究基线:Temporal Memory 不等于 Graph RAG#

本文于 2026-07-15 联网核对 Graphiti 官方 Adding Episodes 文档、Zep 论文与固定版本源码;源码基线为 getzep/graphiti Commit 526dcad7a300f3c5c506ff96a68bcdc7ca9f97ed。[G1][G2][G3]

阅读路径:

graphiti.py:add_episode
node_operations.py: extract / candidate / resolve
edge_operations.py: extract / duplicate / contradiction / timestamp
nodes.py + edges.py: temporal schema
search/search.py + recipes
saga summary + dual watermarks

0.1 十三个研究问题#

  1. Episode、Entity、Fact Edge 与 Community 分别是什么?
  2. created_atvalid_atreference_time 为什么不能合并?
  3. invalid_atexpired_at 的业务语义有何不同?
  4. 为什么写入前要召回 Previous Episodes?
  5. Entity Resolution 如何降低身份碎片和错误合并?
  6. Duplicate Candidate 与 Contradiction Candidate 为什么是两类集合?
  7. 新事实如何关闭旧事实的有效区间,而不是物理删除?
  8. 普通 add_episode 为什么要求顺序 Await?
  9. 为什么 Bulk Ingestion 的网页文档与固定源码在 Edge Invalidation 上发生契约漂移?
  10. Saga 为什么需要 Processing-time 与 Event-time 双 Watermark?
  11. Search 为什么同时查 Edge、Node、Episode、Community?
  12. BM25、Cosine、BFS、RRF、MMR、Cross Encoder 如何组合?
  13. 如何评测时间正确性,而不是只测语义相关性?

1. Graphiti 的数据模型:Event、Identity、Fact 与 Summary 分层#

1.1 Episodic Node:原始摄取事件#

EpisodicNode 保存:[G4]

uuid
name
group_id
source / source_description
content
created_at
valid_at
entity_edges
episode_metadata

Episode 是一个 Ingestion Event,而不是已经 Canonicalized 的事实。它保留原始文本、消息或 JSON,并通过 MENTIONS 连接到 Entity。

1.2 Entity Node:被消歧后的身份#

EntityNode 保存:

name
name_embedding
summary
labels / types
attributes

它解决:

“Jupiter”
“Jupiter363”
“该项目作者”

是否指向同一实体的问题。

1.3 Entity Edge:带时间与来源的事实#

EntityEdge 连接两个 Entity,并保存:[G5]

name / relation
fact
fact_embedding
episodes[]
created_at
valid_at
invalid_at
expired_at
reference_time
attributes

关系边本身就是 Fact Record,而不只是图遍历连接。

1.4 Community:区域摘要#

Community Node / Edge 对局部图进行聚类和摘要,适合高层主题召回。它不是原始事实真相源,而是 Derived Projection。

1.5 Saga:有序 Episode 序列#

固定版本新增 Saga Node 与 HAS_EPISODE / NEXT_EPISODE,用于把一组连续 Episode 组织成可增量摘要的长叙事。[G6]

flowchart LR
E1["Episode 1"] -->|"MENTIONS"| N1["Entity A"]
E1 -->|"MENTIONS"| N2["Entity B"]
N1 -->|"Fact Edge + temporal interval"| N2
S["Saga"] -->|"HAS_EPISODE"| E1
E1 -->|"NEXT_EPISODE"| E2["Episode 2"]
N1 --> C["Community Summary"]

2. 双时间:世界发生时间与系统知道时间#

2.1 created_at:Transaction / Ingestion Time#

Episode created_at 由系统摄取时生成:

系统什么时候把这条 Episode 写入图?

2.2 valid_at:Event / Reference Time#

调用 add_episode(reference_time=...) 时,Episode valid_at 使用业务事件的参考时间:[G7]

这条 Episode 描述的事情在世界中何时发生?

2.3 为什么必须分开#

2026-07-15 ingestion:
“用户在 2025-01-10 搬到上海”
created_at = 2026-07-15
valid_at = 2025-01-10

如果只保留 created_at,系统会误以为搬家发生在 2026;如果只按 valid_at 推进摄取 Watermark,迟到数据可能被永久跳过。

2.4 Fact Edge 的五个时间字段#

字段问题
created_at系统何时创建这条 Edge?
valid_at事实何时开始成立?
invalid_at事实何时不再成立?
expired_at系统何时将 Edge 标记为已失效/过期?
reference_time生成这条 Edge 的 Episode 参考时间是什么?

invalid_at 属于业务有效区间;expired_at 更接近系统失效标记。两者混用会让“世界事实变化”和“系统维护状态”无法区分。[G5]

2.5 Bitemporal Query#

理想查询可以分别问:

AS OF valid time:
2025-06-01 用户住在哪里?
AS OF transaction time:
系统在 2025-06-01 当时知道用户住在哪里?

固定版本提供了必要字段,但应用是否暴露完整 Bitemporal Query API,需要根据 Search Filter 与业务层自行设计。


3. add_episode:在线写入是一条有序语义流水线#

官方文档和源码都建议每个 Episode 顺序加入并 Await 完成,Web 应用可用 Background Queue,但同一序列不能无约束并发。[G3][G7]

固定版本主路径:[G7]

validate types + group
→ retrieve previous episodes
→ create / load episode
→ extract nodes
→ resolve entity identities
→ extract edges and attributes
→ resolve duplicates and contradictions
→ invalidate old edges
→ persist episode / nodes / edges
→ optional community update
→ trace counts and latency

3.1 为什么先取 Previous Episodes#

当前 Episode 可能写:

“她上周从北京搬到了上海。”

没有前序上下文,模型不知道“她”是谁,也无法判断旧的北京居住关系是否需要结束。

Previous Episodes 提供局部 Schema 与叙事连续性:

  • 指代消解;
  • 已出现 Entity;
  • 旧关系;
  • 时间变化;
  • 用户自定义 Type Context。

3.2 previous_episode_uuids#

调用方可以显式传 Previous Episode UUID,避免依赖“按最近 created_at 自动选择”。这对外部有严格序列的事件流很重要:

Kafka partition order
→ previous UUID from prior committed result
→ deterministic local context

3.3 Group Partition#

group_id 是图分区与查询隔离键。固定实现会校验,并可能 Clone Driver 到对应 Database。[G7]

它必须被当成 Tenant Boundary:Node Resolution、Edge Candidate Search、Search 与 Delete 都不能跨 Group 意外混合。


4. Entity Extraction 与 Resolution:身份规范化是图质量上限#

Node Pipeline 包含:[G8]

extract_nodes
→ collapse exact duplicates in current extraction
→ collect exact / semantic candidates
→ LLM resolution when needed
→ commit canonical UUID mapping
→ extract attributes and summaries

4.1 Exact Collapse#

同一 Episode 中重复提取的 Entity 先本地折叠,避免后续每个重复都触发数据库候选搜索和 LLM Resolution。

4.2 Candidate Retrieval#

Resolution 不是让 LLM 在全图自由猜 UUID,而是先构造有限候选:

exact name candidate
∪ semantic name candidate
∪ previous-episode entities

再让 LLM 判断 Merge 或 New Entity。

4.3 两种错误#

False Merge:

Apple company
Apple fruit
→ incorrectly one node

污染会沿所有关系扩散。

False Split:

OpenAI
Open AI
the company
→ three nodes

Recall 会碎片化,多跳关系断裂。

4.4 Custom Entity Types#

entity_types 允许用 Pydantic Model 定义类型与属性;excluded_entity_types 可以禁止某些类型进入图。[G7][G8]

Ontology 越严格:

  • Extraction 更一致;
  • 但新实体更容易被错误套入旧类型;
  • Schema Evolution 更复杂。

需要保存 ontology_version / extractor_version 才能解释历史 Node 为什么拥有某些 Label。


5. Fact Extraction:Duplicate 与 Contradiction 必须分开#

Edge Pipeline 包含:

extract_edges
→ exact / semantic duplicate filtering
→ resolve extracted edge
→ extract timestamps
→ resolve contradictions
→ produce resolved + invalidated + new edges

相关函数在固定版本 edge_operations.py 中显式分离。[G9]

5.1 Duplicate Candidate#

问题是:

新 Fact 是否已经表达过同一关系?

如果是,通常应:

  • 复用 Edge;
  • 添加当前 Episode 到 Provenance List;
  • 避免重复 Fact。

5.2 Contradiction Candidate#

问题是:

新 Fact 是否让某条旧 Fact 从某个时间起不再成立?

例如:

Old: Alice lives in Beijing, valid_at=2024-01
New: Alice moved to Shanghai, valid_at=2025-09

正确操作不是删除旧 Fact,而是:

Old.invalid_at = 2025-09
New.valid_at = 2025-09

5.3 为什么不能用一个 Similarity Threshold 同时解决#

Duplicate 与 Contradiction 都可能高度语义相似:

“用户住在北京”
“用户不再住在北京”

仅靠 Embedding 很可能把两者当近重复。需要结合关系类型、否定、时间与 Entity Pair 判断。

5.4 Provenance List#

EntityEdge.episodes[] 保存引用该 Fact 的 Episode ID。[G5]

它支持:

  • 回到原始证据;
  • 统计多次提及;
  • 删除 Episode 时判断 Edge 是否仍有其他来源;
  • 解释为什么 Fact 存在。

但 Episode List 只提供来源 ID;来源 Trust、Extractor Model 与具体 Text Span 仍需额外 Metadata 才能完成细粒度审计。


6. Temporal Conflict:关闭区间,而不是覆写当前值#

Flat KV Memory 常做:

location = Shanghai

Graphiti 路线保存:

Alice --lives_in--> Beijing
valid_at=2024-01
invalid_at=2025-09
Alice --lives_in--> Shanghai
valid_at=2025-09
invalid_at=null

6.1 三种查询#

Current state
→ invalid_at is null or after now
Historical state at t
→ valid_at <= t < invalid_at
Evolution
→ order facts by valid_at

6.2 迟到数据#

2026 年摄取一条 valid_at=2025-02 的 Episode,可能需要插入到已有时间线中间,并重新计算相邻 Fact 的区间。

这比“新消息总是覆盖旧消息”复杂得多:

  • 新 Episode 的 Processing Time 最新;
  • 但 Event Time 可能更早;
  • 它可能不是 Current State;
  • 却可能修复一段历史空白。

6.3 Temporal Extraction Error#

相对时间“去年春天”“下周一”依赖 Episode Reference Time。Reference Time 错误会让所有 Fact Interval 错位。

生产系统应保留:

raw temporal expression
normalized timestamp
reference_time
timezone
extraction confidence

7. Sequential 与 Bulk:官方文档和固定源码存在契约漂移#

官方 Adding Episodes 文档明确写道:add_episode_bulk 只应用于空图或不要求 Edge Invalidation 的导入,并称 Bulk Pipeline 不执行 Invalidation。[G3]

但本文固定的 526dcad 源码已经不同:add_episode_bulk() 的 Docstring 明确声明 Bulk 同样执行日期提取与 Edge Invalidation;实现也从 _resolve_nodes_and_edges_bulk() 收集 invalidated_edges,并与新 Edge 一起持久化。[G14]

因此不能把“Bulk 不做 Invalidation”写成这个固定 Commit 的源码事实。更准确的判断是:公开文档落后于固定源码,使用者必须按实际安装版本验证 Bulk 契约。

7.1 为什么 Bulk 更快#

它可以:

  • 批量 Extraction;
  • 批量 Embedding;
  • 减少每 Episode 前序查询;
  • 并行更多 CPU / Network Work。

7.2 为什么仍不能假设它与顺序在线写入等价#

在线事实变化依赖顺序:

Episode 1 establishes fact A
Episode 2 contradicts A and opens B
Episode 3 refers to B

即使固定版本已经补上 Invalidation,Bulk 内的多个 Episode 仍经过批量抽取、去重和并行 Resolution。调用方需要验证同一 Batch 内的顺序冲突是否与逐条 await add_episode() 完全一致,特别是:

  • Episode 2 是否能看到 Episode 1 刚提取但尚未持久化的事实;
  • 同一 Batch 内的多个 Contradiction 是否按 valid_at 稳定关闭区间;
  • Previous Episode Context 是否与调用方业务序列一致;
  • Retry 整个 Batch 时,部分已持久化对象是否保持幂等。

7.3 导入策略#

Historical bootstrap on empty graph
→ bulk load
→ validate in-batch invalidation + reconciliation
Online updates
→ partition by group / saga
→ sequential queue per partition
→ await commit before next episode

跨 Partition 可并行;对严格依赖状态演化的同一 Entity Graph,逐 Episode 顺序路径仍是更容易证明的默认选择。若采用 Bulk,必须固定 Graphiti 版本,并用 Contradiction / Late-arrival Fixture 验证实际语义,而不能只依据当前网页文档或最新源码中的任意一方。


8. Persist 与故障窗口#

add_episode 涉及:

  • Episodic Node;
  • Entity Nodes;
  • Entity Edges;
  • MENTIONS Edges;
  • Invalidated Old Edges;
  • Optional Saga Links;
  • Optional Communities。

8.1 原子边界依赖 Graph Driver#

源码在多个异步步骤中保存不同对象。是否处于同一数据库事务、Driver 是否支持跨查询原子提交,需要按 Provider 验证,不能只因使用 Graph DB 就假设完整 ACID Workflow。

8.2 典型部分成功#

Entity nodes saved
but fact edges failed
New edge saved
but old contradictory edge not invalidated
Episode saved
but MENTIONS incomplete
Facts saved
but community update failed

8.3 Idempotency#

调用方可传 Episode UUID。生产 Ingestion 应用稳定 Event ID 作为幂等键,避免 Queue Retry 生成重复 Episode。

但 Episode 幂等不自动保证 Extracted Node / Edge 全部幂等;需要:

  • Deterministic Resolution;
  • Unique Constraints;
  • Episode Processing Status;
  • Reconciliation Job。

8.4 Saga / Outbox 补强#

ingestion_event
→ PENDING
→ episode saved
→ entities resolved
→ edges committed / invalidated
→ projections updated
→ COMPLETE

失败可从最后 Checkpoint 重放,而不是重新执行全部非确定 LLM Extraction。


9. Saga 双 Watermark:Processing Time 与 Event Time#

SagaNode 保存:[G4][G6]

summary
first_episode_uuid
last_episode_uuid
last_summarized_at
last_summarized_episode_valid_at

9.1 last_summarized_at#

这是 Wall-clock Watermark,用于下一次只筛选 created_at > since 的新摄取 Episode。[G6]

迟到 Episode 今天才写入,即使 valid_at 是去年,它的 created_at 仍是今天,因此不会漏掉。

9.2 last_summarized_episode_valid_at#

这是 Episode-time Watermark,记录 Summary 已覆盖 Episode 的最大 valid_at,供消费者回答“摘要在业务时间上覆盖到哪里”。[G6]

9.3 为什么一个 Watermark 不够#

如果只用 Event-time Watermark 过滤下一批:

last valid_at = 2026-01
late event valid_at = 2025-12, ingested today
→ incorrectly skipped

如果只暴露 Processing-time:

summary updated today

却不能说明内容在业务时间上是否只覆盖到去年。

Graphiti 中 Valid Time、Ingestion Time、迟到 Backfill 与双 Watermark 的关系

图 1:last_summarized_at 推进摄取过滤;last_summarized_episode_valid_at 表达事件时间覆盖。两个 Watermark 必须保持不同语义,但双 Watermark 本身并不自动解决分页积压与并发摄取竞态。

9.4 Incremental Summary 的边界#

固定版本每次最多取 200 个 Episode,并把 Existing Summary 与新 Episode 一起交给 LLM;成功后将 last_summarized_at 直接推进到 utc_now(),而不是本批已处理 Episode 的最大 created_at。[G6]

风险:

  • 新 Episode 超过 200 条时,未进入本批的 Episode 可能已经早于新的 Wall-clock Watermark,后续查询不会再选中;
  • Episode 在 Fetch 之后、Watermark 保存之前完成写入时,也可能落入同一个跳过窗口;
  • 摘要反复重写产生 Drift;
  • 迟到旧事件可能改变已有摘要结构;
  • LLM 成功但 Watermark 保存失败会重复处理;
  • Watermark 成功但 Summary 未持久化会漏内容。

生产实现应在读取前固定 upper_bound,按 (created_at, uuid) 游标分页,只把 Watermark 推进到最后一条已成功纳入摘要的 Episode;再用 Versioned Summary 或事务保证 Summary 与 Watermark 同步提交。


10. Search:Graph Memory 不是 Traversal-only#

固定版本 search() 并行执行四个 Scope:[G10]

edge_search
node_search
episode_search
community_search

配置 Recipe 支持:[G11]

BM25
Cosine Similarity
BFS
RRF
MMR
Cross Encoder
Node Distance
Episode Mentions

10.1 为什么查 Edge#

用户问题通常针对事实:

谁负责项目?
用户什么时候搬家?
系统依赖哪个服务?

Fact Text 与时间主要在 Entity Edge。

10.2 为什么查 Node#

Node Search 适合 Entity Name、Type、Summary 和属性。

10.3 为什么查 Episode#

Episode 保留原始上下文与 Provenance。当 Fact Summary 不足时,需要回到原始事件。

10.4 为什么查 Community#

Community 是更高层主题摘要,适合宽泛 Query 和图区域发现。

10.5 BFS 的作用#

BM25 / Vector 解决内容相关性,BFS 解决图结构邻近。给定 Center Node 或 Origin Nodes 后,结构信号可以把与任务实体相连但文本不相似的 Fact 带入候选。

10.6 Reranker 选择#

Reranker目标
RRF融合多个 Rank List,简单稳健
MMR相关性与多样性平衡
Cross Encoder更精细 Query-Document 判断,成本更高
Node Distance偏好图距离近的候选
Episode Mentions偏好多来源提及的事实

“Hybrid Graph Search”真正含义是内容、结构、来源和重排的组合,不是只跑一次 Cypher Traversal。


11. Search Filter、Group 与时间正确性#

11.1 Tenant Isolation#

group_ids 在 Search 入口被校验,并传给 Edge / Node / Episode / Community Search。[G10]

任何一个 Scope 忘记 Group Filter 都可能泄漏其他租户的 Entity 或 Fact。

11.2 Temporal Filter#

查询 Current Fact 时必须过滤无效区间;查询历史时必须使用目标时间。只按 Final Similarity 排序,可能把语义高度相关但已失效的旧 Fact 放在第一位。

11.3 Conflict Set#

当同一 Entity Pair / Relation 存在多条时间相邻 Fact,Context Builder 应整体获取 Conflict Set:

Old fact + valid interval
New fact + valid interval
Source episodes

只把其中一条塞给模型,会丢失变化与不确定性。


12. Deletion、Invalidation 与 Provenance#

12.1 Invalidation#

业务事实变化时,旧 Edge 保留并设置 invalid_at / expired_at,用于历史查询。

12.2 Episode Removal#

固定版本 remove_episode() 的实际边界比一个通用级联删除更窄:它读取 Episode 关联的 Fact Edge,只删除 episodes[0] 等于该 Episode UUID 的 Edge;只删除当前仅被一个 Episode MENTIONS 的 Entity,最后删除 Episode 本身。[G13]

这意味着实现审计还要追问:当被删 Episode 不是 episodes[] 第一项时,数组中的来源引用由谁移除?Community、Saga 与 Summary 由谁重建?源码主函数中没有展示这些派生物的同步更新。因此生产删除工作流必须额外考虑:

  • MENTIONS Edge;
  • Fact Edge 的 episodes[]
  • 该 Fact 是否还有其他 Episode 来源;
  • Entity 是否成为孤儿;
  • Community Summary 是否需要重建;
  • Saga Link 与 Summary 是否需要更新。

12.3 合规删除与历史保留冲突#

Temporal System 倾向保留历史,但用户删除请求可能要求物理删除所有派生数据。

这类“可召回的派生记忆无法随源数据删除”的风险也是 Agent Memory 治理必须单独验证的攻击与合规面。[G12]

需要 Tombstone Workflow:

mark subject deletion
→ delete / redact episodes
→ remove provenance references
→ rebuild or delete facts
→ rebuild communities / sagas
→ purge embeddings and caches
→ verify no searchable projection

“设置 invalid_at”不能替代隐私删除。


13. Cost Model:图的表达能力不是免费的#

一次 Episode 可能触发:

  • Node Extraction LLM;
  • Entity Resolution LLM;
  • Edge Extraction LLM;
  • Contradiction / Timestamp LLM;
  • Node Attribute / Summary LLM;
  • Entity Name / Fact Embedding;
  • 多次 Graph Query;
  • Optional Community Update。

13.1 写放大#

WriteAmplification=LLMCalls+Embeddings+GraphWritesEpisodeWriteAmplification = \frac{LLMCalls + Embeddings + GraphWrites}{Episode}

Graphiti 用 max_coroutines 和并行流程降低 Wall-clock,但 Provider Rate Limit、Graph Connection Pool 与 Group Ordering 仍要协调。

13.2 什么时候不该用 Temporal Graph#

  • 只有少量稳定偏好;
  • 没有 Entity / Relation Query;
  • 不需要历史状态;
  • 写入延迟和成本极敏感;
  • 团队无法维护 Ontology 与 Resolution Eval。

这时 Flat Memory + Expiration 可能更务实。


14. Evaluation:相关不等于时间正确#

14.1 Entity Resolution#

merge precision / recall
fragmentation rate
false merge blast radius

14.2 Fact Extraction#

relation precision / recall
provenance completeness
attribute accuracy

14.3 Temporal Correctness#

valid_at accuracy
invalid_at accuracy
current-state accuracy
historical-state accuracy
late-arrival repair accuracy

14.4 Invalidation#

需要专门构造:

  • 明确重复;
  • 明确矛盾;
  • 偏好增强但非冲突;
  • 临时例外;
  • 迟到旧事实;
  • 同名不同实体。

测量 Duplicate 与 Contradiction 是否被混淆。

分别测:

Edge Recall@K
Node Recall@K
Episode Evidence Recall@K
Community Topic Recall@K
Current Fact Precision
Conflict-set Completeness

14.6 End-to-end#

最终问题是:给定时间点 tt 与系统当时可见的 Episode 集合,Agent 是否基于正确有效事实回答,并引用正确来源。


15. 生产化参考架构#

flowchart TB
ES["Partitioned Episode Stream"] --> IQ["Idempotent Ingestion Queue"]
IQ --> EX["Extraction + Resolution Workers"]
EX --> GS["Temporal Graph Store"]
EX --> JL["Job / Mutation Log"]
GS --> RC["Reconciliation"]
RC --> GS
GS --> HS["Hybrid Search"]
HS --> CB["Temporal Context Builder"]
CB --> AG["Agent"]
GS --> SG["Saga / Community Projectors"]
SG --> GS
JL --> DLQ["Retry / Dead-letter"]

15.1 Ingestion State#

RECEIVED
→ EXTRACTING
→ RESOLVING
→ COMMITTING
→ PROJECTING
→ COMPLETE
or RETRYABLE / QUARANTINED

15.2 Reconciliation#

周期检测:

episode without mentions
edge references missing episode
invalid interval overlaps
entity fragment candidates
orphan nodes
saga watermark behind ingestion
summary version behind episodes

15.3 Observability#

固定版本已经写入 Tracing Span,包括 Node / Edge / Invalidation / Previous Episode Count 与 Duration。[G7]

生产指标还应包括:

episode_lag_seconds
resolution_candidate_count
entity_false_merge_rate
edge_invalidation_rate
late_episode_rate
temporal_interval_violation_count
search_latency_ms{scope,method}
saga_summary_lag
reconciliation_repair_total

16. Graphiti 路线的优势与边界#

16.1 优势#

  • Episode 保留原始事件与 Provenance;
  • Entity Resolution 建立长期身份;
  • Fact Edge 同时保存文本、来源、Embedding 与时间区间;
  • Invalidation 保留历史而不是覆盖;
  • Previous Episode 提供叙事连续性;
  • Saga 用 Processing-time 过滤与 Event-time Coverage 区分迟到事件语义;
  • Edge / Node / Episode / Community 多 Scope Search;
  • BM25 / Vector / BFS / Reranker 组合灵活;
  • Group Partition 支持隔离。

16.2 边界#

  • LLM 与 Embedding 写放大高;
  • Entity Resolution 错误会污染全图;
  • Temporal Extraction 与 Ontology Drift 难评测;
  • 同 Group 在线写入需要顺序控制;
  • Bulk 契约存在文档/源码漂移,Batch 内顺序冲突语义需要版本化验证;
  • 多对象 Persist 可能部分成功;
  • Community / Saga Summary 会累积摘要漂移;
  • 合规删除比 Flat Store 更复杂;
  • Graph DB 运维与 Provider 差异不可忽略。

16.3 最准确的架构定义#

Graphiti 不是:

Knowledge Graph + Embeddings

而是:

Event-sourced Temporal World Model
+ Entity Identity Resolution
+ Provenance-bearing Fact Edges
+ Validity Interval Maintenance
+ Sequential Contradiction Processing
+ Processing/Event-time Watermarks
+ Multi-scope Hybrid Graph Retrieval

它最值得学习的结论是:

当 Agent 面对会变化的世界,Memory 的核心不再是“召回相似文本”,而是维护谁、什么关系、在什么时间成立,以及系统为什么相信它。


参考资料与源码#

[G1] Graphiti 固定版本#

[G2] Zep Paper#

[G3] Adding Episodes#

  • Adding Episodes
  • Markdown 原文
  • 核对内容:Episode 类型、Reference Time、顺序 Await,以及网页文档对 Bulk“不执行 Edge Invalidation”的当前声明。

[G4] Node Schema#

  • nodes.py
  • 核对内容:Episodic / Entity / Community / Saga Node、Valid Time 与双 Watermark。

[G5] Edge Schema#

  • edges.py
  • 核对内容:Entity Edge Fact、Episode Provenance、Valid / Invalid / Expired / Reference Time。

[G6] Saga Incremental Summary#

[G7] Online Episode Write Path#

[G8] Entity Extraction and Resolution#

  • node_operations.py
  • 核对内容:Exact Collapse、Candidate Collection、Semantic Search、LLM Resolution、Attribute / Summary Extraction。

[G9] Fact Edge Resolution#

  • edge_operations.py
  • 核对内容:Edge Extraction、Duplicate Filter、Timestamp Extraction、Contradiction 与 Invalidation。
  • search/search.py
  • 核对内容:Edge / Node / Episode / Community 并行 Search、Group Filter 与 Tracing。

[G11] Search Recipes#

[G12] Agent Memory Security#

[G13] Episode Removal#

  • Graphiti.remove_episode
  • 核对内容:Fact Edge 的首来源判定、单 Episode Entity 删除与 Episode 删除边界。

[G14] Bulk Episode Write Path#

  • Graphiti.add_episode_bulk
  • 核对内容:固定源码中的批量日期提取、Edge Resolution、invalidated_edges 持久化,以及与网页文档的契约漂移。

上一篇:Letta / MemGPT Memory 深潜 · 返回:六种 Agent Memory 架构总览

Graphiti Temporal Memory 源码深潜:双时间、实体消歧与事实失效
https://jupiter-ws.cn/posts/ai-coding/agent-memory-graphiti-deep-dive/
作者
Jupiter
发布于
2026-07-15
许可协议
CC BY-NC-SA 4.0