大模型AI Agent记忆系统——Graphiti_源码架构与实现教程

Graphiti 源码架构与实现教程

项目:getzep/graphiti

目标:从 架构 → 数据模型 → 写入流程 → 检索流程 → Driver → LLM/Embedding → 服务层 → 每个代码模块 ,逐层拆解 Graphiti。

分析基线:GitHub main 分支,检查时间 2026-08-19 ;根目录 pyproject.tomlgraphiti-core 版本为 0.29.3

说明:本文以"源码阅读教程"的方式组织,不逐行复制源码,而是解释源码职责、关键函数、调用关系与设计意图。这样既便于理解,也不受行号频繁变化影响。


目录

  1. [先建立全局认知:Graphiti 到底是什么](#先建立全局认知:Graphiti 到底是什么)
  2. [总体架构:六层理解 Graphiti](#总体架构:六层理解 Graphiti)
  3. 仓库目录总览
  4. 核心图数据模型:Episode、Entity、Fact、Community、Saga
  5. [时间模型:Graphiti 为什么适合"记忆"](#时间模型:Graphiti 为什么适合“记忆”)
  6. [Graphiti 类:整个系统的总编排器](#Graphiti 类:整个系统的总编排器)
  7. add_episode():一次记忆写入的完整源码流程
  8. 节点抽取与实体去重
  9. 关系抽取、事实去重与时间失效
  10. [Episode、MENTIONS 与事实溯源](#Episode、MENTIONS 与事实溯源)
  11. Saga:连续会话/事件链如何组织
  12. [批量写入 add_episode_bulk()](#批量写入 add_episode_bulk())
  13. 搜索系统总览:不是单纯向量检索
  14. BM25、Cosine、BFS、RRF、MMR、Cross-Encoder
  15. [SearchConfig 与搜索配方](#SearchConfig 与搜索配方)
  16. [Node / Edge Namespace:新一代数据访问 API](#Node / Edge Namespace:新一代数据访问 API)
  17. [Driver 层:Neo4j / FalkorDB / Neptune / Kuzu](#Driver 层:Neo4j / FalkorDB / Neptune / Kuzu)
  18. [LLM Client 层](#LLM Client 层)
  19. [Prompt 系统:Graphiti 的"认知逻辑"放在哪里](#Prompt 系统:Graphiti 的“认知逻辑”放在哪里)
  20. [Embedder 与 Cross-Encoder](#Embedder 与 Cross-Encoder)
  21. Community:图社区与摘要
  22. 维护、去重、属性抽取工具链
  23. [FastAPI Server](#FastAPI Server)
  24. [MCP Server](#MCP Server)
  25. Tracing、Telemetry、并发与容错
  26. [graphiti_core 每个模块源码地图](#graphiti_core 每个模块源码地图)
  27. 服务、示例、测试与工程目录逐项说明
  28. 完整函数调用关系总图
  29. [如何扩展 Graphiti](#如何扩展 Graphiti)
  30. 源码阅读顺序与调试建议
  31. 术语与缩写字典
  32. [最终总结:把 Graphiti 看成什么最准确](#最终总结:把 Graphiti 看成什么最准确)

第1章 先建立全局认知:Graphiti 到底是什么

Graphiti 的定位不是"一个图数据库 SDK",也不是"一个向量数据库包装器"。

更准确地说:

Graphiti 是一套把连续事件转换成可更新、可追溯、带时间语义的知识图,并提供混合检索能力的 Agent Context / Memory Engine。

它试图解决的是:

  • Agent 每天不断接收新的对话、事件、业务数据;
  • 新信息可能补充旧信息,也可能推翻旧信息;
  • 不能只保留"最新值",因为过去事实仍可能有价值;
  • 检索时不仅要按"语义像不像"找,还要考虑:
    • 关键词;
    • 图结构邻近性;
    • 信息来源;
    • 时间;
    • 实体关系;
    • 候选结果的精排。

因此 Graphiti 的基本思想可以概括为:
#mermaid-svg-smls9hdAKNSVu57r{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-smls9hdAKNSVu57r .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-smls9hdAKNSVu57r .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-smls9hdAKNSVu57r .error-icon{fill:#552222;}#mermaid-svg-smls9hdAKNSVu57r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-smls9hdAKNSVu57r .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-smls9hdAKNSVu57r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-smls9hdAKNSVu57r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-smls9hdAKNSVu57r .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-smls9hdAKNSVu57r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-smls9hdAKNSVu57r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-smls9hdAKNSVu57r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-smls9hdAKNSVu57r .marker.cross{stroke:#333333;}#mermaid-svg-smls9hdAKNSVu57r svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-smls9hdAKNSVu57r p{margin:0;}#mermaid-svg-smls9hdAKNSVu57r .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-smls9hdAKNSVu57r .cluster-label text{fill:#333;}#mermaid-svg-smls9hdAKNSVu57r .cluster-label span{color:#333;}#mermaid-svg-smls9hdAKNSVu57r .cluster-label span p{background-color:transparent;}#mermaid-svg-smls9hdAKNSVu57r .label text,#mermaid-svg-smls9hdAKNSVu57r span{fill:#333;color:#333;}#mermaid-svg-smls9hdAKNSVu57r .node rect,#mermaid-svg-smls9hdAKNSVu57r .node circle,#mermaid-svg-smls9hdAKNSVu57r .node ellipse,#mermaid-svg-smls9hdAKNSVu57r .node polygon,#mermaid-svg-smls9hdAKNSVu57r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-smls9hdAKNSVu57r .rough-node .label text,#mermaid-svg-smls9hdAKNSVu57r .node .label text,#mermaid-svg-smls9hdAKNSVu57r .image-shape .label,#mermaid-svg-smls9hdAKNSVu57r .icon-shape .label{text-anchor:middle;}#mermaid-svg-smls9hdAKNSVu57r .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-smls9hdAKNSVu57r .rough-node .label,#mermaid-svg-smls9hdAKNSVu57r .node .label,#mermaid-svg-smls9hdAKNSVu57r .image-shape .label,#mermaid-svg-smls9hdAKNSVu57r .icon-shape .label{text-align:center;}#mermaid-svg-smls9hdAKNSVu57r .node.clickable{cursor:pointer;}#mermaid-svg-smls9hdAKNSVu57r .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-smls9hdAKNSVu57r .arrowheadPath{fill:#333333;}#mermaid-svg-smls9hdAKNSVu57r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-smls9hdAKNSVu57r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-smls9hdAKNSVu57r .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-smls9hdAKNSVu57r .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-smls9hdAKNSVu57r .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-smls9hdAKNSVu57r .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-smls9hdAKNSVu57r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-smls9hdAKNSVu57r .cluster text{fill:#333;}#mermaid-svg-smls9hdAKNSVu57r .cluster span{color:#333;}#mermaid-svg-smls9hdAKNSVu57r div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-smls9hdAKNSVu57r .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-smls9hdAKNSVu57r rect.text{fill:none;stroke-width:0;}#mermaid-svg-smls9hdAKNSVu57r .icon-shape,#mermaid-svg-smls9hdAKNSVu57r .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-smls9hdAKNSVu57r .icon-shape p,#mermaid-svg-smls9hdAKNSVu57r .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-smls9hdAKNSVu57r .icon-shape .label rect,#mermaid-svg-smls9hdAKNSVu57r .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-smls9hdAKNSVu57r .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-smls9hdAKNSVu57r .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-smls9hdAKNSVu57r :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 连续输入

Message / Text / JSON / Fact Triple
Episode

原始事件
LLM 信息抽取
Entity

实体节点
Fact / Relationship

关系事实
去重 + 消歧 + 时间判断
Temporal Context Graph

时间上下文图
Hybrid Search

混合检索
Agent / RAG / Memory

1.1 Graphiti 与普通 RAG 的区别

典型 RAG:

text 复制代码
文档
  ↓
切块
  ↓
Embedding
  ↓
向量数据库
  ↓
Query Embedding
  ↓
Top-K 相似文本

Graphiti:

text 复制代码
事件
  ↓
Episode
  ↓
实体 / 关系 / 时间抽取
  ↓
实体消歧
  ↓
事实去重
  ↓
矛盾事实时间失效
  ↓
图数据库 + 向量 + 全文索引
  ↓
BM25 / Vector / BFS
  ↓
RRF / MMR / Cross-Encoder / 图距离
  ↓
结构化、可追溯的上下文

普通 RAG 主要回答

"有哪些文本跟这个 Query 相似?"

Graphiti 更希望回答

"与这个问题相关的实体、事实、事件有哪些?

这些事实什么时候成立?

哪些已经失效?

它们来自哪一次 Episode?

与某个中心实体在图上有什么关系?"

1.2 Graphiti 与静态知识图谱的区别

传统知识图谱通常倾向于得到:

text 复制代码
Alice --WORKS_AT--> Company A

如果 Alice 后来去了 Company B,最简单的系统可能更新成:

text 复制代码
Alice --WORKS_AT--> Company B

Graphiti 更强调保留事实生命周期:

text 复制代码
Alice --WORKS_AT--> Company A
valid_at = 2024-01
invalid_at = 2026-03

Alice --WORKS_AT--> Company B
valid_at = 2026-03
invalid_at = null

也就是说:

"事实变化"不是删除历史,而是改变事实有效区间。

这正是 Graphiti 很适合 Agent 长期记忆的原因。


第2章 总体架构:六层理解 Graphiti

阅读 Graphiti 源码时,建议始终脑中保持下面六层。
#mermaid-svg-w58E8E1hV65pzqZS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-w58E8E1hV65pzqZS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w58E8E1hV65pzqZS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w58E8E1hV65pzqZS .error-icon{fill:#552222;}#mermaid-svg-w58E8E1hV65pzqZS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w58E8E1hV65pzqZS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w58E8E1hV65pzqZS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w58E8E1hV65pzqZS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w58E8E1hV65pzqZS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w58E8E1hV65pzqZS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w58E8E1hV65pzqZS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w58E8E1hV65pzqZS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w58E8E1hV65pzqZS .marker.cross{stroke:#333333;}#mermaid-svg-w58E8E1hV65pzqZS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w58E8E1hV65pzqZS p{margin:0;}#mermaid-svg-w58E8E1hV65pzqZS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-w58E8E1hV65pzqZS .cluster-label text{fill:#333;}#mermaid-svg-w58E8E1hV65pzqZS .cluster-label span{color:#333;}#mermaid-svg-w58E8E1hV65pzqZS .cluster-label span p{background-color:transparent;}#mermaid-svg-w58E8E1hV65pzqZS .label text,#mermaid-svg-w58E8E1hV65pzqZS span{fill:#333;color:#333;}#mermaid-svg-w58E8E1hV65pzqZS .node rect,#mermaid-svg-w58E8E1hV65pzqZS .node circle,#mermaid-svg-w58E8E1hV65pzqZS .node ellipse,#mermaid-svg-w58E8E1hV65pzqZS .node polygon,#mermaid-svg-w58E8E1hV65pzqZS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-w58E8E1hV65pzqZS .rough-node .label text,#mermaid-svg-w58E8E1hV65pzqZS .node .label text,#mermaid-svg-w58E8E1hV65pzqZS .image-shape .label,#mermaid-svg-w58E8E1hV65pzqZS .icon-shape .label{text-anchor:middle;}#mermaid-svg-w58E8E1hV65pzqZS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-w58E8E1hV65pzqZS .rough-node .label,#mermaid-svg-w58E8E1hV65pzqZS .node .label,#mermaid-svg-w58E8E1hV65pzqZS .image-shape .label,#mermaid-svg-w58E8E1hV65pzqZS .icon-shape .label{text-align:center;}#mermaid-svg-w58E8E1hV65pzqZS .node.clickable{cursor:pointer;}#mermaid-svg-w58E8E1hV65pzqZS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-w58E8E1hV65pzqZS .arrowheadPath{fill:#333333;}#mermaid-svg-w58E8E1hV65pzqZS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-w58E8E1hV65pzqZS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-w58E8E1hV65pzqZS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w58E8E1hV65pzqZS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-w58E8E1hV65pzqZS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w58E8E1hV65pzqZS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-w58E8E1hV65pzqZS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-w58E8E1hV65pzqZS .cluster text{fill:#333;}#mermaid-svg-w58E8E1hV65pzqZS .cluster span{color:#333;}#mermaid-svg-w58E8E1hV65pzqZS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-w58E8E1hV65pzqZS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-w58E8E1hV65pzqZS rect.text{fill:none;stroke-width:0;}#mermaid-svg-w58E8E1hV65pzqZS .icon-shape,#mermaid-svg-w58E8E1hV65pzqZS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-w58E8E1hV65pzqZS .icon-shape p,#mermaid-svg-w58E8E1hV65pzqZS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-w58E8E1hV65pzqZS .icon-shape .label rect,#mermaid-svg-w58E8E1hV65pzqZS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-w58E8E1hV65pzqZS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-w58E8E1hV65pzqZS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-w58E8E1hV65pzqZS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ⑥ 数据访问层
⑤ 检索层
④ AI 能力层
③ 记忆构建层
② 编排层
① 接入层
FastAPI Server
MCP Server
Python SDK / Graphiti
Graphiti
NodeNamespace
EdgeNamespace
Node Extraction
Node Resolution / Dedupe
Edge Extraction
Edge Resolution / Dedupe
Temporal Invalidation
Community
Saga
LLMClient
PromptLibrary
EmbedderClient
CrossEncoderClient
search() / search_()
BM25
Cosine Similarity
BFS
RRF / MMR / Cross-Encoder
Operations ABC
GraphDriver
Neo4j
FalkorDB
Neptune
Kuzu

2.1 六层分别负责什么

作用 典型源码
接入层 给应用暴露 HTTP、MCP、Python API server/, mcp_server/, graphiti_core/graphiti.py
编排层 把一次写入/搜索拆成多个阶段并调度 graphiti.py, namespaces/
记忆构建层 抽实体、抽关系、去重、时间失效、社区、Saga utils/maintenance/
AI 能力层 LLM、Embedding、Reranker、Prompt llm_client/, embedder/, cross_encoder/, prompts/
检索层 混合召回 + 重排 search/
数据访问层 屏蔽不同图数据库差异 driver/, models/*_db_queries.py

第3章 仓库目录总览

当前仓库顶层可按下面理解:

text 复制代码
graphiti/
├─ graphiti_core/          # 核心 Python SDK,最重要
├─ server/                 # FastAPI 图服务
├─ mcp_server/             # MCP Server
├─ examples/               # 示例
├─ tests/                  # 核心测试
├─ spec/                   # 架构设计文档
├─ signatures/version1/    # CLA 等签署资料
├─ images/                 # README / 文档图片
├─ .github/                # CI、Issue、PR、脚本
├─ pyproject.toml          # graphiti-core 包配置
├─ Dockerfile
├─ docker-compose.yml
├─ Makefile
└─ ...

3.1 顶层目录职责

目录 功能
graphiti_core/ Graphiti 真正的核心源码
server/ 将 Graphiti 封装为 HTTP/FastAPI 服务
mcp_server/ 将 Graphiti 封装成 MCP 工具,供 Agent/LLM Client 调用
examples/ OpenAI、Azure、LangGraph、GLiNER2、OpenTelemetry 等使用样例
tests/ 单元测试、集成测试、Driver/LLM/Embedder 测试
spec/ 设计演进文档,目前尤其重要的是 Driver Operations redesign
signatures/version1/ 贡献者协议相关数据
images/ 项目文档中的 SVG/GIF/截图
.github/ GitHub Actions、Issue 模板、PR 模板、安全扫描等

第4章 核心图数据模型:Episode、Entity、Fact、Community、Saga

Graphiti 的"图"并不是只有 Entity。

核心节点大致有四类,边有五类。

4.1 图模型总图

#mermaid-svg-9rwUrplj1P2PNcrW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9rwUrplj1P2PNcrW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9rwUrplj1P2PNcrW .error-icon{fill:#552222;}#mermaid-svg-9rwUrplj1P2PNcrW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9rwUrplj1P2PNcrW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9rwUrplj1P2PNcrW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9rwUrplj1P2PNcrW .marker.cross{stroke:#333333;}#mermaid-svg-9rwUrplj1P2PNcrW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9rwUrplj1P2PNcrW p{margin:0;}#mermaid-svg-9rwUrplj1P2PNcrW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9rwUrplj1P2PNcrW .cluster-label text{fill:#333;}#mermaid-svg-9rwUrplj1P2PNcrW .cluster-label span{color:#333;}#mermaid-svg-9rwUrplj1P2PNcrW .cluster-label span p{background-color:transparent;}#mermaid-svg-9rwUrplj1P2PNcrW .label text,#mermaid-svg-9rwUrplj1P2PNcrW span{fill:#333;color:#333;}#mermaid-svg-9rwUrplj1P2PNcrW .node rect,#mermaid-svg-9rwUrplj1P2PNcrW .node circle,#mermaid-svg-9rwUrplj1P2PNcrW .node ellipse,#mermaid-svg-9rwUrplj1P2PNcrW .node polygon,#mermaid-svg-9rwUrplj1P2PNcrW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9rwUrplj1P2PNcrW .rough-node .label text,#mermaid-svg-9rwUrplj1P2PNcrW .node .label text,#mermaid-svg-9rwUrplj1P2PNcrW .image-shape .label,#mermaid-svg-9rwUrplj1P2PNcrW .icon-shape .label{text-anchor:middle;}#mermaid-svg-9rwUrplj1P2PNcrW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9rwUrplj1P2PNcrW .rough-node .label,#mermaid-svg-9rwUrplj1P2PNcrW .node .label,#mermaid-svg-9rwUrplj1P2PNcrW .image-shape .label,#mermaid-svg-9rwUrplj1P2PNcrW .icon-shape .label{text-align:center;}#mermaid-svg-9rwUrplj1P2PNcrW .node.clickable{cursor:pointer;}#mermaid-svg-9rwUrplj1P2PNcrW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9rwUrplj1P2PNcrW .arrowheadPath{fill:#333333;}#mermaid-svg-9rwUrplj1P2PNcrW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9rwUrplj1P2PNcrW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9rwUrplj1P2PNcrW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9rwUrplj1P2PNcrW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9rwUrplj1P2PNcrW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9rwUrplj1P2PNcrW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9rwUrplj1P2PNcrW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9rwUrplj1P2PNcrW .cluster text{fill:#333;}#mermaid-svg-9rwUrplj1P2PNcrW .cluster span{color:#333;}#mermaid-svg-9rwUrplj1P2PNcrW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9rwUrplj1P2PNcrW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9rwUrplj1P2PNcrW rect.text{fill:none;stroke-width:0;}#mermaid-svg-9rwUrplj1P2PNcrW .icon-shape,#mermaid-svg-9rwUrplj1P2PNcrW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9rwUrplj1P2PNcrW .icon-shape p,#mermaid-svg-9rwUrplj1P2PNcrW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9rwUrplj1P2PNcrW .icon-shape .label rect,#mermaid-svg-9rwUrplj1P2PNcrW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9rwUrplj1P2PNcrW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9rwUrplj1P2PNcrW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9rwUrplj1P2PNcrW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HAS_EPISODE
HAS_EPISODE
NEXT_EPISODE
MENTIONS
MENTIONS
MENTIONS
RELATES_TO

EntityEdge = Fact
HAS_MEMBER
HAS_MEMBER
SagaNode

一段连续事件/会话
EpisodicNode

Episode 1
EpisodicNode

Episode 2
EntityNode

User
EntityNode

Company
CommunityNode

实体社区

4.2 Node 基类

源码:

text 复制代码
graphiti_core/nodes.py

基础字段可以理解为:

字段 含义
uuid 全局唯一 ID
name 节点名字
group_id 图分区 / 租户 / namespace 语义
labels 图数据库标签
created_at Graphiti 创建该节点的系统时间

Node 是抽象基类,不负责具体业务语义。


4.3 EpisodicNode

它保存"原始发生过的一次输入"。

典型输入:

  • 用户说了一句话;
  • 一段 JSON;
  • 一篇新文档;
  • 一个业务事件;
  • 一条事实三元组。

重要字段:

字段 解释
source Episode 输入类型
source_description 来源说明
content 原始事件内容
valid_at 该事件在现实世界中的参考时间
entity_edges 本 Episode 产生/关联的 EntityEdge UUID
episode_metadata 额外元数据

EpisodeType

常见值:

  • message
  • text
  • json
  • fact_triple

注意:

EpisodicNode.valid_at 不是"Graphiti 把它写进数据库的时间",而是源事件自己的参考时间

这对历史回填非常重要。


4.4 EntityNode

实体就是知识图中的"对象"。

例如:

text 复制代码
张三
OpenAI
上海
ESP32-P4
小搭
Graphiti

重要字段:

字段 作用
name 实体名称
name_embedding 名称的向量表示
summary 实体周围事实的区域性摘要
attributes 自定义 ontology 提取出的字段
labels 实体类型

其中:

text 复制代码
generate_name_embedding()
    ↓
EmbedderClient.create(name)

4.5 EntityEdge

这是 Graphiti 最重要的数据结构之一。

物理图上通常表现为:

text 复制代码
(Entity)-[:RELATES_TO]->(Entity)

但真正的业务关系名存在 EntityEdge.name 中。

例如:

text 复制代码
张三 --RELATES_TO--> OpenAI

name = "WORKS_AT"
fact = "张三在 OpenAI 工作"

重要字段:

字段 含义
name 关系类型/名字
fact 自然语言事实
fact_embedding fact 的向量
episodes 支撑这个事实的 Episode UUID 列表
valid_at 事实什么时候开始为真
invalid_at 事实什么时候停止为真
expired_at Graphiti 什么时候发现并标记旧事实失效
reference_time 产生该事实的 Episode 的参考时间
attributes 自定义关系属性

为什么同时要 namefact

name 更适合图结构:

text 复制代码
WORKS_AT
LIVES_IN
OWNS
LIKES

fact 更适合:

  • 人类阅读;
  • Embedding;
  • Cross-Encoder;
  • LLM 去重;
  • 时间判断。

4.6 EpisodicEdge

物理语义:

text 复制代码
(Episodic)-[:MENTIONS]->(Entity)

表示:

某次 Episode 提到了某个实体。

它是 Provenance(溯源) 的重要组成部分。


4.7 CommunityNode / CommunityEdge

社区节点代表图中一组语义/结构上相关的实体。

关系:

text 复制代码
(Community)-[:HAS_MEMBER]->(Entity)

Community 能保存:

  • 名称;
  • Summary;
  • Embedding。

用途:

当单个 Entity/Fact 太细时,Community 提供更高层次的上下文摘要。


4.8 SagaNode

Saga 用来把一系列 Episode 组织成连续链。
#mermaid-svg-6ttSuVySs6PMFJog{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6ttSuVySs6PMFJog .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6ttSuVySs6PMFJog .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6ttSuVySs6PMFJog .error-icon{fill:#552222;}#mermaid-svg-6ttSuVySs6PMFJog .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6ttSuVySs6PMFJog .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6ttSuVySs6PMFJog .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6ttSuVySs6PMFJog .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6ttSuVySs6PMFJog .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6ttSuVySs6PMFJog .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6ttSuVySs6PMFJog .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6ttSuVySs6PMFJog .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6ttSuVySs6PMFJog .marker.cross{stroke:#333333;}#mermaid-svg-6ttSuVySs6PMFJog svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6ttSuVySs6PMFJog p{margin:0;}#mermaid-svg-6ttSuVySs6PMFJog .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6ttSuVySs6PMFJog .cluster-label text{fill:#333;}#mermaid-svg-6ttSuVySs6PMFJog .cluster-label span{color:#333;}#mermaid-svg-6ttSuVySs6PMFJog .cluster-label span p{background-color:transparent;}#mermaid-svg-6ttSuVySs6PMFJog .label text,#mermaid-svg-6ttSuVySs6PMFJog span{fill:#333;color:#333;}#mermaid-svg-6ttSuVySs6PMFJog .node rect,#mermaid-svg-6ttSuVySs6PMFJog .node circle,#mermaid-svg-6ttSuVySs6PMFJog .node ellipse,#mermaid-svg-6ttSuVySs6PMFJog .node polygon,#mermaid-svg-6ttSuVySs6PMFJog .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6ttSuVySs6PMFJog .rough-node .label text,#mermaid-svg-6ttSuVySs6PMFJog .node .label text,#mermaid-svg-6ttSuVySs6PMFJog .image-shape .label,#mermaid-svg-6ttSuVySs6PMFJog .icon-shape .label{text-anchor:middle;}#mermaid-svg-6ttSuVySs6PMFJog .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6ttSuVySs6PMFJog .rough-node .label,#mermaid-svg-6ttSuVySs6PMFJog .node .label,#mermaid-svg-6ttSuVySs6PMFJog .image-shape .label,#mermaid-svg-6ttSuVySs6PMFJog .icon-shape .label{text-align:center;}#mermaid-svg-6ttSuVySs6PMFJog .node.clickable{cursor:pointer;}#mermaid-svg-6ttSuVySs6PMFJog .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6ttSuVySs6PMFJog .arrowheadPath{fill:#333333;}#mermaid-svg-6ttSuVySs6PMFJog .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6ttSuVySs6PMFJog .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6ttSuVySs6PMFJog .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6ttSuVySs6PMFJog .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6ttSuVySs6PMFJog .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6ttSuVySs6PMFJog .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6ttSuVySs6PMFJog .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6ttSuVySs6PMFJog .cluster text{fill:#333;}#mermaid-svg-6ttSuVySs6PMFJog .cluster span{color:#333;}#mermaid-svg-6ttSuVySs6PMFJog div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6ttSuVySs6PMFJog .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6ttSuVySs6PMFJog rect.text{fill:none;stroke-width:0;}#mermaid-svg-6ttSuVySs6PMFJog .icon-shape,#mermaid-svg-6ttSuVySs6PMFJog .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6ttSuVySs6PMFJog .icon-shape p,#mermaid-svg-6ttSuVySs6PMFJog .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6ttSuVySs6PMFJog .icon-shape .label rect,#mermaid-svg-6ttSuVySs6PMFJog .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6ttSuVySs6PMFJog .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6ttSuVySs6PMFJog .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6ttSuVySs6PMFJog :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HAS_EPISODE
HAS_EPISODE
HAS_EPISODE
NEXT_EPISODE
NEXT_EPISODE
Saga
Episode 1
Episode 2
Episode 3

Saga 保存:

  • summary
  • first_episode_uuid
  • last_episode_uuid
  • last_summarized_at
  • last_summarized_episode_valid_at

后两个字段非常值得区分:

  • last_summarized_at:系统真正执行摘要的墙钟时间;
  • last_summarized_episode_valid_at:已经摘要到哪个 Episode 的业务时间。

这是为了正确处理"历史 Episode 回填"。


第5章 时间模型:Graphiti 为什么适合"记忆"

Graphiti 的核心不是简单"加时间戳",而是区分多种时间。

5.1 五个常见时间字段

#mermaid-svg-2NO4NsgkK6tUtpDY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2NO4NsgkK6tUtpDY .error-icon{fill:#552222;}#mermaid-svg-2NO4NsgkK6tUtpDY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2NO4NsgkK6tUtpDY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2NO4NsgkK6tUtpDY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2NO4NsgkK6tUtpDY .marker.cross{stroke:#333333;}#mermaid-svg-2NO4NsgkK6tUtpDY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2NO4NsgkK6tUtpDY p{margin:0;}#mermaid-svg-2NO4NsgkK6tUtpDY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster-label text{fill:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster-label span{color:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster-label span p{background-color:transparent;}#mermaid-svg-2NO4NsgkK6tUtpDY .label text,#mermaid-svg-2NO4NsgkK6tUtpDY span{fill:#333;color:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY .node rect,#mermaid-svg-2NO4NsgkK6tUtpDY .node circle,#mermaid-svg-2NO4NsgkK6tUtpDY .node ellipse,#mermaid-svg-2NO4NsgkK6tUtpDY .node polygon,#mermaid-svg-2NO4NsgkK6tUtpDY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2NO4NsgkK6tUtpDY .rough-node .label text,#mermaid-svg-2NO4NsgkK6tUtpDY .node .label text,#mermaid-svg-2NO4NsgkK6tUtpDY .image-shape .label,#mermaid-svg-2NO4NsgkK6tUtpDY .icon-shape .label{text-anchor:middle;}#mermaid-svg-2NO4NsgkK6tUtpDY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2NO4NsgkK6tUtpDY .rough-node .label,#mermaid-svg-2NO4NsgkK6tUtpDY .node .label,#mermaid-svg-2NO4NsgkK6tUtpDY .image-shape .label,#mermaid-svg-2NO4NsgkK6tUtpDY .icon-shape .label{text-align:center;}#mermaid-svg-2NO4NsgkK6tUtpDY .node.clickable{cursor:pointer;}#mermaid-svg-2NO4NsgkK6tUtpDY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2NO4NsgkK6tUtpDY .arrowheadPath{fill:#333333;}#mermaid-svg-2NO4NsgkK6tUtpDY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2NO4NsgkK6tUtpDY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2NO4NsgkK6tUtpDY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2NO4NsgkK6tUtpDY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2NO4NsgkK6tUtpDY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2NO4NsgkK6tUtpDY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster text{fill:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY .cluster span{color:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2NO4NsgkK6tUtpDY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2NO4NsgkK6tUtpDY rect.text{fill:none;stroke-width:0;}#mermaid-svg-2NO4NsgkK6tUtpDY .icon-shape,#mermaid-svg-2NO4NsgkK6tUtpDY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2NO4NsgkK6tUtpDY .icon-shape p,#mermaid-svg-2NO4NsgkK6tUtpDY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2NO4NsgkK6tUtpDY .icon-shape .label rect,#mermaid-svg-2NO4NsgkK6tUtpDY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2NO4NsgkK6tUtpDY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2NO4NsgkK6tUtpDY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2NO4NsgkK6tUtpDY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} reference_time

来源 Episode 的参考时间
valid_at

事实开始为真
invalid_at

事实停止为真
created_at

Graphiti 写入时间
expired_at

Graphiti 发现旧事实被替代的时间

字段 时间轴 意义
created_at 系统时间 记录何时创建
reference_time 事件时间 本事实依据哪个时间点的 Episode
valid_at 业务有效时间 事实开始为真
invalid_at 业务有效时间 事实停止为真
expired_at 系统时间 系统何时判断旧事实已失效

5.2 一个例子

2025-01:

"小王住在北京。"

得到:

text 复制代码
Fact A:
小王 --LIVES_IN--> 北京
valid_at = 2025-01
invalid_at = null

2026-06:

"小王已经搬到上海。"

得到:

text 复制代码
Fact B:
小王 --LIVES_IN--> 上海
valid_at = 2026-06
invalid_at = null

同时旧事实可能被更新为:

text 复制代码
Fact A:
valid_at   = 2025-01
invalid_at = 2026-06
expired_at = Graphiti 处理第二个 Episode 的系统时间

5.3 事实失效流程

#mermaid-svg-e6xbVUKnx4lIwjjU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-e6xbVUKnx4lIwjjU .error-icon{fill:#552222;}#mermaid-svg-e6xbVUKnx4lIwjjU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e6xbVUKnx4lIwjjU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e6xbVUKnx4lIwjjU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e6xbVUKnx4lIwjjU .marker.cross{stroke:#333333;}#mermaid-svg-e6xbVUKnx4lIwjjU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e6xbVUKnx4lIwjjU p{margin:0;}#mermaid-svg-e6xbVUKnx4lIwjjU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster-label text{fill:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster-label span{color:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster-label span p{background-color:transparent;}#mermaid-svg-e6xbVUKnx4lIwjjU .label text,#mermaid-svg-e6xbVUKnx4lIwjjU span{fill:#333;color:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU .node rect,#mermaid-svg-e6xbVUKnx4lIwjjU .node circle,#mermaid-svg-e6xbVUKnx4lIwjjU .node ellipse,#mermaid-svg-e6xbVUKnx4lIwjjU .node polygon,#mermaid-svg-e6xbVUKnx4lIwjjU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e6xbVUKnx4lIwjjU .rough-node .label text,#mermaid-svg-e6xbVUKnx4lIwjjU .node .label text,#mermaid-svg-e6xbVUKnx4lIwjjU .image-shape .label,#mermaid-svg-e6xbVUKnx4lIwjjU .icon-shape .label{text-anchor:middle;}#mermaid-svg-e6xbVUKnx4lIwjjU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-e6xbVUKnx4lIwjjU .rough-node .label,#mermaid-svg-e6xbVUKnx4lIwjjU .node .label,#mermaid-svg-e6xbVUKnx4lIwjjU .image-shape .label,#mermaid-svg-e6xbVUKnx4lIwjjU .icon-shape .label{text-align:center;}#mermaid-svg-e6xbVUKnx4lIwjjU .node.clickable{cursor:pointer;}#mermaid-svg-e6xbVUKnx4lIwjjU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-e6xbVUKnx4lIwjjU .arrowheadPath{fill:#333333;}#mermaid-svg-e6xbVUKnx4lIwjjU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e6xbVUKnx4lIwjjU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e6xbVUKnx4lIwjjU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e6xbVUKnx4lIwjjU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-e6xbVUKnx4lIwjjU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e6xbVUKnx4lIwjjU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster text{fill:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU .cluster span{color:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-e6xbVUKnx4lIwjjU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-e6xbVUKnx4lIwjjU rect.text{fill:none;stroke-width:0;}#mermaid-svg-e6xbVUKnx4lIwjjU .icon-shape,#mermaid-svg-e6xbVUKnx4lIwjjU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e6xbVUKnx4lIwjjU .icon-shape p,#mermaid-svg-e6xbVUKnx4lIwjjU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-e6xbVUKnx4lIwjjU .icon-shape .label rect,#mermaid-svg-e6xbVUKnx4lIwjjU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e6xbVUKnx4lIwjjU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-e6xbVUKnx4lIwjjU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-e6xbVUKnx4lIwjjU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





新 Fact
找到相同实体对附近的 Existing Edges
语义是否重复?
合并到已有 Fact
是否与旧 Fact 矛盾?
时间区间是否重叠?
旧 Fact invalid_at = 新 Fact valid_at
旧 Fact expired_at = 当前系统时间
新 Fact 作为新 Edge 保存
并存

不错误地互相失效

这体现了一个很重要的原则:

"语义矛盾"并不必然意味着旧事实必须失效;还要看它们在时间上是否应该同时成立。


第6章 Graphiti 类:整个系统的总编排器

核心文件:

text 复制代码
graphiti_core/graphiti.py

这是源码阅读最应该先看的文件。

6.1 构造函数依赖

Graphiti(...) 大致接收:

  • DB URI / user / password;
  • llm_client
  • embedder
  • cross_encoder
  • graph_driver
  • store_raw_episode_content
  • 并发限制;
  • tracer。

默认情况下,会为未传入的组件创建默认实现。

6.2 GraphitiClients

Graphiti 将四个主要依赖打包:

text 复制代码
GraphitiClients
├─ driver
├─ llm_client
├─ embedder
├─ cross_encoder
└─ tracer

可以把它理解成轻量的 Dependency Container。

6.3 Graphiti 的职责不是做算法,而是"编排"

#mermaid-svg-2L33i3tHk08WxxHe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2L33i3tHk08WxxHe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2L33i3tHk08WxxHe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2L33i3tHk08WxxHe .error-icon{fill:#552222;}#mermaid-svg-2L33i3tHk08WxxHe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2L33i3tHk08WxxHe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2L33i3tHk08WxxHe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2L33i3tHk08WxxHe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2L33i3tHk08WxxHe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2L33i3tHk08WxxHe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2L33i3tHk08WxxHe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2L33i3tHk08WxxHe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2L33i3tHk08WxxHe .marker.cross{stroke:#333333;}#mermaid-svg-2L33i3tHk08WxxHe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2L33i3tHk08WxxHe p{margin:0;}#mermaid-svg-2L33i3tHk08WxxHe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2L33i3tHk08WxxHe .cluster-label text{fill:#333;}#mermaid-svg-2L33i3tHk08WxxHe .cluster-label span{color:#333;}#mermaid-svg-2L33i3tHk08WxxHe .cluster-label span p{background-color:transparent;}#mermaid-svg-2L33i3tHk08WxxHe .label text,#mermaid-svg-2L33i3tHk08WxxHe span{fill:#333;color:#333;}#mermaid-svg-2L33i3tHk08WxxHe .node rect,#mermaid-svg-2L33i3tHk08WxxHe .node circle,#mermaid-svg-2L33i3tHk08WxxHe .node ellipse,#mermaid-svg-2L33i3tHk08WxxHe .node polygon,#mermaid-svg-2L33i3tHk08WxxHe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2L33i3tHk08WxxHe .rough-node .label text,#mermaid-svg-2L33i3tHk08WxxHe .node .label text,#mermaid-svg-2L33i3tHk08WxxHe .image-shape .label,#mermaid-svg-2L33i3tHk08WxxHe .icon-shape .label{text-anchor:middle;}#mermaid-svg-2L33i3tHk08WxxHe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2L33i3tHk08WxxHe .rough-node .label,#mermaid-svg-2L33i3tHk08WxxHe .node .label,#mermaid-svg-2L33i3tHk08WxxHe .image-shape .label,#mermaid-svg-2L33i3tHk08WxxHe .icon-shape .label{text-align:center;}#mermaid-svg-2L33i3tHk08WxxHe .node.clickable{cursor:pointer;}#mermaid-svg-2L33i3tHk08WxxHe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2L33i3tHk08WxxHe .arrowheadPath{fill:#333333;}#mermaid-svg-2L33i3tHk08WxxHe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2L33i3tHk08WxxHe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2L33i3tHk08WxxHe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2L33i3tHk08WxxHe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2L33i3tHk08WxxHe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2L33i3tHk08WxxHe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2L33i3tHk08WxxHe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2L33i3tHk08WxxHe .cluster text{fill:#333;}#mermaid-svg-2L33i3tHk08WxxHe .cluster span{color:#333;}#mermaid-svg-2L33i3tHk08WxxHe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2L33i3tHk08WxxHe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2L33i3tHk08WxxHe rect.text{fill:none;stroke-width:0;}#mermaid-svg-2L33i3tHk08WxxHe .icon-shape,#mermaid-svg-2L33i3tHk08WxxHe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2L33i3tHk08WxxHe .icon-shape p,#mermaid-svg-2L33i3tHk08WxxHe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2L33i3tHk08WxxHe .icon-shape .label rect,#mermaid-svg-2L33i3tHk08WxxHe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2L33i3tHk08WxxHe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2L33i3tHk08WxxHe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2L33i3tHk08WxxHe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Graphiti
Ingestion
Retrieval
Maintenance
Namespaces
add_episode
add_episode_bulk
add_triplet
search
search_
get_nodes_and_edges_by_episode
build_communities
remove_episode
nodes
edges

它本身更多是:

  1. 建立上下文;
  2. 调用抽取;
  3. 调用去重;
  4. 调用时间解析;
  5. 调用持久化;
  6. 调用搜索;
  7. 管理 tracing。

第7章 add_episode():一次记忆写入的完整源码流程

这是整个项目最重要的一条调用链。

7.1 总体时序图

Community GraphDriver _process_episode_data extract_attributes_from_nodes resolve_extracted_edges extract_edges resolve_extracted_nodes extract_nodes retrieve_episodes Graphiti.add_episode 应用 Community GraphDriver _process_episode_data extract_attributes_from_nodes resolve_extracted_edges extract_edges resolve_extracted_nodes extract_nodes retrieve_episodes Graphiti.add_episode 应用 #mermaid-svg-sbLSDU9gNNrfmQCF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sbLSDU9gNNrfmQCF .error-icon{fill:#552222;}#mermaid-svg-sbLSDU9gNNrfmQCF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sbLSDU9gNNrfmQCF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sbLSDU9gNNrfmQCF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sbLSDU9gNNrfmQCF .marker.cross{stroke:#333333;}#mermaid-svg-sbLSDU9gNNrfmQCF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sbLSDU9gNNrfmQCF p{margin:0;}#mermaid-svg-sbLSDU9gNNrfmQCF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sbLSDU9gNNrfmQCF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-sbLSDU9gNNrfmQCF .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-sbLSDU9gNNrfmQCF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-sbLSDU9gNNrfmQCF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-sbLSDU9gNNrfmQCF .sequenceNumber{fill:white;}#mermaid-svg-sbLSDU9gNNrfmQCF #sequencenumber{fill:#333;}#mermaid-svg-sbLSDU9gNNrfmQCF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-sbLSDU9gNNrfmQCF .messageText{fill:#333;stroke:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sbLSDU9gNNrfmQCF .labelText,#mermaid-svg-sbLSDU9gNNrfmQCF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .loopText,#mermaid-svg-sbLSDU9gNNrfmQCF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-sbLSDU9gNNrfmQCF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-sbLSDU9gNNrfmQCF .noteText,#mermaid-svg-sbLSDU9gNNrfmQCF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-sbLSDU9gNNrfmQCF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sbLSDU9gNNrfmQCF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sbLSDU9gNNrfmQCF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sbLSDU9gNNrfmQCF .actorPopupMenu{position:absolute;}#mermaid-svg-sbLSDU9gNNrfmQCF .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-sbLSDU9gNNrfmQCF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sbLSDU9gNNrfmQCF .actor-man circle,#mermaid-svg-sbLSDU9gNNrfmQCF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-sbLSDU9gNNrfmQCF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} opt saga enabled opt update_communities name, episode_body, reference_time... validate entity_types / exclusions / group_id 获取前序 Episodes 作为上下文 query recent episodes episodes previous_episodes 构造 EpisodicNode 从 Episode 抽实体 extracted_nodes 实体消歧/去重 resolved_nodes + uuid_map 从 Episode 抽关系 extracted_edges resolve + dedupe + temporal invalidation resolved_edges + invalidated_edges + new_edges 更新实体属性/summary enriched_nodes Episode + Nodes + Edges bulk persist HAS_EPISODE / NEXT_EPISODE / Saga 更新 Community save community nodes/edges AddEpisodeResults

7.2 add_episode() 分 10 步看

Step 1:校验输入

包括:

  • entity_types
  • excluded_entity_types
  • edge_types
  • edge_type_map
  • group_id

entity_types 决定 LLM 可抽取哪些自定义实体 schema。

edge_type_map 决定:

什么类型实体之间允许出现什么关系类型。


Step 2:处理 group_id

group_id 是非常重要的隔离维度。

可以理解为:

text 复制代码
group_id = tenant / user / workspace / graph partition

对于 FalkorDB 等 Provider,它甚至可能映射为不同 Graph。


Step 3:查询 previous episodes

调用方向:

text 复制代码
add_episode()
  └─ retrieve_episodes()
       └─ GraphOperations / Driver query

目的是给当前 Episode 抽取提供近期上下文。

为什么?

例如:

text 复制代码
Episode 1: 小王现在在 OpenAI 工作。
Episode 2: 他下个月要搬去旧金山。

仅看 Episode 2,LLM 不知道"他"是谁。

加入历史 Episode 后才能把"他"解析为"小王"。


Step 4:构造 EpisodicNode

核心信息:

text 复制代码
name
group_id
source
source_description
content
valid_at = reference_time
created_at = now

Graphiti 在这一刻先保留"原始事件"。


Step 5:抽取实体 extract_nodes()

text 复制代码
Episode
  ↓
Prompt context
  ↓
LLM
  ↓
Extracted EntityNode[]

此时的节点还是"候选实体",并不意味着一定要新建。


Step 6:实体解析 resolve_extracted_nodes()

text 复制代码
"Open AI"
"OpenAI"
"OpenAI Inc."

可能都是同一实体。

因此 Graphiti 会做:

  1. 名字向量;
  2. 相似候选搜索;
  3. 确定性/相似度判断;
  4. 必要时 LLM dedupe;
  5. 产生 uuid_map

uuid_map 非常关键:

text 复制代码
临时候选 UUID
    ↓
最终 canonical entity UUID

后面关系必须把 source / target pointer 改到 canonical UUID。


Step 7:边抽取 + 解析

调用:

text 复制代码
_extract_and_resolve_edges()
  ├─ extract_edges()
  ├─ resolve_edge_pointers()
  └─ resolve_extracted_edges()

输出三组:

text 复制代码
resolved_edges
invalidated_edges
new_edges

这里是 Graphiti 时间记忆机制的核心。


Step 8:实体属性/摘要更新

调用大意:

text 复制代码
extract_attributes_from_nodes(
    nodes,
    edges=new_edges,
    ...
)

注意它重点使用 new_edges,而不是把已经存在的重复事实反复灌入摘要。


Step 9:统一持久化

进入:

text 复制代码
_process_episode_data()

内部完成:

  • 生成 MENTIONS
  • Episode 记录对应 fact edge UUID;
  • 批量保存 Episode;
  • 批量保存 Entity;
  • 批量保存 EntityEdge;
  • 保存 EpisodicEdge;
  • 可选处理 Saga。

Step 10:可选更新 Community

如果配置更新社区,则:

text 复制代码
Entity/Edge changes
   ↓
community maintenance
   ↓
CommunityNode / CommunityEdge

7.3 add_episode() 主调用树

#mermaid-svg-zZWJSMunKUakFvZI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zZWJSMunKUakFvZI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zZWJSMunKUakFvZI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zZWJSMunKUakFvZI .error-icon{fill:#552222;}#mermaid-svg-zZWJSMunKUakFvZI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zZWJSMunKUakFvZI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zZWJSMunKUakFvZI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zZWJSMunKUakFvZI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zZWJSMunKUakFvZI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zZWJSMunKUakFvZI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zZWJSMunKUakFvZI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zZWJSMunKUakFvZI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zZWJSMunKUakFvZI .marker.cross{stroke:#333333;}#mermaid-svg-zZWJSMunKUakFvZI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zZWJSMunKUakFvZI p{margin:0;}#mermaid-svg-zZWJSMunKUakFvZI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zZWJSMunKUakFvZI .cluster-label text{fill:#333;}#mermaid-svg-zZWJSMunKUakFvZI .cluster-label span{color:#333;}#mermaid-svg-zZWJSMunKUakFvZI .cluster-label span p{background-color:transparent;}#mermaid-svg-zZWJSMunKUakFvZI .label text,#mermaid-svg-zZWJSMunKUakFvZI span{fill:#333;color:#333;}#mermaid-svg-zZWJSMunKUakFvZI .node rect,#mermaid-svg-zZWJSMunKUakFvZI .node circle,#mermaid-svg-zZWJSMunKUakFvZI .node ellipse,#mermaid-svg-zZWJSMunKUakFvZI .node polygon,#mermaid-svg-zZWJSMunKUakFvZI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zZWJSMunKUakFvZI .rough-node .label text,#mermaid-svg-zZWJSMunKUakFvZI .node .label text,#mermaid-svg-zZWJSMunKUakFvZI .image-shape .label,#mermaid-svg-zZWJSMunKUakFvZI .icon-shape .label{text-anchor:middle;}#mermaid-svg-zZWJSMunKUakFvZI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zZWJSMunKUakFvZI .rough-node .label,#mermaid-svg-zZWJSMunKUakFvZI .node .label,#mermaid-svg-zZWJSMunKUakFvZI .image-shape .label,#mermaid-svg-zZWJSMunKUakFvZI .icon-shape .label{text-align:center;}#mermaid-svg-zZWJSMunKUakFvZI .node.clickable{cursor:pointer;}#mermaid-svg-zZWJSMunKUakFvZI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zZWJSMunKUakFvZI .arrowheadPath{fill:#333333;}#mermaid-svg-zZWJSMunKUakFvZI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zZWJSMunKUakFvZI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zZWJSMunKUakFvZI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zZWJSMunKUakFvZI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zZWJSMunKUakFvZI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zZWJSMunKUakFvZI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zZWJSMunKUakFvZI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zZWJSMunKUakFvZI .cluster text{fill:#333;}#mermaid-svg-zZWJSMunKUakFvZI .cluster span{color:#333;}#mermaid-svg-zZWJSMunKUakFvZI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zZWJSMunKUakFvZI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zZWJSMunKUakFvZI rect.text{fill:none;stroke-width:0;}#mermaid-svg-zZWJSMunKUakFvZI .icon-shape,#mermaid-svg-zZWJSMunKUakFvZI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zZWJSMunKUakFvZI .icon-shape p,#mermaid-svg-zZWJSMunKUakFvZI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zZWJSMunKUakFvZI .icon-shape .label rect,#mermaid-svg-zZWJSMunKUakFvZI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zZWJSMunKUakFvZI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zZWJSMunKUakFvZI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zZWJSMunKUakFvZI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Graphiti.add_episode
validate_entity_types
retrieve_episodes
EpisodicNode(...)
extract_nodes
resolve_extracted_nodes
_extract_and_resolve_edges
extract_edges
resolve_edge_pointers
resolve_extracted_edges
extract_attributes_from_nodes
_process_episode_data
build_episodic_edges
add_nodes_and_edges_bulk
Saga processing
Community update


第8章 节点抽取与实体去重

核心文件:

text 复制代码
graphiti_core/utils/maintenance/node_operations.py

8.1 extract_nodes()

它不是只把当前一句话塞给 LLM。

构造的上下文大致包含:

  • 当前 Episode 内容;
  • Episode 类型;
  • Episode reference time;
  • 前序 Episodes;
  • Entity Type schema;
  • 自定义抽取 instructions;
  • 多 Episode 时的 episode index 归属约束。

8.2 根据 Episode 类型选择 Prompt

概念上:
#mermaid-svg-0j8GdU9eJFXPr9ZB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0j8GdU9eJFXPr9ZB .error-icon{fill:#552222;}#mermaid-svg-0j8GdU9eJFXPr9ZB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0j8GdU9eJFXPr9ZB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .marker.cross{stroke:#333333;}#mermaid-svg-0j8GdU9eJFXPr9ZB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0j8GdU9eJFXPr9ZB p{margin:0;}#mermaid-svg-0j8GdU9eJFXPr9ZB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster-label text{fill:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster-label span{color:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster-label span p{background-color:transparent;}#mermaid-svg-0j8GdU9eJFXPr9ZB .label text,#mermaid-svg-0j8GdU9eJFXPr9ZB span{fill:#333;color:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .node rect,#mermaid-svg-0j8GdU9eJFXPr9ZB .node circle,#mermaid-svg-0j8GdU9eJFXPr9ZB .node ellipse,#mermaid-svg-0j8GdU9eJFXPr9ZB .node polygon,#mermaid-svg-0j8GdU9eJFXPr9ZB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .rough-node .label text,#mermaid-svg-0j8GdU9eJFXPr9ZB .node .label text,#mermaid-svg-0j8GdU9eJFXPr9ZB .image-shape .label,#mermaid-svg-0j8GdU9eJFXPr9ZB .icon-shape .label{text-anchor:middle;}#mermaid-svg-0j8GdU9eJFXPr9ZB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .rough-node .label,#mermaid-svg-0j8GdU9eJFXPr9ZB .node .label,#mermaid-svg-0j8GdU9eJFXPr9ZB .image-shape .label,#mermaid-svg-0j8GdU9eJFXPr9ZB .icon-shape .label{text-align:center;}#mermaid-svg-0j8GdU9eJFXPr9ZB .node.clickable{cursor:pointer;}#mermaid-svg-0j8GdU9eJFXPr9ZB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .arrowheadPath{fill:#333333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0j8GdU9eJFXPr9ZB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0j8GdU9eJFXPr9ZB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0j8GdU9eJFXPr9ZB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster text{fill:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB .cluster span{color:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0j8GdU9eJFXPr9ZB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0j8GdU9eJFXPr9ZB rect.text{fill:none;stroke-width:0;}#mermaid-svg-0j8GdU9eJFXPr9ZB .icon-shape,#mermaid-svg-0j8GdU9eJFXPr9ZB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0j8GdU9eJFXPr9ZB .icon-shape p,#mermaid-svg-0j8GdU9eJFXPr9ZB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0j8GdU9eJFXPr9ZB .icon-shape .label rect,#mermaid-svg-0j8GdU9eJFXPr9ZB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0j8GdU9eJFXPr9ZB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0j8GdU9eJFXPr9ZB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0j8GdU9eJFXPr9ZB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} message
text
json
fact_triple
Episode
source type
message prompt
text prompt
json prompt
fact_triple prompt

Prompt 定义集中在:

text 复制代码
graphiti_core/prompts/extract_nodes.py

8.3 为什么不直接把所有抽出的 Entity 保存

因为这会产生大量重复节点:

text 复制代码
OpenAI
Open AI
openai
OpenAI Inc
OpenAI 公司

所以必须做 Entity Resolution。

8.4 resolve_extracted_nodes() 的多阶段去重

#mermaid-svg-p2Ve1s1ZZAJirftc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p2Ve1s1ZZAJirftc .error-icon{fill:#552222;}#mermaid-svg-p2Ve1s1ZZAJirftc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p2Ve1s1ZZAJirftc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p2Ve1s1ZZAJirftc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p2Ve1s1ZZAJirftc .marker.cross{stroke:#333333;}#mermaid-svg-p2Ve1s1ZZAJirftc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p2Ve1s1ZZAJirftc p{margin:0;}#mermaid-svg-p2Ve1s1ZZAJirftc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster-label text{fill:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster-label span{color:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster-label span p{background-color:transparent;}#mermaid-svg-p2Ve1s1ZZAJirftc .label text,#mermaid-svg-p2Ve1s1ZZAJirftc span{fill:#333;color:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc .node rect,#mermaid-svg-p2Ve1s1ZZAJirftc .node circle,#mermaid-svg-p2Ve1s1ZZAJirftc .node ellipse,#mermaid-svg-p2Ve1s1ZZAJirftc .node polygon,#mermaid-svg-p2Ve1s1ZZAJirftc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p2Ve1s1ZZAJirftc .rough-node .label text,#mermaid-svg-p2Ve1s1ZZAJirftc .node .label text,#mermaid-svg-p2Ve1s1ZZAJirftc .image-shape .label,#mermaid-svg-p2Ve1s1ZZAJirftc .icon-shape .label{text-anchor:middle;}#mermaid-svg-p2Ve1s1ZZAJirftc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-p2Ve1s1ZZAJirftc .rough-node .label,#mermaid-svg-p2Ve1s1ZZAJirftc .node .label,#mermaid-svg-p2Ve1s1ZZAJirftc .image-shape .label,#mermaid-svg-p2Ve1s1ZZAJirftc .icon-shape .label{text-align:center;}#mermaid-svg-p2Ve1s1ZZAJirftc .node.clickable{cursor:pointer;}#mermaid-svg-p2Ve1s1ZZAJirftc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-p2Ve1s1ZZAJirftc .arrowheadPath{fill:#333333;}#mermaid-svg-p2Ve1s1ZZAJirftc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p2Ve1s1ZZAJirftc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p2Ve1s1ZZAJirftc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2Ve1s1ZZAJirftc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-p2Ve1s1ZZAJirftc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2Ve1s1ZZAJirftc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster text{fill:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc .cluster span{color:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-p2Ve1s1ZZAJirftc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-p2Ve1s1ZZAJirftc rect.text{fill:none;stroke-width:0;}#mermaid-svg-p2Ve1s1ZZAJirftc .icon-shape,#mermaid-svg-p2Ve1s1ZZAJirftc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p2Ve1s1ZZAJirftc .icon-shape p,#mermaid-svg-p2Ve1s1ZZAJirftc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-p2Ve1s1ZZAJirftc .icon-shape .label rect,#mermaid-svg-p2Ve1s1ZZAJirftc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p2Ve1s1ZZAJirftc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-p2Ve1s1ZZAJirftc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-p2Ve1s1ZZAJirftc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

Extracted Nodes
名称归一 / 候选索引
批量生成 name embedding
node_similarity_search

召回已有节点候选
确定性匹配 / 相似度规则
仍无法确定?
LLM dedupe
Canonical EntityNode
uuid_map
duplicate pairs

关键优化思想

不是:

text 复制代码
每个实体都问一次大模型:
"它是不是已有实体?"

而是:

text 复制代码
便宜的确定性判断
   ↓
Embedding 候选缩小
   ↓
只把难判断的送给 LLM

这对成本和性能很关键。

8.5 _semantic_candidate_search()

主要步骤:

text 复制代码
nodes
  ↓
Embedder.create_batch(names)
  ↓
每个 node 做 node_similarity_search
  ↓
返回少量候选

源码中候选规模和 cosine threshold 都用于控制:

  • 召回质量;
  • LLM 后续输入长度;
  • 延迟;
  • 成本。

8.6 uuid_map 为什么是整个写入链的"粘合剂"

抽取时 Edge 可能引用新节点临时 UUID:

text 复制代码
edge.source = temp-A
edge.target = temp-B

实体去重后:

text 复制代码
temp-A -> existing-OpenAI-UUID
temp-B -> existing-Sam-UUID

因此必须:

text 复制代码
resolve_edge_pointers()

把边指向最终实体。


第9章 关系抽取、事实去重与时间失效

核心文件:

text 复制代码
graphiti_core/utils/maintenance/edge_operations.py

9.1 extract_edges()

输入:

  • Episode(s)
  • 抽出的节点
  • Episode reference time
  • edge type schema
  • edge type map
  • prompt context

输出:

text 复制代码
EntityEdge[]

每条候选 Edge 至少包含:

text 复制代码
source_node_uuid
target_node_uuid
name
fact
episodes
reference_time
valid_at / invalid_at(如果抽取到)

9.2 edge_type_map

假设:

python 复制代码
Person
Company
City

允许关系:

text 复制代码
(Person, Company) -> WORKS_AT
(Person, City)    -> LIVES_IN

Graphiti 会把这些约束形成 Prompt context。

意义:

不让 LLM 任意创造不符合业务 ontology 的关系。


9.3 resolve_extracted_edges()

这是最值得读的函数之一。

总体过程:
#mermaid-svg-MPCKdTGSWPJMBxwG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MPCKdTGSWPJMBxwG .error-icon{fill:#552222;}#mermaid-svg-MPCKdTGSWPJMBxwG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MPCKdTGSWPJMBxwG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MPCKdTGSWPJMBxwG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MPCKdTGSWPJMBxwG .marker.cross{stroke:#333333;}#mermaid-svg-MPCKdTGSWPJMBxwG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MPCKdTGSWPJMBxwG p{margin:0;}#mermaid-svg-MPCKdTGSWPJMBxwG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster-label text{fill:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster-label span{color:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster-label span p{background-color:transparent;}#mermaid-svg-MPCKdTGSWPJMBxwG .label text,#mermaid-svg-MPCKdTGSWPJMBxwG span{fill:#333;color:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG .node rect,#mermaid-svg-MPCKdTGSWPJMBxwG .node circle,#mermaid-svg-MPCKdTGSWPJMBxwG .node ellipse,#mermaid-svg-MPCKdTGSWPJMBxwG .node polygon,#mermaid-svg-MPCKdTGSWPJMBxwG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MPCKdTGSWPJMBxwG .rough-node .label text,#mermaid-svg-MPCKdTGSWPJMBxwG .node .label text,#mermaid-svg-MPCKdTGSWPJMBxwG .image-shape .label,#mermaid-svg-MPCKdTGSWPJMBxwG .icon-shape .label{text-anchor:middle;}#mermaid-svg-MPCKdTGSWPJMBxwG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MPCKdTGSWPJMBxwG .rough-node .label,#mermaid-svg-MPCKdTGSWPJMBxwG .node .label,#mermaid-svg-MPCKdTGSWPJMBxwG .image-shape .label,#mermaid-svg-MPCKdTGSWPJMBxwG .icon-shape .label{text-align:center;}#mermaid-svg-MPCKdTGSWPJMBxwG .node.clickable{cursor:pointer;}#mermaid-svg-MPCKdTGSWPJMBxwG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MPCKdTGSWPJMBxwG .arrowheadPath{fill:#333333;}#mermaid-svg-MPCKdTGSWPJMBxwG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MPCKdTGSWPJMBxwG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MPCKdTGSWPJMBxwG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MPCKdTGSWPJMBxwG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MPCKdTGSWPJMBxwG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MPCKdTGSWPJMBxwG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster text{fill:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG .cluster span{color:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MPCKdTGSWPJMBxwG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MPCKdTGSWPJMBxwG rect.text{fill:none;stroke-width:0;}#mermaid-svg-MPCKdTGSWPJMBxwG .icon-shape,#mermaid-svg-MPCKdTGSWPJMBxwG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MPCKdTGSWPJMBxwG .icon-shape p,#mermaid-svg-MPCKdTGSWPJMBxwG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MPCKdTGSWPJMBxwG .icon-shape .label rect,#mermaid-svg-MPCKdTGSWPJMBxwG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MPCKdTGSWPJMBxwG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MPCKdTGSWPJMBxwG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MPCKdTGSWPJMBxwG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



Extracted Edges
Exact duplicate fast path
生成 fact embeddings
查询相同 source/target 的 Existing Edges
搜索 broader related edges
resolve_extracted_edge
重复事实?
复用 Existing Edge

追加 provenance
矛盾事实?
时间区间检查
invalidate old edge
保存 new edge
输出 resolved / invalidated / new

9.4 三组输出分别是什么

resolved_edges

当前 Episode 最终应该关联到的 canonical facts。

可能包含:

  • 新 Edge;
  • 被认定为同一事实的 Existing Edge。

invalidated_edges

因为当前新事实而被修改有效期的旧 Edge。

new_edges

真正新创建的事实 Edge。

这个区分很重要,因为:

  • resolved 用于"当前 Episode 最终关联哪些事实";
  • invalidated 用于保存旧事实生命周期变化;
  • new 用于后续属性/摘要增量更新。

9.5 时间抽取 _extract_edge_timestamps()

如果关系抽取阶段没有得到足够准确的时间,则 Graphiti 会针对 Fact 再做时间解析。

输入大意:

text 复制代码
fact
episode reference time

输出:

text 复制代码
valid_at
invalid_at

关键思想:

时间解析使用 Episode 的 reference_time 做相对时间锚点,而不是总用当前服务器时间。

例如:

text 复制代码
Episode reference_time = 2025-03-01
内容 = "他上个月加入公司"

"上个月"应该相对于 2025-03-01,而不是系统今天。


9.6 为什么查询"同端点已有 Edge"

假设:

text 复制代码
Sam -> OpenAI

当前新 Fact:

text 复制代码
Sam works at OpenAI

已有:

text 复制代码
Sam joined OpenAI
Sam is employed by OpenAI
Sam works at OpenAI

仅做全库语义搜索成本更高,而且 source/target 是非常强的结构约束。

所以 Graphiti 会优先利用:

text 复制代码
相同 Entity pair

再结合语义去重。


第10章 Episode、MENTIONS 与事实溯源

Graphiti 与很多"只保存最终知识"的系统不同:

它保留 Episode 作为第一等对象。

10.1 双向溯源思想

#mermaid-svg-U0eRRBEPyBpIEEjj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U0eRRBEPyBpIEEjj .error-icon{fill:#552222;}#mermaid-svg-U0eRRBEPyBpIEEjj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U0eRRBEPyBpIEEjj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U0eRRBEPyBpIEEjj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U0eRRBEPyBpIEEjj .marker.cross{stroke:#333333;}#mermaid-svg-U0eRRBEPyBpIEEjj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U0eRRBEPyBpIEEjj p{margin:0;}#mermaid-svg-U0eRRBEPyBpIEEjj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster-label text{fill:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster-label span{color:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster-label span p{background-color:transparent;}#mermaid-svg-U0eRRBEPyBpIEEjj .label text,#mermaid-svg-U0eRRBEPyBpIEEjj span{fill:#333;color:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj .node rect,#mermaid-svg-U0eRRBEPyBpIEEjj .node circle,#mermaid-svg-U0eRRBEPyBpIEEjj .node ellipse,#mermaid-svg-U0eRRBEPyBpIEEjj .node polygon,#mermaid-svg-U0eRRBEPyBpIEEjj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U0eRRBEPyBpIEEjj .rough-node .label text,#mermaid-svg-U0eRRBEPyBpIEEjj .node .label text,#mermaid-svg-U0eRRBEPyBpIEEjj .image-shape .label,#mermaid-svg-U0eRRBEPyBpIEEjj .icon-shape .label{text-anchor:middle;}#mermaid-svg-U0eRRBEPyBpIEEjj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U0eRRBEPyBpIEEjj .rough-node .label,#mermaid-svg-U0eRRBEPyBpIEEjj .node .label,#mermaid-svg-U0eRRBEPyBpIEEjj .image-shape .label,#mermaid-svg-U0eRRBEPyBpIEEjj .icon-shape .label{text-align:center;}#mermaid-svg-U0eRRBEPyBpIEEjj .node.clickable{cursor:pointer;}#mermaid-svg-U0eRRBEPyBpIEEjj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U0eRRBEPyBpIEEjj .arrowheadPath{fill:#333333;}#mermaid-svg-U0eRRBEPyBpIEEjj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U0eRRBEPyBpIEEjj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U0eRRBEPyBpIEEjj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U0eRRBEPyBpIEEjj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U0eRRBEPyBpIEEjj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U0eRRBEPyBpIEEjj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster text{fill:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj .cluster span{color:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U0eRRBEPyBpIEEjj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U0eRRBEPyBpIEEjj rect.text{fill:none;stroke-width:0;}#mermaid-svg-U0eRRBEPyBpIEEjj .icon-shape,#mermaid-svg-U0eRRBEPyBpIEEjj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U0eRRBEPyBpIEEjj .icon-shape p,#mermaid-svg-U0eRRBEPyBpIEEjj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U0eRRBEPyBpIEEjj .icon-shape .label rect,#mermaid-svg-U0eRRBEPyBpIEEjj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U0eRRBEPyBpIEEjj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U0eRRBEPyBpIEEjj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U0eRRBEPyBpIEEjj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MENTIONS
MENTIONS
RELATES_TO
entity_edges contains Fact UUID
episodes contains Episode UUID
Episode
Entity A
Entity B
EntityEdge / Fact

这使系统能够回答:

  • 这个实体在哪些原始事件被提过?
  • 这个 Fact 是由哪些 Episode 支撑的?
  • 某个 Episode 抽出了哪些 Facts?
  • 如果删除 Episode,哪些 Facts 可能失去来源?

10.2 build_episodic_edges()

职责:

text 复制代码
Episode UUID + resolved entity UUIDs
          ↓
EpisodicEdge(MENTIONS)

批量抽取时还需要 node_episode_index_map

一个批次里抽出的实体到底来自哪一个 Episode?


第11章 Saga:连续会话/事件链如何组织

Saga 可以理解为:

一个带顺序的 Episode Container。

例如:

text 复制代码
"用户与小搭 8 月 19 日的一段会话"

可以作为一个 Saga。

11.1 _process_episode_data() 中的 Saga 处理

概念链:
#mermaid-svg-6I6ymEU99cidoum1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6I6ymEU99cidoum1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6I6ymEU99cidoum1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6I6ymEU99cidoum1 .error-icon{fill:#552222;}#mermaid-svg-6I6ymEU99cidoum1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6I6ymEU99cidoum1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6I6ymEU99cidoum1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6I6ymEU99cidoum1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6I6ymEU99cidoum1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6I6ymEU99cidoum1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6I6ymEU99cidoum1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6I6ymEU99cidoum1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6I6ymEU99cidoum1 .marker.cross{stroke:#333333;}#mermaid-svg-6I6ymEU99cidoum1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6I6ymEU99cidoum1 p{margin:0;}#mermaid-svg-6I6ymEU99cidoum1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6I6ymEU99cidoum1 .cluster-label text{fill:#333;}#mermaid-svg-6I6ymEU99cidoum1 .cluster-label span{color:#333;}#mermaid-svg-6I6ymEU99cidoum1 .cluster-label span p{background-color:transparent;}#mermaid-svg-6I6ymEU99cidoum1 .label text,#mermaid-svg-6I6ymEU99cidoum1 span{fill:#333;color:#333;}#mermaid-svg-6I6ymEU99cidoum1 .node rect,#mermaid-svg-6I6ymEU99cidoum1 .node circle,#mermaid-svg-6I6ymEU99cidoum1 .node ellipse,#mermaid-svg-6I6ymEU99cidoum1 .node polygon,#mermaid-svg-6I6ymEU99cidoum1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6I6ymEU99cidoum1 .rough-node .label text,#mermaid-svg-6I6ymEU99cidoum1 .node .label text,#mermaid-svg-6I6ymEU99cidoum1 .image-shape .label,#mermaid-svg-6I6ymEU99cidoum1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-6I6ymEU99cidoum1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6I6ymEU99cidoum1 .rough-node .label,#mermaid-svg-6I6ymEU99cidoum1 .node .label,#mermaid-svg-6I6ymEU99cidoum1 .image-shape .label,#mermaid-svg-6I6ymEU99cidoum1 .icon-shape .label{text-align:center;}#mermaid-svg-6I6ymEU99cidoum1 .node.clickable{cursor:pointer;}#mermaid-svg-6I6ymEU99cidoum1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6I6ymEU99cidoum1 .arrowheadPath{fill:#333333;}#mermaid-svg-6I6ymEU99cidoum1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6I6ymEU99cidoum1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6I6ymEU99cidoum1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6I6ymEU99cidoum1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6I6ymEU99cidoum1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6I6ymEU99cidoum1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6I6ymEU99cidoum1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6I6ymEU99cidoum1 .cluster text{fill:#333;}#mermaid-svg-6I6ymEU99cidoum1 .cluster span{color:#333;}#mermaid-svg-6I6ymEU99cidoum1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6I6ymEU99cidoum1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6I6ymEU99cidoum1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-6I6ymEU99cidoum1 .icon-shape,#mermaid-svg-6I6ymEU99cidoum1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6I6ymEU99cidoum1 .icon-shape p,#mermaid-svg-6I6ymEU99cidoum1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6I6ymEU99cidoum1 .icon-shape .label rect,#mermaid-svg-6I6ymEU99cidoum1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6I6ymEU99cidoum1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6I6ymEU99cidoum1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6I6ymEU99cidoum1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 当前 Episode
get/create SagaNode
找到 previous episode
创建 NEXT_EPISODE
创建 HAS_EPISODE
更新 first_episode_uuid
更新 last_episode_uuid
save Saga

11.2 为什么 Saga 与 group_id 不是一回事

group_id

更像数据隔离范围。

Saga:

是这个隔离范围内部的一条具体事件序列。

例:

text 复制代码
group_id = user_001

Saga A = 与工作项目相关的一次长会话
Saga B = 旅行规划会话
Saga C = 某个持续 7 天的任务事件链

第12章 批量写入 add_episode_bulk()

单条 add_episode() 强调正确的时序上下文。

批量路径则更强调:

  • 减少 LLM 调用;
  • 批量 Embedding;
  • 并发;
  • 批量 DB IO。

12.1 Bulk 主流程

#mermaid-svg-5PccAJWxtTATljYL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5PccAJWxtTATljYL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5PccAJWxtTATljYL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5PccAJWxtTATljYL .error-icon{fill:#552222;}#mermaid-svg-5PccAJWxtTATljYL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5PccAJWxtTATljYL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5PccAJWxtTATljYL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5PccAJWxtTATljYL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5PccAJWxtTATljYL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5PccAJWxtTATljYL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5PccAJWxtTATljYL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5PccAJWxtTATljYL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5PccAJWxtTATljYL .marker.cross{stroke:#333333;}#mermaid-svg-5PccAJWxtTATljYL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5PccAJWxtTATljYL p{margin:0;}#mermaid-svg-5PccAJWxtTATljYL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5PccAJWxtTATljYL .cluster-label text{fill:#333;}#mermaid-svg-5PccAJWxtTATljYL .cluster-label span{color:#333;}#mermaid-svg-5PccAJWxtTATljYL .cluster-label span p{background-color:transparent;}#mermaid-svg-5PccAJWxtTATljYL .label text,#mermaid-svg-5PccAJWxtTATljYL span{fill:#333;color:#333;}#mermaid-svg-5PccAJWxtTATljYL .node rect,#mermaid-svg-5PccAJWxtTATljYL .node circle,#mermaid-svg-5PccAJWxtTATljYL .node ellipse,#mermaid-svg-5PccAJWxtTATljYL .node polygon,#mermaid-svg-5PccAJWxtTATljYL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5PccAJWxtTATljYL .rough-node .label text,#mermaid-svg-5PccAJWxtTATljYL .node .label text,#mermaid-svg-5PccAJWxtTATljYL .image-shape .label,#mermaid-svg-5PccAJWxtTATljYL .icon-shape .label{text-anchor:middle;}#mermaid-svg-5PccAJWxtTATljYL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5PccAJWxtTATljYL .rough-node .label,#mermaid-svg-5PccAJWxtTATljYL .node .label,#mermaid-svg-5PccAJWxtTATljYL .image-shape .label,#mermaid-svg-5PccAJWxtTATljYL .icon-shape .label{text-align:center;}#mermaid-svg-5PccAJWxtTATljYL .node.clickable{cursor:pointer;}#mermaid-svg-5PccAJWxtTATljYL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5PccAJWxtTATljYL .arrowheadPath{fill:#333333;}#mermaid-svg-5PccAJWxtTATljYL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5PccAJWxtTATljYL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5PccAJWxtTATljYL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5PccAJWxtTATljYL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5PccAJWxtTATljYL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5PccAJWxtTATljYL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5PccAJWxtTATljYL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5PccAJWxtTATljYL .cluster text{fill:#333;}#mermaid-svg-5PccAJWxtTATljYL .cluster span{color:#333;}#mermaid-svg-5PccAJWxtTATljYL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5PccAJWxtTATljYL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5PccAJWxtTATljYL rect.text{fill:none;stroke-width:0;}#mermaid-svg-5PccAJWxtTATljYL .icon-shape,#mermaid-svg-5PccAJWxtTATljYL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5PccAJWxtTATljYL .icon-shape p,#mermaid-svg-5PccAJWxtTATljYL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5PccAJWxtTATljYL .icon-shape .label rect,#mermaid-svg-5PccAJWxtTATljYL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5PccAJWxtTATljYL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5PccAJWxtTATljYL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5PccAJWxtTATljYL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} add_episode_bulk
构造 EpisodicNodes
预保存 / 准备 Episodes
retrieve_previous_episodes_bulk
_extract_and_dedupe_nodes_bulk
Combined Extraction
build_episodic_edges
resolve_edge_pointers
dedupe_edges_bulk
_resolve_nodes_and_edges_bulk
修正 EpisodicEdge 指针
add_nodes_and_edges_bulk
Bulk Results

12.2 Combined Extraction

在维护目录中有:

text 复制代码
combined_extraction.py

其重要价值是:

在适合的路径中,把 node + edge extraction 合并到一次结构化 LLM 调用中。

这样比:

text 复制代码
LLM 抽节点
+
LLM 抽边

更省请求开销。

12.3 为什么官方更强调 Episode 顺序

如果事件之间高度依赖前文:

text 复制代码
E1: 我现在住北京
E2: 下周我要搬家
E3: 新地址在上海

严格按 reference time 顺序逐条加入更容易正确建立上下文和事实生命周期。

Bulk 更适合:

  • 大规模历史灌入;
  • 可并行处理的数据;
  • 已有比较明确时间和结构的数据。

第13章 搜索系统总览:不是单纯向量检索

核心:

text 复制代码
graphiti_core/search/

Graphiti 高级搜索同时面对四类对象:

  1. EntityEdge
  2. EntityNode
  3. EpisodicNode
  4. CommunityNode

13.1 search()search_()

Graphiti.search()

更方便的高层 API:

  • 主要返回 EntityEdge;
  • 默认走 Edge Hybrid Search;
  • 如果给中心节点,可以使用 node-distance recipe。

Graphiti.search_()

更完整的 API:

text 复制代码
SearchResults
├─ edges
├─ nodes
├─ episodes
├─ communities
├─ edge_reranker_scores
├─ node_reranker_scores
├─ episode_reranker_scores
└─ community_reranker_scores

默认偏向综合 Hybrid + Cross-Encoder 配方。


13.2 搜索流水线

#mermaid-svg-lNQVcUlJxDclM3yB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lNQVcUlJxDclM3yB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lNQVcUlJxDclM3yB .error-icon{fill:#552222;}#mermaid-svg-lNQVcUlJxDclM3yB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lNQVcUlJxDclM3yB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lNQVcUlJxDclM3yB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lNQVcUlJxDclM3yB .marker.cross{stroke:#333333;}#mermaid-svg-lNQVcUlJxDclM3yB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lNQVcUlJxDclM3yB p{margin:0;}#mermaid-svg-lNQVcUlJxDclM3yB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lNQVcUlJxDclM3yB .cluster-label text{fill:#333;}#mermaid-svg-lNQVcUlJxDclM3yB .cluster-label span{color:#333;}#mermaid-svg-lNQVcUlJxDclM3yB .cluster-label span p{background-color:transparent;}#mermaid-svg-lNQVcUlJxDclM3yB .label text,#mermaid-svg-lNQVcUlJxDclM3yB span{fill:#333;color:#333;}#mermaid-svg-lNQVcUlJxDclM3yB .node rect,#mermaid-svg-lNQVcUlJxDclM3yB .node circle,#mermaid-svg-lNQVcUlJxDclM3yB .node ellipse,#mermaid-svg-lNQVcUlJxDclM3yB .node polygon,#mermaid-svg-lNQVcUlJxDclM3yB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lNQVcUlJxDclM3yB .rough-node .label text,#mermaid-svg-lNQVcUlJxDclM3yB .node .label text,#mermaid-svg-lNQVcUlJxDclM3yB .image-shape .label,#mermaid-svg-lNQVcUlJxDclM3yB .icon-shape .label{text-anchor:middle;}#mermaid-svg-lNQVcUlJxDclM3yB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lNQVcUlJxDclM3yB .rough-node .label,#mermaid-svg-lNQVcUlJxDclM3yB .node .label,#mermaid-svg-lNQVcUlJxDclM3yB .image-shape .label,#mermaid-svg-lNQVcUlJxDclM3yB .icon-shape .label{text-align:center;}#mermaid-svg-lNQVcUlJxDclM3yB .node.clickable{cursor:pointer;}#mermaid-svg-lNQVcUlJxDclM3yB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lNQVcUlJxDclM3yB .arrowheadPath{fill:#333333;}#mermaid-svg-lNQVcUlJxDclM3yB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lNQVcUlJxDclM3yB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lNQVcUlJxDclM3yB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lNQVcUlJxDclM3yB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lNQVcUlJxDclM3yB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lNQVcUlJxDclM3yB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lNQVcUlJxDclM3yB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lNQVcUlJxDclM3yB .cluster text{fill:#333;}#mermaid-svg-lNQVcUlJxDclM3yB .cluster span{color:#333;}#mermaid-svg-lNQVcUlJxDclM3yB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lNQVcUlJxDclM3yB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lNQVcUlJxDclM3yB rect.text{fill:none;stroke-width:0;}#mermaid-svg-lNQVcUlJxDclM3yB .icon-shape,#mermaid-svg-lNQVcUlJxDclM3yB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lNQVcUlJxDclM3yB .icon-shape p,#mermaid-svg-lNQVcUlJxDclM3yB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lNQVcUlJxDclM3yB .icon-shape .label rect,#mermaid-svg-lNQVcUlJxDclM3yB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lNQVcUlJxDclM3yB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lNQVcUlJxDclM3yB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lNQVcUlJxDclM3yB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可选重排
融合 / 去重
候选召回
Query
BM25

字面匹配
Cosine Similarity

语义匹配
BFS

图结构扩展
UUID 去重
RRF
MMR
Cross-Encoder
Node Distance
Episode Mentions
Top-K Results

注意:

不同 SearchConfig 不一定同时使用图中的所有模块。


13.3 四种对象的搜索能力

对象 召回方式
Edge BM25 / Cosine / BFS
Node BM25 / Cosine / BFS
Episode BM25
Community BM25 / Cosine

随后各自选择不同 reranker。


第14章 BM25、Cosine、BFS、RRF、MMR、Cross-Encoder

14.1 BM25

全称通常写作:

Best Matching 25

它属于经典全文检索 ranking 算法。

擅长:

  • 专有名词;
  • 产品型号;
  • 人名;
  • 精确关键词;
  • 词频相关性。

例如 Query:

text 复制代码
ESP32-P4

BM25 往往比纯语义 Embedding 更稳。


14.2 Cosine Similarity

比较两个向量方向的相似程度。

在 Graphiti 中通常:

text 复制代码
Query
  ↓
Embedding
  ↓
与 Node/Edge/Community embedding 比较

擅长找:

字面不一样但语义相近的内容。


14.3 BFS

BFS:

Breadth-First Search,广度优先搜索

它不是文本检索,而是图遍历。

例如:

text 复制代码
User
 ├─ works_at -> Company
 ├─ owns -> Device
 └─ lives_in -> City

如果一个相关 Node 已经被召回,BFS 可以进一步扩展它附近的结构上下文。


14.4 RRF

RRF:

Reciprocal Rank Fusion,倒数排名融合

它解决:

text 复制代码
BM25 有一个排名
Vector 有一个排名
BFS 又有一个排名

不同检索器的原始 score 完全不是一个尺度,直接加分不合理。

RRF 主要依据:

候选在不同结果列表中排第几。

概念公式:

text 复制代码
score(d) = Σ 1 / (k + rank_i(d))

所以它非常适合融合 heterogeneous retrievers。


14.5 MMR

MMR:

Maximal Marginal Relevance,最大边际相关性

它同时追求:

  1. 跟 Query 相关;
  2. 结果之间不要太重复。

如果 Top-10 全都在说同一个事实,只是文字换了说法,对 Agent 没有价值。

MMR 会增加结果多样性。


14.6 Cross-Encoder

普通 Embedding:

text 复制代码
Query -> embedding
Doc   -> embedding
        ↓
     cosine

Cross-Encoder:

text 复制代码
[Query, Candidate]
       ↓
同一个模型联合编码
       ↓
relevance score

优点:

  • 精排通常更准。

缺点:

  • 每个 Query-Candidate pair 都要重新推理;
  • 贵;
  • 慢。

因此 Graphiti 的合理方式是:

text 复制代码
便宜召回一批候选
  ↓
RRF 粗融合
  ↓
缩小候选集
  ↓
Cross-Encoder 精排

14.7 Node Distance

有中心实体时:

text 复制代码
center_node_uuid = User

Graphiti 可以根据图结构距离重排:

text 复制代码
User -> direct edge

通常比:

text 复制代码
User -> A -> B -> C -> edge

更强相关。


14.8 Episode Mentions

一个 Fact / Entity 被很多 Episode 反复提及,可能意味着它具有更强的"记忆显著性"。

因此 mention 次数可以作为 reranking 信号。


第15章 SearchConfig 与搜索配方

核心:

text 复制代码
graphiti_core/search/search_config.py
graphiti_core/search/search_config_recipes.py

15.1 方法枚举

text 复制代码
cosine_similarity
bm25
bfs
text 复制代码
cosine_similarity
bm25
bfs
text 复制代码
bm25
text 复制代码
cosine_similarity
bm25

15.2 Reranker 类型

Edge/Node 常见:

  • rrf
  • node_distance
  • episode_mentions
  • mmr
  • cross_encoder

Episode:

  • rrf
  • cross_encoder

Community:

  • rrf
  • mmr
  • cross_encoder

15.3 默认综合 Cross-Encoder 配方

概念上:
#mermaid-svg-CVXggxwwM9tFgSbA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CVXggxwwM9tFgSbA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CVXggxwwM9tFgSbA .error-icon{fill:#552222;}#mermaid-svg-CVXggxwwM9tFgSbA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CVXggxwwM9tFgSbA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CVXggxwwM9tFgSbA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CVXggxwwM9tFgSbA .marker.cross{stroke:#333333;}#mermaid-svg-CVXggxwwM9tFgSbA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CVXggxwwM9tFgSbA p{margin:0;}#mermaid-svg-CVXggxwwM9tFgSbA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CVXggxwwM9tFgSbA .cluster-label text{fill:#333;}#mermaid-svg-CVXggxwwM9tFgSbA .cluster-label span{color:#333;}#mermaid-svg-CVXggxwwM9tFgSbA .cluster-label span p{background-color:transparent;}#mermaid-svg-CVXggxwwM9tFgSbA .label text,#mermaid-svg-CVXggxwwM9tFgSbA span{fill:#333;color:#333;}#mermaid-svg-CVXggxwwM9tFgSbA .node rect,#mermaid-svg-CVXggxwwM9tFgSbA .node circle,#mermaid-svg-CVXggxwwM9tFgSbA .node ellipse,#mermaid-svg-CVXggxwwM9tFgSbA .node polygon,#mermaid-svg-CVXggxwwM9tFgSbA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CVXggxwwM9tFgSbA .rough-node .label text,#mermaid-svg-CVXggxwwM9tFgSbA .node .label text,#mermaid-svg-CVXggxwwM9tFgSbA .image-shape .label,#mermaid-svg-CVXggxwwM9tFgSbA .icon-shape .label{text-anchor:middle;}#mermaid-svg-CVXggxwwM9tFgSbA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CVXggxwwM9tFgSbA .rough-node .label,#mermaid-svg-CVXggxwwM9tFgSbA .node .label,#mermaid-svg-CVXggxwwM9tFgSbA .image-shape .label,#mermaid-svg-CVXggxwwM9tFgSbA .icon-shape .label{text-align:center;}#mermaid-svg-CVXggxwwM9tFgSbA .node.clickable{cursor:pointer;}#mermaid-svg-CVXggxwwM9tFgSbA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CVXggxwwM9tFgSbA .arrowheadPath{fill:#333333;}#mermaid-svg-CVXggxwwM9tFgSbA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CVXggxwwM9tFgSbA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CVXggxwwM9tFgSbA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CVXggxwwM9tFgSbA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CVXggxwwM9tFgSbA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CVXggxwwM9tFgSbA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CVXggxwwM9tFgSbA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CVXggxwwM9tFgSbA .cluster text{fill:#333;}#mermaid-svg-CVXggxwwM9tFgSbA .cluster span{color:#333;}#mermaid-svg-CVXggxwwM9tFgSbA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CVXggxwwM9tFgSbA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CVXggxwwM9tFgSbA rect.text{fill:none;stroke-width:0;}#mermaid-svg-CVXggxwwM9tFgSbA .icon-shape,#mermaid-svg-CVXggxwwM9tFgSbA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CVXggxwwM9tFgSbA .icon-shape p,#mermaid-svg-CVXggxwwM9tFgSbA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CVXggxwwM9tFgSbA .icon-shape .label rect,#mermaid-svg-CVXggxwwM9tFgSbA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CVXggxwwM9tFgSbA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CVXggxwwM9tFgSbA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CVXggxwwM9tFgSbA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Query
Edges

BM25 + Cosine + BFS
Nodes

BM25 + Cosine + BFS
Episodes

BM25
Communities

BM25 + Cosine
Cross-Encoder rerank
Cross-Encoder rerank
Cross-Encoder rerank
Cross-Encoder rerank


15.4 search/search.py 的顶层并发

顶层 search(...) 会:

  1. 检查是否需要 Query Embedding;
  2. 只有配置包含 Vector/MMR 时才生成 Embedding;
  3. 并发运行:
    • edge_search
    • node_search
    • episode_search
    • community_search
  4. 最后拼成 SearchResults

这体现一个很好的性能设计:

不需要向量的搜索,不额外调用 Embedding API。


15.5 edge_search() 详细流程

#mermaid-svg-JwjJZWQlpKgIKvo7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JwjJZWQlpKgIKvo7 .error-icon{fill:#552222;}#mermaid-svg-JwjJZWQlpKgIKvo7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JwjJZWQlpKgIKvo7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .marker.cross{stroke:#333333;}#mermaid-svg-JwjJZWQlpKgIKvo7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JwjJZWQlpKgIKvo7 p{margin:0;}#mermaid-svg-JwjJZWQlpKgIKvo7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster-label text{fill:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster-label span{color:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster-label span p{background-color:transparent;}#mermaid-svg-JwjJZWQlpKgIKvo7 .label text,#mermaid-svg-JwjJZWQlpKgIKvo7 span{fill:#333;color:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .node rect,#mermaid-svg-JwjJZWQlpKgIKvo7 .node circle,#mermaid-svg-JwjJZWQlpKgIKvo7 .node ellipse,#mermaid-svg-JwjJZWQlpKgIKvo7 .node polygon,#mermaid-svg-JwjJZWQlpKgIKvo7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .rough-node .label text,#mermaid-svg-JwjJZWQlpKgIKvo7 .node .label text,#mermaid-svg-JwjJZWQlpKgIKvo7 .image-shape .label,#mermaid-svg-JwjJZWQlpKgIKvo7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JwjJZWQlpKgIKvo7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .rough-node .label,#mermaid-svg-JwjJZWQlpKgIKvo7 .node .label,#mermaid-svg-JwjJZWQlpKgIKvo7 .image-shape .label,#mermaid-svg-JwjJZWQlpKgIKvo7 .icon-shape .label{text-align:center;}#mermaid-svg-JwjJZWQlpKgIKvo7 .node.clickable{cursor:pointer;}#mermaid-svg-JwjJZWQlpKgIKvo7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .arrowheadPath{fill:#333333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JwjJZWQlpKgIKvo7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JwjJZWQlpKgIKvo7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JwjJZWQlpKgIKvo7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster text{fill:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 .cluster span{color:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JwjJZWQlpKgIKvo7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JwjJZWQlpKgIKvo7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JwjJZWQlpKgIKvo7 .icon-shape,#mermaid-svg-JwjJZWQlpKgIKvo7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JwjJZWQlpKgIKvo7 .icon-shape p,#mermaid-svg-JwjJZWQlpKgIKvo7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JwjJZWQlpKgIKvo7 .icon-shape .label rect,#mermaid-svg-JwjJZWQlpKgIKvo7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JwjJZWQlpKgIKvo7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JwjJZWQlpKgIKvo7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JwjJZWQlpKgIKvo7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} edge_search
Configured methods
edge_fulltext_search
edge_similarity_search
edge_bfs_search
并发召回候选
UUID dedupe
reranker
RRF
MMR
CrossEncoder.rank
node_distance_reranker
episode mentions

BFS 的一个细节

如果配置 BFS,但调用者没有提供明确 BFS origin nodes:

Graphiti 可以先从初步召回结果里的 source nodes 作为种子,再展开 BFS。

这使"文本召回 → 图结构扩展"自然连接起来。


第16章 Node / Edge Namespace:新一代数据访问 API

Graphiti 当前源码中有一个非常重要的架构演进。

设计文档:

text 复制代码
spec/driver-operations-redesign.md

16.1 新目标

从过去"数据 Model 自己会访问数据库":

text 复制代码
EntityNode.save(driver)
EntityEdge.get_by_uuid(driver)

逐步转向:

text 复制代码
Graphiti
  ↓
Namespace
  ↓
Operations
  ↓
GraphDriver

16.2 新架构

#mermaid-svg-5SXU8posSLm4dbpE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5SXU8posSLm4dbpE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5SXU8posSLm4dbpE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5SXU8posSLm4dbpE .error-icon{fill:#552222;}#mermaid-svg-5SXU8posSLm4dbpE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5SXU8posSLm4dbpE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5SXU8posSLm4dbpE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5SXU8posSLm4dbpE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5SXU8posSLm4dbpE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5SXU8posSLm4dbpE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5SXU8posSLm4dbpE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5SXU8posSLm4dbpE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5SXU8posSLm4dbpE .marker.cross{stroke:#333333;}#mermaid-svg-5SXU8posSLm4dbpE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5SXU8posSLm4dbpE p{margin:0;}#mermaid-svg-5SXU8posSLm4dbpE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5SXU8posSLm4dbpE .cluster-label text{fill:#333;}#mermaid-svg-5SXU8posSLm4dbpE .cluster-label span{color:#333;}#mermaid-svg-5SXU8posSLm4dbpE .cluster-label span p{background-color:transparent;}#mermaid-svg-5SXU8posSLm4dbpE .label text,#mermaid-svg-5SXU8posSLm4dbpE span{fill:#333;color:#333;}#mermaid-svg-5SXU8posSLm4dbpE .node rect,#mermaid-svg-5SXU8posSLm4dbpE .node circle,#mermaid-svg-5SXU8posSLm4dbpE .node ellipse,#mermaid-svg-5SXU8posSLm4dbpE .node polygon,#mermaid-svg-5SXU8posSLm4dbpE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5SXU8posSLm4dbpE .rough-node .label text,#mermaid-svg-5SXU8posSLm4dbpE .node .label text,#mermaid-svg-5SXU8posSLm4dbpE .image-shape .label,#mermaid-svg-5SXU8posSLm4dbpE .icon-shape .label{text-anchor:middle;}#mermaid-svg-5SXU8posSLm4dbpE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5SXU8posSLm4dbpE .rough-node .label,#mermaid-svg-5SXU8posSLm4dbpE .node .label,#mermaid-svg-5SXU8posSLm4dbpE .image-shape .label,#mermaid-svg-5SXU8posSLm4dbpE .icon-shape .label{text-align:center;}#mermaid-svg-5SXU8posSLm4dbpE .node.clickable{cursor:pointer;}#mermaid-svg-5SXU8posSLm4dbpE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5SXU8posSLm4dbpE .arrowheadPath{fill:#333333;}#mermaid-svg-5SXU8posSLm4dbpE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5SXU8posSLm4dbpE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5SXU8posSLm4dbpE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5SXU8posSLm4dbpE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5SXU8posSLm4dbpE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5SXU8posSLm4dbpE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5SXU8posSLm4dbpE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5SXU8posSLm4dbpE .cluster text{fill:#333;}#mermaid-svg-5SXU8posSLm4dbpE .cluster span{color:#333;}#mermaid-svg-5SXU8posSLm4dbpE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5SXU8posSLm4dbpE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5SXU8posSLm4dbpE rect.text{fill:none;stroke-width:0;}#mermaid-svg-5SXU8posSLm4dbpE .icon-shape,#mermaid-svg-5SXU8posSLm4dbpE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5SXU8posSLm4dbpE .icon-shape p,#mermaid-svg-5SXU8posSLm4dbpE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5SXU8posSLm4dbpE .icon-shape .label rect,#mermaid-svg-5SXU8posSLm4dbpE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5SXU8posSLm4dbpE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5SXU8posSLm4dbpE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5SXU8posSLm4dbpE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Application
Graphiti
graphiti.nodes.entity

graphiti.edges.entity
EntityNodeOps / EntityEdgeOps / ...
GraphDriver
Neo4j/FalkorDB/Neptune/Kuzu

16.3 为什么加 Namespace

例如:

text 复制代码
graphiti.nodes.entity
graphiti.nodes.episode
graphiti.nodes.community
graphiti.nodes.saga

graphiti.edges.entity
graphiti.edges.episodic
...

好处:

  • API 分类更清晰;
  • Node / Edge Model 可以越来越"纯数据化";
  • DB IO 不再散落在 Model;
  • Namespace 可以负责 embedding/tracing;
  • Operations 专注 DB 操作契约。

16.4 为什么源码里还能看到旧路径

因为 Driver Operations redesign 文档明确是 渐进迁移

所以当前读源码会看到:

旧路径

text 复制代码
model.save(driver)
graph_operations_interface
search_interface

新路径

text 复制代码
graphiti.nodes...
graphiti.edges...
driver.entity_node_ops
driver.entity_edge_ops
driver.search_ops
...

这不是"重复设计失控",而是:

兼容迁移阶段同时存在两套 API。

读源码时务必意识到这一点。


第17章 Driver 层:Neo4j / FalkorDB / Neptune / Kuzu

核心:

text 复制代码
graphiti_core/driver/

17.1 GraphDriver

driver.py 定义数据库抽象。

Provider 枚举包括:

text 复制代码
NEO4J
FALKORDB
KUZU
NEPTUNE

17.2 Driver 的基本职责

#mermaid-svg-IiB4AajGt7QUAlOs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IiB4AajGt7QUAlOs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IiB4AajGt7QUAlOs .error-icon{fill:#552222;}#mermaid-svg-IiB4AajGt7QUAlOs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IiB4AajGt7QUAlOs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IiB4AajGt7QUAlOs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IiB4AajGt7QUAlOs .marker.cross{stroke:#333333;}#mermaid-svg-IiB4AajGt7QUAlOs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IiB4AajGt7QUAlOs p{margin:0;}#mermaid-svg-IiB4AajGt7QUAlOs g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-IiB4AajGt7QUAlOs g.classGroup text .title{font-weight:bolder;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster-label text{fill:#333;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster-label span{color:#333;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster-label span p{background-color:transparent;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster text{fill:#333;}#mermaid-svg-IiB4AajGt7QUAlOs .cluster span{color:#333;}#mermaid-svg-IiB4AajGt7QUAlOs .nodeLabel,#mermaid-svg-IiB4AajGt7QUAlOs .edgeLabel{color:#131300;}#mermaid-svg-IiB4AajGt7QUAlOs .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-IiB4AajGt7QUAlOs .label text{fill:#131300;}#mermaid-svg-IiB4AajGt7QUAlOs .labelBkg{background:#ECECFF;}#mermaid-svg-IiB4AajGt7QUAlOs .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-IiB4AajGt7QUAlOs .classTitle{font-weight:bolder;}#mermaid-svg-IiB4AajGt7QUAlOs .node rect,#mermaid-svg-IiB4AajGt7QUAlOs .node circle,#mermaid-svg-IiB4AajGt7QUAlOs .node ellipse,#mermaid-svg-IiB4AajGt7QUAlOs .node polygon,#mermaid-svg-IiB4AajGt7QUAlOs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IiB4AajGt7QUAlOs .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs g.clickable{cursor:pointer;}#mermaid-svg-IiB4AajGt7QUAlOs g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-IiB4AajGt7QUAlOs g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-IiB4AajGt7QUAlOs .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-IiB4AajGt7QUAlOs .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-IiB4AajGt7QUAlOs .dashed-line{stroke-dasharray:3;}#mermaid-svg-IiB4AajGt7QUAlOs .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-IiB4AajGt7QUAlOs #compositionStart,#mermaid-svg-IiB4AajGt7QUAlOs .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #compositionEnd,#mermaid-svg-IiB4AajGt7QUAlOs .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #dependencyStart,#mermaid-svg-IiB4AajGt7QUAlOs .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #dependencyStart,#mermaid-svg-IiB4AajGt7QUAlOs .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #extensionStart,#mermaid-svg-IiB4AajGt7QUAlOs .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #extensionEnd,#mermaid-svg-IiB4AajGt7QUAlOs .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #aggregationStart,#mermaid-svg-IiB4AajGt7QUAlOs .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #aggregationEnd,#mermaid-svg-IiB4AajGt7QUAlOs .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #lollipopStart,#mermaid-svg-IiB4AajGt7QUAlOs .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs #lollipopEnd,#mermaid-svg-IiB4AajGt7QUAlOs .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-IiB4AajGt7QUAlOs .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-IiB4AajGt7QUAlOs .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IiB4AajGt7QUAlOs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IiB4AajGt7QUAlOs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IiB4AajGt7QUAlOs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<abstract>>
GraphDriver
+entity_node_ops
+episode_node_ops
+entity_edge_ops
+search_ops
+graph_ops
+execute_query()
+session()
+close()
+build_indices_and_constraints()
+delete_all_indexes()
+with_database()
+clone()
+transaction()
Neo4jDriver
FalkorDriver
NeptuneDriver
KuzuDriver

17.3 QueryExecutor

它是更底层的 Query 执行契约。

思路:

text 复制代码
QueryExecutor
      ↑
Operations interfaces
      ↑
GraphDriver
      ↑
Namespaces
      ↑
Graphiti

17.4 Operations 分类

driver/operations/ 按对象拆分:

text 复制代码
community_edge_ops.py
community_node_ops.py
entity_edge_ops.py
entity_node_ops.py
episode_node_ops.py
episodic_edge_ops.py
graph_ops.py
graph_utils.py
has_episode_edge_ops.py
next_episode_edge_ops.py
saga_node_ops.py
search_ops.py

这比过去一个巨大的 Graph Operations interface 更容易维护。


17.5 Provider 特性

Neo4j

典型属性图数据库。

适合:

  • Cypher;
  • 节点/边查询;
  • vector/fulltext indexes;
  • 原生 transaction。

FalkorDB

基于 Redis 生态的图数据库。

Graphiti 对它有特殊的多 group_id 处理逻辑:

group 可能对应不同 graph,需要 clone driver 后分别查询并合并。

Neptune

AWS 图数据库。

Graphiti 还包含与 AWS/OpenSearch 相关的处理路径。

Kuzu

嵌入式图数据库方向。

项目结构中仍保留 Driver,但要结合当前项目文档看其支持状态;源码迁移过程中部分路径带有兼容/历史性质。


17.6 Transaction 抽象

#mermaid-svg-CwgDTKj1UmsLn0y0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CwgDTKj1UmsLn0y0 .error-icon{fill:#552222;}#mermaid-svg-CwgDTKj1UmsLn0y0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CwgDTKj1UmsLn0y0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .marker.cross{stroke:#333333;}#mermaid-svg-CwgDTKj1UmsLn0y0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CwgDTKj1UmsLn0y0 p{margin:0;}#mermaid-svg-CwgDTKj1UmsLn0y0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster-label text{fill:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster-label span{color:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster-label span p{background-color:transparent;}#mermaid-svg-CwgDTKj1UmsLn0y0 .label text,#mermaid-svg-CwgDTKj1UmsLn0y0 span{fill:#333;color:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .node rect,#mermaid-svg-CwgDTKj1UmsLn0y0 .node circle,#mermaid-svg-CwgDTKj1UmsLn0y0 .node ellipse,#mermaid-svg-CwgDTKj1UmsLn0y0 .node polygon,#mermaid-svg-CwgDTKj1UmsLn0y0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .rough-node .label text,#mermaid-svg-CwgDTKj1UmsLn0y0 .node .label text,#mermaid-svg-CwgDTKj1UmsLn0y0 .image-shape .label,#mermaid-svg-CwgDTKj1UmsLn0y0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-CwgDTKj1UmsLn0y0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .rough-node .label,#mermaid-svg-CwgDTKj1UmsLn0y0 .node .label,#mermaid-svg-CwgDTKj1UmsLn0y0 .image-shape .label,#mermaid-svg-CwgDTKj1UmsLn0y0 .icon-shape .label{text-align:center;}#mermaid-svg-CwgDTKj1UmsLn0y0 .node.clickable{cursor:pointer;}#mermaid-svg-CwgDTKj1UmsLn0y0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .arrowheadPath{fill:#333333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CwgDTKj1UmsLn0y0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CwgDTKj1UmsLn0y0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CwgDTKj1UmsLn0y0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster text{fill:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 .cluster span{color:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CwgDTKj1UmsLn0y0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CwgDTKj1UmsLn0y0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-CwgDTKj1UmsLn0y0 .icon-shape,#mermaid-svg-CwgDTKj1UmsLn0y0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CwgDTKj1UmsLn0y0 .icon-shape p,#mermaid-svg-CwgDTKj1UmsLn0y0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CwgDTKj1UmsLn0y0 .icon-shape .label rect,#mermaid-svg-CwgDTKj1UmsLn0y0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CwgDTKj1UmsLn0y0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CwgDTKj1UmsLn0y0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CwgDTKj1UmsLn0y0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} driver.transaction()
Provider
Neo4j

native transaction

commit / rollback
其他 Provider

wrapper / provider-specific semantics

设计重点:

上层 Operations 使用统一 transaction contract,而不用知道底层数据库差异。


第18章 LLM Client 层

核心:

text 复制代码
graphiti_core/llm_client/

18.1 主要抽象

text 复制代码
LLMClient
├─ OpenAIClient
├─ AzureOpenAIClient
├─ AnthropicClient
├─ GeminiClient
├─ GroqClient
├─ OpenAIGenericClient
└─ GLiNER2Client

18.2 LLMClient.generate_response()

它不仅是:

text 复制代码
messages -> API

还承担:

  • response model schema 注入;
  • structured output;
  • 字符清洗;
  • 多语言 extraction instruction;
  • cache;
  • retry;
  • token tracking;
  • tracing;
  • error normalization。

18.3 调用流程

TokenTracker/Tracer Provider API Cache Pydantic Response Model LLMClient Extraction/Dedupe TokenTracker/Tracer Provider API Cache Pydantic Response Model LLMClient Extraction/Dedupe #mermaid-svg-eRbEIelOUw3Qxtz3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eRbEIelOUw3Qxtz3 .error-icon{fill:#552222;}#mermaid-svg-eRbEIelOUw3Qxtz3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eRbEIelOUw3Qxtz3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eRbEIelOUw3Qxtz3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eRbEIelOUw3Qxtz3 .marker.cross{stroke:#333333;}#mermaid-svg-eRbEIelOUw3Qxtz3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eRbEIelOUw3Qxtz3 p{margin:0;}#mermaid-svg-eRbEIelOUw3Qxtz3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eRbEIelOUw3Qxtz3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eRbEIelOUw3Qxtz3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-eRbEIelOUw3Qxtz3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-eRbEIelOUw3Qxtz3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-eRbEIelOUw3Qxtz3 .sequenceNumber{fill:white;}#mermaid-svg-eRbEIelOUw3Qxtz3 #sequencenumber{fill:#333;}#mermaid-svg-eRbEIelOUw3Qxtz3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-eRbEIelOUw3Qxtz3 .messageText{fill:#333;stroke:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eRbEIelOUw3Qxtz3 .labelText,#mermaid-svg-eRbEIelOUw3Qxtz3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .loopText,#mermaid-svg-eRbEIelOUw3Qxtz3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eRbEIelOUw3Qxtz3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-eRbEIelOUw3Qxtz3 .noteText,#mermaid-svg-eRbEIelOUw3Qxtz3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-eRbEIelOUw3Qxtz3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eRbEIelOUw3Qxtz3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eRbEIelOUw3Qxtz3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eRbEIelOUw3Qxtz3 .actorPopupMenu{position:absolute;}#mermaid-svg-eRbEIelOUw3Qxtz3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-eRbEIelOUw3Qxtz3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eRbEIelOUw3Qxtz3 .actor-man circle,#mermaid-svg-eRbEIelOUw3Qxtz3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-eRbEIelOUw3Qxtz3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt cache hit cache miss generate_response(messages, response_model) 生成/注入 JSON Schema 清理字符 + instruction start span 查 cache structured result provider request response parse / validate / retry if needed save cache usage + span metadata dict / structured response

18.4 Retry

Graphiti 的 LLM base client 有重试逻辑,用于:

  • rate limit;
  • 空结果;
  • JSON decode;
  • 部分 5xx。

这是生产 Agent 很重要的一层。


18.5 small_model

配置中区分:

text 复制代码
model
small_model

含义:

不同复杂度任务可以使用不同成本模型。

例如时间解析、简单抽取等任务不一定要始终使用最强模型。


第19章 Prompt 系统:Graphiti 的"认知逻辑"放在哪里

核心:

text 复制代码
graphiti_core/prompts/

很多人读 Agent 项目只看 Python 代码,会漏掉真正的业务逻辑。

Graphiti 中 Prompt 是系统行为的重要组成部分。

19.1 PromptLibrary

大体提供:

text 复制代码
PromptLibrary
├─ extract_nodes
├─ dedupe_nodes
├─ extract_edges
├─ extract_nodes_and_edges
├─ dedupe_edges
├─ summarize_nodes
├─ summarize_sagas
└─ eval

19.2 Prompt 与算法模块对应关系

#mermaid-svg-8iCG2DvLxmQtH4z2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8iCG2DvLxmQtH4z2 .error-icon{fill:#552222;}#mermaid-svg-8iCG2DvLxmQtH4z2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8iCG2DvLxmQtH4z2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .marker.cross{stroke:#333333;}#mermaid-svg-8iCG2DvLxmQtH4z2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8iCG2DvLxmQtH4z2 p{margin:0;}#mermaid-svg-8iCG2DvLxmQtH4z2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster-label text{fill:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster-label span{color:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster-label span p{background-color:transparent;}#mermaid-svg-8iCG2DvLxmQtH4z2 .label text,#mermaid-svg-8iCG2DvLxmQtH4z2 span{fill:#333;color:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .node rect,#mermaid-svg-8iCG2DvLxmQtH4z2 .node circle,#mermaid-svg-8iCG2DvLxmQtH4z2 .node ellipse,#mermaid-svg-8iCG2DvLxmQtH4z2 .node polygon,#mermaid-svg-8iCG2DvLxmQtH4z2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .rough-node .label text,#mermaid-svg-8iCG2DvLxmQtH4z2 .node .label text,#mermaid-svg-8iCG2DvLxmQtH4z2 .image-shape .label,#mermaid-svg-8iCG2DvLxmQtH4z2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8iCG2DvLxmQtH4z2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .rough-node .label,#mermaid-svg-8iCG2DvLxmQtH4z2 .node .label,#mermaid-svg-8iCG2DvLxmQtH4z2 .image-shape .label,#mermaid-svg-8iCG2DvLxmQtH4z2 .icon-shape .label{text-align:center;}#mermaid-svg-8iCG2DvLxmQtH4z2 .node.clickable{cursor:pointer;}#mermaid-svg-8iCG2DvLxmQtH4z2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .arrowheadPath{fill:#333333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8iCG2DvLxmQtH4z2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8iCG2DvLxmQtH4z2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8iCG2DvLxmQtH4z2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster text{fill:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 .cluster span{color:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8iCG2DvLxmQtH4z2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8iCG2DvLxmQtH4z2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8iCG2DvLxmQtH4z2 .icon-shape,#mermaid-svg-8iCG2DvLxmQtH4z2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8iCG2DvLxmQtH4z2 .icon-shape p,#mermaid-svg-8iCG2DvLxmQtH4z2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8iCG2DvLxmQtH4z2 .icon-shape .label rect,#mermaid-svg-8iCG2DvLxmQtH4z2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8iCG2DvLxmQtH4z2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8iCG2DvLxmQtH4z2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8iCG2DvLxmQtH4z2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} node_operations.extract_nodes
prompts.extract_nodes
edge_operations.extract_edges
prompts.extract_edges
resolve nodes
prompts.dedupe_nodes
resolve edges
prompts.dedupe_edges
summary
prompts.summarize_nodes / sagas

19.3 为什么 Prompt 使用 Pydantic Model

LLM 不应该随便返回一段自由文本。

Graphiti 倾向让输出满足结构:

text 复制代码
entities: [...]
edges: [...]
duplicates: [...]
valid_at: ...
invalid_at: ...

因此:

text 复制代码
Prompt
  +
response_model JSON schema
  ↓
LLM
  ↓
结构化数据
  ↓
Python 业务逻辑

这比用正则从自然语言答案里提取稳定很多。


第20章 Embedder 与 Cross-Encoder

20.1 Embedder

目录:

text 复制代码
graphiti_core/embedder/

抽象:

text 复制代码
EmbedderClient
├─ create(text)
└─ create_batch(texts)

Provider:

  • OpenAI
  • Azure OpenAI
  • Gemini
  • Voyage

默认 embedding dimension 可通过环境配置,源码存在 EMBEDDING_DIM 相关设置。

20.2 Embedding 用在哪里

#mermaid-svg-BmjzSDGimUJMlp53{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BmjzSDGimUJMlp53 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BmjzSDGimUJMlp53 .error-icon{fill:#552222;}#mermaid-svg-BmjzSDGimUJMlp53 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BmjzSDGimUJMlp53 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BmjzSDGimUJMlp53 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BmjzSDGimUJMlp53 .marker.cross{stroke:#333333;}#mermaid-svg-BmjzSDGimUJMlp53 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BmjzSDGimUJMlp53 p{margin:0;}#mermaid-svg-BmjzSDGimUJMlp53 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BmjzSDGimUJMlp53 .cluster-label text{fill:#333;}#mermaid-svg-BmjzSDGimUJMlp53 .cluster-label span{color:#333;}#mermaid-svg-BmjzSDGimUJMlp53 .cluster-label span p{background-color:transparent;}#mermaid-svg-BmjzSDGimUJMlp53 .label text,#mermaid-svg-BmjzSDGimUJMlp53 span{fill:#333;color:#333;}#mermaid-svg-BmjzSDGimUJMlp53 .node rect,#mermaid-svg-BmjzSDGimUJMlp53 .node circle,#mermaid-svg-BmjzSDGimUJMlp53 .node ellipse,#mermaid-svg-BmjzSDGimUJMlp53 .node polygon,#mermaid-svg-BmjzSDGimUJMlp53 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BmjzSDGimUJMlp53 .rough-node .label text,#mermaid-svg-BmjzSDGimUJMlp53 .node .label text,#mermaid-svg-BmjzSDGimUJMlp53 .image-shape .label,#mermaid-svg-BmjzSDGimUJMlp53 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BmjzSDGimUJMlp53 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BmjzSDGimUJMlp53 .rough-node .label,#mermaid-svg-BmjzSDGimUJMlp53 .node .label,#mermaid-svg-BmjzSDGimUJMlp53 .image-shape .label,#mermaid-svg-BmjzSDGimUJMlp53 .icon-shape .label{text-align:center;}#mermaid-svg-BmjzSDGimUJMlp53 .node.clickable{cursor:pointer;}#mermaid-svg-BmjzSDGimUJMlp53 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BmjzSDGimUJMlp53 .arrowheadPath{fill:#333333;}#mermaid-svg-BmjzSDGimUJMlp53 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BmjzSDGimUJMlp53 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BmjzSDGimUJMlp53 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BmjzSDGimUJMlp53 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BmjzSDGimUJMlp53 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BmjzSDGimUJMlp53 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BmjzSDGimUJMlp53 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BmjzSDGimUJMlp53 .cluster text{fill:#333;}#mermaid-svg-BmjzSDGimUJMlp53 .cluster span{color:#333;}#mermaid-svg-BmjzSDGimUJMlp53 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BmjzSDGimUJMlp53 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BmjzSDGimUJMlp53 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BmjzSDGimUJMlp53 .icon-shape,#mermaid-svg-BmjzSDGimUJMlp53 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BmjzSDGimUJMlp53 .icon-shape p,#mermaid-svg-BmjzSDGimUJMlp53 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BmjzSDGimUJMlp53 .icon-shape .label rect,#mermaid-svg-BmjzSDGimUJMlp53 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BmjzSDGimUJMlp53 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BmjzSDGimUJMlp53 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BmjzSDGimUJMlp53 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Embedder
Entity.name
EntityEdge.fact
Community.name / summary related representation
Search Query

主要用途:

  • Entity semantic dedupe;
  • Edge semantic dedupe;
  • vector search;
  • MMR;
  • Community similarity。

20.3 CrossEncoder

目录:

text 复制代码
graphiti_core/cross_encoder/

抽象:

text 复制代码
rank(query, passages)
    ↓
[(passage, score), ...]

实现包括:

  • BGE reranker;
  • OpenAI reranker;
  • Gemini reranker。

它主要处于:

text 复制代码
Recall
  ↓
Candidate set
  ↓
CrossEncoder.rank
  ↓
high precision Top-K

第21章 Community:图社区与摘要

Community 不是 Episode,也不是普通实体。

它是:

一组紧密相关 Entity 的高层抽象。

21.1 Community 构建概念

#mermaid-svg-3iT9ONHQkBuLdYav{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3iT9ONHQkBuLdYav .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3iT9ONHQkBuLdYav .error-icon{fill:#552222;}#mermaid-svg-3iT9ONHQkBuLdYav .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3iT9ONHQkBuLdYav .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3iT9ONHQkBuLdYav .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3iT9ONHQkBuLdYav .marker.cross{stroke:#333333;}#mermaid-svg-3iT9ONHQkBuLdYav svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3iT9ONHQkBuLdYav p{margin:0;}#mermaid-svg-3iT9ONHQkBuLdYav .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3iT9ONHQkBuLdYav .cluster-label text{fill:#333;}#mermaid-svg-3iT9ONHQkBuLdYav .cluster-label span{color:#333;}#mermaid-svg-3iT9ONHQkBuLdYav .cluster-label span p{background-color:transparent;}#mermaid-svg-3iT9ONHQkBuLdYav .label text,#mermaid-svg-3iT9ONHQkBuLdYav span{fill:#333;color:#333;}#mermaid-svg-3iT9ONHQkBuLdYav .node rect,#mermaid-svg-3iT9ONHQkBuLdYav .node circle,#mermaid-svg-3iT9ONHQkBuLdYav .node ellipse,#mermaid-svg-3iT9ONHQkBuLdYav .node polygon,#mermaid-svg-3iT9ONHQkBuLdYav .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3iT9ONHQkBuLdYav .rough-node .label text,#mermaid-svg-3iT9ONHQkBuLdYav .node .label text,#mermaid-svg-3iT9ONHQkBuLdYav .image-shape .label,#mermaid-svg-3iT9ONHQkBuLdYav .icon-shape .label{text-anchor:middle;}#mermaid-svg-3iT9ONHQkBuLdYav .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3iT9ONHQkBuLdYav .rough-node .label,#mermaid-svg-3iT9ONHQkBuLdYav .node .label,#mermaid-svg-3iT9ONHQkBuLdYav .image-shape .label,#mermaid-svg-3iT9ONHQkBuLdYav .icon-shape .label{text-align:center;}#mermaid-svg-3iT9ONHQkBuLdYav .node.clickable{cursor:pointer;}#mermaid-svg-3iT9ONHQkBuLdYav .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3iT9ONHQkBuLdYav .arrowheadPath{fill:#333333;}#mermaid-svg-3iT9ONHQkBuLdYav .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3iT9ONHQkBuLdYav .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3iT9ONHQkBuLdYav .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3iT9ONHQkBuLdYav .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3iT9ONHQkBuLdYav .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3iT9ONHQkBuLdYav .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3iT9ONHQkBuLdYav .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3iT9ONHQkBuLdYav .cluster text{fill:#333;}#mermaid-svg-3iT9ONHQkBuLdYav .cluster span{color:#333;}#mermaid-svg-3iT9ONHQkBuLdYav div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3iT9ONHQkBuLdYav .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3iT9ONHQkBuLdYav rect.text{fill:none;stroke-width:0;}#mermaid-svg-3iT9ONHQkBuLdYav .icon-shape,#mermaid-svg-3iT9ONHQkBuLdYav .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3iT9ONHQkBuLdYav .icon-shape p,#mermaid-svg-3iT9ONHQkBuLdYav .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3iT9ONHQkBuLdYav .icon-shape .label rect,#mermaid-svg-3iT9ONHQkBuLdYav .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3iT9ONHQkBuLdYav .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3iT9ONHQkBuLdYav .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3iT9ONHQkBuLdYav :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Entity Graph
Community Detection
member nodes
LLM Summary
CommunityNode
CommunityEdge HAS_MEMBER
Embedding
Database

21.2 Graphiti.build_communities()

大意:

  1. 按 scope 清理/处理已有 community;
  2. 调维护函数构建 communities;
  3. 生成 Community embedding;
  4. 保存 community nodes/edges。

21.3 为什么 SearchResults 单独有 communities

因为 Query 有时要的是:

"这个主题整体是怎么回事?"

而不是某一条具体 Fact。


第22章 维护、去重、属性抽取工具链

目录:

text 复制代码
graphiti_core/utils/maintenance/

这是除 graphiti.py 外最值得逐文件阅读的区域。

22.1 文件职责图

#mermaid-svg-glj7EFbSF56c13zS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-glj7EFbSF56c13zS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-glj7EFbSF56c13zS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-glj7EFbSF56c13zS .error-icon{fill:#552222;}#mermaid-svg-glj7EFbSF56c13zS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-glj7EFbSF56c13zS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-glj7EFbSF56c13zS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-glj7EFbSF56c13zS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-glj7EFbSF56c13zS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-glj7EFbSF56c13zS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-glj7EFbSF56c13zS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-glj7EFbSF56c13zS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-glj7EFbSF56c13zS .marker.cross{stroke:#333333;}#mermaid-svg-glj7EFbSF56c13zS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-glj7EFbSF56c13zS p{margin:0;}#mermaid-svg-glj7EFbSF56c13zS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-glj7EFbSF56c13zS .cluster-label text{fill:#333;}#mermaid-svg-glj7EFbSF56c13zS .cluster-label span{color:#333;}#mermaid-svg-glj7EFbSF56c13zS .cluster-label span p{background-color:transparent;}#mermaid-svg-glj7EFbSF56c13zS .label text,#mermaid-svg-glj7EFbSF56c13zS span{fill:#333;color:#333;}#mermaid-svg-glj7EFbSF56c13zS .node rect,#mermaid-svg-glj7EFbSF56c13zS .node circle,#mermaid-svg-glj7EFbSF56c13zS .node ellipse,#mermaid-svg-glj7EFbSF56c13zS .node polygon,#mermaid-svg-glj7EFbSF56c13zS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-glj7EFbSF56c13zS .rough-node .label text,#mermaid-svg-glj7EFbSF56c13zS .node .label text,#mermaid-svg-glj7EFbSF56c13zS .image-shape .label,#mermaid-svg-glj7EFbSF56c13zS .icon-shape .label{text-anchor:middle;}#mermaid-svg-glj7EFbSF56c13zS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-glj7EFbSF56c13zS .rough-node .label,#mermaid-svg-glj7EFbSF56c13zS .node .label,#mermaid-svg-glj7EFbSF56c13zS .image-shape .label,#mermaid-svg-glj7EFbSF56c13zS .icon-shape .label{text-align:center;}#mermaid-svg-glj7EFbSF56c13zS .node.clickable{cursor:pointer;}#mermaid-svg-glj7EFbSF56c13zS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-glj7EFbSF56c13zS .arrowheadPath{fill:#333333;}#mermaid-svg-glj7EFbSF56c13zS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-glj7EFbSF56c13zS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-glj7EFbSF56c13zS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-glj7EFbSF56c13zS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-glj7EFbSF56c13zS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-glj7EFbSF56c13zS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-glj7EFbSF56c13zS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-glj7EFbSF56c13zS .cluster text{fill:#333;}#mermaid-svg-glj7EFbSF56c13zS .cluster span{color:#333;}#mermaid-svg-glj7EFbSF56c13zS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-glj7EFbSF56c13zS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-glj7EFbSF56c13zS rect.text{fill:none;stroke-width:0;}#mermaid-svg-glj7EFbSF56c13zS .icon-shape,#mermaid-svg-glj7EFbSF56c13zS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-glj7EFbSF56c13zS .icon-shape p,#mermaid-svg-glj7EFbSF56c13zS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-glj7EFbSF56c13zS .icon-shape .label rect,#mermaid-svg-glj7EFbSF56c13zS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-glj7EFbSF56c13zS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-glj7EFbSF56c13zS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-glj7EFbSF56c13zS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} graphiti.py
node_operations.py
edge_operations.py
community_operations.py
combined_extraction.py
dedup_helpers.py
attribute_utils.py
graph_data_operations.py

22.2 node_operations.py

职责:

  • extract_nodes
  • semantic candidate search
  • entity resolution
  • LLM dedupe fallback
  • attributes extraction
  • entity summaries

它负责:

"一句话里有哪些东西?"以及"这个东西是不是图里已经有了?"


22.3 edge_operations.py

职责:

  • extract_edges
  • build_episodic_edges
  • build_community_edges
  • resolve_extracted_edges
  • resolve_extracted_edge
  • 时间抽取
  • 矛盾失效

它负责:

"这些东西之间发生了什么?这个事实是不是新的?是否推翻了旧事实?"


22.4 combined_extraction.py

将:

text 复制代码
Node extraction
+
Edge extraction

在适合的 bulk 场景中合并。

目的:

  • 降低 LLM round trips;
  • 保持一次抽取的一致性。

22.5 dedup_helpers.py

集中放置去重辅助算法:

  • 候选组织;
  • 相似性处理;
  • duplicate indexing;
  • resolution 过程中的共享逻辑。

22.6 attribute_utils.py

处理自定义 entity/edge schema 的属性:

从 LLM structured output 中,把 ontology 定义的字段安全地装配到节点/边。


22.7 graph_data_operations.py

处理更偏图数据维护的操作,例如:

  • clear;
  • episode retrieval;
  • 查询前序 Episode;
  • 与图数据操作相关的工具。

它把"业务算法"和"数据取回"分离开。


第23章 FastAPI Server

目录:

text 复制代码
server/
└─ graph_service/
   ├─ dto/
   ├─ routers/
   ├─ config.py
   ├─ main.py
   └─ zep_graphiti.py

23.1 分层

#mermaid-svg-nGxZ1kuVLmzWat4k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nGxZ1kuVLmzWat4k .error-icon{fill:#552222;}#mermaid-svg-nGxZ1kuVLmzWat4k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nGxZ1kuVLmzWat4k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nGxZ1kuVLmzWat4k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nGxZ1kuVLmzWat4k .marker.cross{stroke:#333333;}#mermaid-svg-nGxZ1kuVLmzWat4k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nGxZ1kuVLmzWat4k p{margin:0;}#mermaid-svg-nGxZ1kuVLmzWat4k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster-label text{fill:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster-label span{color:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster-label span p{background-color:transparent;}#mermaid-svg-nGxZ1kuVLmzWat4k .label text,#mermaid-svg-nGxZ1kuVLmzWat4k span{fill:#333;color:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k .node rect,#mermaid-svg-nGxZ1kuVLmzWat4k .node circle,#mermaid-svg-nGxZ1kuVLmzWat4k .node ellipse,#mermaid-svg-nGxZ1kuVLmzWat4k .node polygon,#mermaid-svg-nGxZ1kuVLmzWat4k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nGxZ1kuVLmzWat4k .rough-node .label text,#mermaid-svg-nGxZ1kuVLmzWat4k .node .label text,#mermaid-svg-nGxZ1kuVLmzWat4k .image-shape .label,#mermaid-svg-nGxZ1kuVLmzWat4k .icon-shape .label{text-anchor:middle;}#mermaid-svg-nGxZ1kuVLmzWat4k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nGxZ1kuVLmzWat4k .rough-node .label,#mermaid-svg-nGxZ1kuVLmzWat4k .node .label,#mermaid-svg-nGxZ1kuVLmzWat4k .image-shape .label,#mermaid-svg-nGxZ1kuVLmzWat4k .icon-shape .label{text-align:center;}#mermaid-svg-nGxZ1kuVLmzWat4k .node.clickable{cursor:pointer;}#mermaid-svg-nGxZ1kuVLmzWat4k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nGxZ1kuVLmzWat4k .arrowheadPath{fill:#333333;}#mermaid-svg-nGxZ1kuVLmzWat4k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nGxZ1kuVLmzWat4k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nGxZ1kuVLmzWat4k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nGxZ1kuVLmzWat4k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nGxZ1kuVLmzWat4k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nGxZ1kuVLmzWat4k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster text{fill:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k .cluster span{color:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nGxZ1kuVLmzWat4k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nGxZ1kuVLmzWat4k rect.text{fill:none;stroke-width:0;}#mermaid-svg-nGxZ1kuVLmzWat4k .icon-shape,#mermaid-svg-nGxZ1kuVLmzWat4k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nGxZ1kuVLmzWat4k .icon-shape p,#mermaid-svg-nGxZ1kuVLmzWat4k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nGxZ1kuVLmzWat4k .icon-shape .label rect,#mermaid-svg-nGxZ1kuVLmzWat4k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nGxZ1kuVLmzWat4k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nGxZ1kuVLmzWat4k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nGxZ1kuVLmzWat4k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP Client
graph_service.main

FastAPI App
routers
dto
zep_graphiti.py
Graphiti Core
Graph DB

23.2 dto/

DTO:

Data Transfer Object,数据传输对象

主要分:

  • common.py
  • ingest.py
  • retrieve.py

用于定义 HTTP 请求/响应模型,而不是领域核心 Model。


23.3 routers/

  • ingest.py:写入相关 API;
  • retrieve.py:检索相关 API。

Router 应该被看作:

传输层适配器。

不要把核心知识图逻辑写在 Router 中,真正逻辑仍在 Graphiti Core。


23.4 config.py

服务配置:

  • DB;
  • 模型;
  • 环境变量;
  • 服务运行参数等。

23.5 zep_graphiti.py

作用可以理解为:

Server 与 Graphiti Core 的装配/适配层。

即负责构造 Graphiti 实例、绑定服务需要的依赖。


第24章 MCP Server

目录:

text 复制代码
mcp_server/
├─ src/
│  ├─ config/
│  ├─ models/
│  ├─ services/
│  ├─ utils/
│  └─ graphiti_mcp_server.py
├─ config/
├─ docker/
├─ docs/
├─ tests/
├─ main.py
└─ pyproject.toml

24.1 MCP 是什么

MCP:

Model Context Protocol

可以理解为一个让 AI Client / Agent 统一发现和调用外部工具、资源、上下文的协议层。

Graphiti MCP Server 的作用就是:
#mermaid-svg-5XQJFxB1fwprX7BE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5XQJFxB1fwprX7BE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5XQJFxB1fwprX7BE .error-icon{fill:#552222;}#mermaid-svg-5XQJFxB1fwprX7BE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5XQJFxB1fwprX7BE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5XQJFxB1fwprX7BE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5XQJFxB1fwprX7BE .marker.cross{stroke:#333333;}#mermaid-svg-5XQJFxB1fwprX7BE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5XQJFxB1fwprX7BE p{margin:0;}#mermaid-svg-5XQJFxB1fwprX7BE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5XQJFxB1fwprX7BE .cluster-label text{fill:#333;}#mermaid-svg-5XQJFxB1fwprX7BE .cluster-label span{color:#333;}#mermaid-svg-5XQJFxB1fwprX7BE .cluster-label span p{background-color:transparent;}#mermaid-svg-5XQJFxB1fwprX7BE .label text,#mermaid-svg-5XQJFxB1fwprX7BE span{fill:#333;color:#333;}#mermaid-svg-5XQJFxB1fwprX7BE .node rect,#mermaid-svg-5XQJFxB1fwprX7BE .node circle,#mermaid-svg-5XQJFxB1fwprX7BE .node ellipse,#mermaid-svg-5XQJFxB1fwprX7BE .node polygon,#mermaid-svg-5XQJFxB1fwprX7BE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5XQJFxB1fwprX7BE .rough-node .label text,#mermaid-svg-5XQJFxB1fwprX7BE .node .label text,#mermaid-svg-5XQJFxB1fwprX7BE .image-shape .label,#mermaid-svg-5XQJFxB1fwprX7BE .icon-shape .label{text-anchor:middle;}#mermaid-svg-5XQJFxB1fwprX7BE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5XQJFxB1fwprX7BE .rough-node .label,#mermaid-svg-5XQJFxB1fwprX7BE .node .label,#mermaid-svg-5XQJFxB1fwprX7BE .image-shape .label,#mermaid-svg-5XQJFxB1fwprX7BE .icon-shape .label{text-align:center;}#mermaid-svg-5XQJFxB1fwprX7BE .node.clickable{cursor:pointer;}#mermaid-svg-5XQJFxB1fwprX7BE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5XQJFxB1fwprX7BE .arrowheadPath{fill:#333333;}#mermaid-svg-5XQJFxB1fwprX7BE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5XQJFxB1fwprX7BE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5XQJFxB1fwprX7BE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5XQJFxB1fwprX7BE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5XQJFxB1fwprX7BE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5XQJFxB1fwprX7BE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5XQJFxB1fwprX7BE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5XQJFxB1fwprX7BE .cluster text{fill:#333;}#mermaid-svg-5XQJFxB1fwprX7BE .cluster span{color:#333;}#mermaid-svg-5XQJFxB1fwprX7BE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5XQJFxB1fwprX7BE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5XQJFxB1fwprX7BE rect.text{fill:none;stroke-width:0;}#mermaid-svg-5XQJFxB1fwprX7BE .icon-shape,#mermaid-svg-5XQJFxB1fwprX7BE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5XQJFxB1fwprX7BE .icon-shape p,#mermaid-svg-5XQJFxB1fwprX7BE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5XQJFxB1fwprX7BE .icon-shape .label rect,#mermaid-svg-5XQJFxB1fwprX7BE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5XQJFxB1fwprX7BE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5XQJFxB1fwprX7BE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5XQJFxB1fwprX7BE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Claude / IDE / Agent
MCP Protocol
Graphiti MCP Server
Graphiti
Context Graph

24.2 graphiti_mcp_server.py

这是 MCP 服务主实现。

它负责把 Graphiti 能力包装成 MCP 可调用接口。

24.3 services/factories.py

典型职责:

根据配置创建 Driver、LLM、Embedder、Graphiti 等依赖。

属于 Factory / Dependency Assembly。

24.4 services/queue_service.py

用于组织写入队列/任务处理。

原因:

Graphiti Episode ingestion 是多阶段异步过程:

text 复制代码
LLM
Embedding
DB read
DB write

直接无控制地并发大量写入会造成:

  • 顺序问题;
  • rate limit;
  • DB 压力;
  • 事实时间解析上下文异常。

Queue Service 用来让 MCP 接入侧更好地控制处理。

24.5 models/

包括:

  • edge_types.py
  • entity_types.py
  • response_types.py

这些更偏 MCP API schema,而不是 graphiti_core 领域 Model。

24.6 utils/

  • formatting.py
  • type_config.py
  • utils.py

负责 MCP 层格式转换、类型配置、共用工具。


第25章 Tracing、Telemetry、并发与容错

25.1 Tracer 与 Telemetry 不要混淆

graphiti_core/tracer.py

这是 OpenTelemetry 风格的运行链路追踪抽象

text 复制代码
Graphiti operation
  ↓
span
  ├─ add_episode
  ├─ extract
  ├─ search
  └─ llm.generate

默认可以是 NoOp,也可以接 OpenTelemetry。

graphiti_core/telemetry/

这是项目自己的匿名产品/使用 telemetry 逻辑,包含 PostHog 相关能力。

所以:

text 复制代码
Tracing = 运行链路观测
Telemetry = 使用/产品统计

25.2 并发

Graphiti 大量使用:

text 复制代码
async / await
semaphore
gather

尤其:

  • 多节点 embedding;
  • 多实体 candidate search;
  • 多搜索通道;
  • nodes / edges / episodes / communities 并发检索。

25.3 为什么需要 Semaphore

如果 Episode 一次抽出 30 个实体:

text 复制代码
30 embeddings
30 searches
若干 LLM calls

完全裸 asyncio.gather() 可能瞬间触发:

  • API rate limit;
  • DB connection pool 爆满;
  • 资源峰值。

所以项目中存在并发限制工具。


25.4 handle_multiple_group_ids

该 decorator 尤其用于处理 FalkorDB 多 graph 情况。

概念:
#mermaid-svg-yIJzF4KRhtIxxtp8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yIJzF4KRhtIxxtp8 .error-icon{fill:#552222;}#mermaid-svg-yIJzF4KRhtIxxtp8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yIJzF4KRhtIxxtp8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .marker.cross{stroke:#333333;}#mermaid-svg-yIJzF4KRhtIxxtp8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yIJzF4KRhtIxxtp8 p{margin:0;}#mermaid-svg-yIJzF4KRhtIxxtp8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster-label text{fill:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster-label span{color:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster-label span p{background-color:transparent;}#mermaid-svg-yIJzF4KRhtIxxtp8 .label text,#mermaid-svg-yIJzF4KRhtIxxtp8 span{fill:#333;color:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .node rect,#mermaid-svg-yIJzF4KRhtIxxtp8 .node circle,#mermaid-svg-yIJzF4KRhtIxxtp8 .node ellipse,#mermaid-svg-yIJzF4KRhtIxxtp8 .node polygon,#mermaid-svg-yIJzF4KRhtIxxtp8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .rough-node .label text,#mermaid-svg-yIJzF4KRhtIxxtp8 .node .label text,#mermaid-svg-yIJzF4KRhtIxxtp8 .image-shape .label,#mermaid-svg-yIJzF4KRhtIxxtp8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-yIJzF4KRhtIxxtp8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .rough-node .label,#mermaid-svg-yIJzF4KRhtIxxtp8 .node .label,#mermaid-svg-yIJzF4KRhtIxxtp8 .image-shape .label,#mermaid-svg-yIJzF4KRhtIxxtp8 .icon-shape .label{text-align:center;}#mermaid-svg-yIJzF4KRhtIxxtp8 .node.clickable{cursor:pointer;}#mermaid-svg-yIJzF4KRhtIxxtp8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .arrowheadPath{fill:#333333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yIJzF4KRhtIxxtp8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yIJzF4KRhtIxxtp8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yIJzF4KRhtIxxtp8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster text{fill:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 .cluster span{color:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yIJzF4KRhtIxxtp8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yIJzF4KRhtIxxtp8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-yIJzF4KRhtIxxtp8 .icon-shape,#mermaid-svg-yIJzF4KRhtIxxtp8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yIJzF4KRhtIxxtp8 .icon-shape p,#mermaid-svg-yIJzF4KRhtIxxtp8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yIJzF4KRhtIxxtp8 .icon-shape .label rect,#mermaid-svg-yIJzF4KRhtIxxtp8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yIJzF4KRhtIxxtp8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yIJzF4KRhtIxxtp8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yIJzF4KRhtIxxtp8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} search(group_ids=A,B,C)
@handle_multiple_group_ids
clone driver -> graph A
clone driver -> graph B
clone driver -> graph C
query A
query B
query C
merge results

关键点:

不在共享 Driver 上粗暴修改当前 database,而是 clone,以避免并发读的状态污染。


第26章 graphiti_core 每个模块源码地图

本章按"文件 → 职责 → 关键调用关系"建立可查表。


26.1 根目录文件

graphiti_core/__init__.py

职责:

  • 包入口;
  • 对外导出常用符号。

调用关系:

text 复制代码
Application import
    ↓
graphiti_core
    ↓
Graphiti / types / public APIs

graphiti_core/graphiti.py

项目总编排器。

关键内容:

  • Graphiti
  • add_episode
  • add_episode_bulk
  • add_triplet
  • retrieve_episodes
  • build_communities
  • search
  • search_
  • get_nodes_and_edges_by_episode
  • episode/saga summary 与维护相关辅助方法
  • _extract_and_resolve_nodes
  • _extract_and_resolve_edges
  • _process_episode_data

上游:

text 复制代码
Python Application
FastAPI
MCP Server

下游:

text 复制代码
maintenance/*
search/*
driver/*
llm_client/*
embedder/*
cross_encoder/*
nodes.py / edges.py

graphiti_core/nodes.py

定义核心节点 Model:

  • Node
  • EpisodicNode
  • EntityNode
  • CommunityNode
  • SagaNode
  • EpisodeType

还保留部分数据库访问兼容接口。

上游:

text 复制代码
graphiti.py
maintenance/*
namespaces/*
search/*

下游:

text 复制代码
Embedder
Driver / DB query layer

graphiti_core/edges.py

定义核心 Edge Model:

  • Edge
  • EntityEdge
  • EpisodicEdge
  • CommunityEdge
  • HasEpisodeEdge
  • NextEpisodeEdge

重要方法:

  • EntityEdge embedding;
  • save/get/delete 等兼容数据访问方法。

graphiti_core/graphiti_types.py

集中定义 Graphiti 层公共类型。

其中重要的是:

text 复制代码
GraphitiClients

用于捆绑:

  • Driver;
  • LLM;
  • Embedder;
  • CrossEncoder;
  • Tracer。

graphiti_core/graph_queries.py

集中放图数据库通用查询/索引/约束相关 Query 定义或辅助。

它属于:

数据库 Query 基础设施层,而不是 Agent 业务逻辑层。

读 Driver 时会频繁遇到。


graphiti_core/decorators.py

重要 decorator:

text 复制代码
handle_multiple_group_ids

处理某些 Driver 的 group/database 分发与结果合并。


graphiti_core/helpers.py

通用高频 helper:

  • 并发控制;
  • semaphore gather;
  • 输入/时间辅助;
  • resolution 工具;
  • 其他跨模块公用函数。

它属于"高扇出依赖"文件:

很多核心模块都会 import 它。


graphiti_core/errors.py

定义 Graphiti 核心异常类型。

目的:

  • 不让上层直接依赖 Provider 原始异常;
  • 对业务错误提供统一语义。

graphiti_core/tracer.py

定义:

  • Tracer ABC
  • Span ABC
  • NoOp 实现
  • OpenTelemetry 适配实现

被:

text 复制代码
Graphiti
LLMClient
Search

调用。


graphiti_core/py.typed

PEP 561 标记文件。

含义:

告诉 Python 类型检查器:这个包发布时提供类型信息。

它不是运行时代码。


26.2 cross_encoder/

text 复制代码
cross_encoder/
├─ __init__.py
├─ client.py
├─ bge_reranker_client.py
├─ gemini_reranker_client.py
└─ openai_reranker_client.py

client.py

定义:

text 复制代码
CrossEncoderClient.rank(query, passages)

抽象契约。

bge_reranker_client.py

BGE Reranker 实现。

用于:

text 复制代码
Query + Candidate
    ↓
BGE reranking model
    ↓
score

gemini_reranker_client.py

Gemini-backed reranking 实现。

openai_reranker_client.py

OpenAI-backed reranking 实现。

__init__.py

对外导出。

调用者主要是:

text 复制代码
search/search.py

26.3 embedder/

text 复制代码
embedder/
├─ __init__.py
├─ client.py
├─ openai.py
├─ azure_openai.py
├─ gemini.py
└─ voyage.py

client.py

定义 Embedder ABC:

text 复制代码
create(text)
create_batch(texts)

以及 embedding dimension 配置。

openai.py

OpenAI Embedding Provider。

azure_openai.py

Azure OpenAI Embedding Provider。

gemini.py

Gemini Embedding Provider。

voyage.py

Voyage Embedding Provider。

调用者:

text 复制代码
EntityNode.generate_name_embedding
EntityEdge.generate_embedding
node_operations
edge_operations
search
community building

26.4 llm_client/

text 复制代码
llm_client/
├─ __init__.py
├─ client.py
├─ config.py
├─ cache.py
├─ errors.py
├─ utils.py
├─ token_tracker.py
├─ openai_base_client.py
├─ openai_client.py
├─ openai_generic_client.py
├─ azure_openai_client.py
├─ anthropic_client.py
├─ gemini_client.py
├─ groq_client.py
└─ gliner2_client.py

client.py

LLM 抽象与公共 orchestration:

  • generate_response
  • structured response schema
  • retry
  • cache
  • tracing
  • token tracking

是整个 LLM 层最值得先读的文件。

config.py

模型配置:

  • model;
  • small model;
  • temperature;
  • token limit;
  • Provider 相关配置。

cache.py

LLM 响应 cache。

作用:

相同 Prompt/输入在调试、评测、重复处理时减少 API 请求。

errors.py

LLM-specific exception。

utils.py

Provider/LLM 共享工具。

token_tracker.py

记录 token usage,帮助:

  • 成本统计;
  • tracing;
  • 性能分析。

openai_base_client.py

OpenAI 协议族的公共底层实现。

openai_client.py

官方 OpenAI Provider 适配。

openai_generic_client.py

兼容 OpenAI API 风格的第三方服务。

azure_openai_client.py

Azure OpenAI 适配。

anthropic_client.py

Anthropic 适配。

gemini_client.py

Google Gemini 适配。

groq_client.py

Groq 适配。

gliner2_client.py

GLiNER2-backed extraction client。

它更偏向信息抽取场景,可作为某些 LLM extraction 任务的替代/专用实现。


26.5 prompts/

text 复制代码
prompts/
├─ __init__.py
├─ lib.py
├─ models.py
├─ prompt_helpers.py
├─ snippets.py
├─ extract_nodes.py
├─ extract_edges.py
├─ extract_nodes_and_edges.py
├─ dedupe_nodes.py
├─ dedupe_edges.py
├─ summarize_nodes.py
├─ summarize_sagas.py
└─ eval.py

lib.py

定义/装配 PromptLibrary

它是 Prompt 总入口。

models.py

定义 Prompt structured output 使用的数据模型。

prompt_helpers.py

Prompt 构造辅助函数。

snippets.py

跨 Prompt 复用的提示片段。

extract_nodes.py

Entity extraction Prompt。

extract_edges.py

Relationship/Fact extraction Prompt。

extract_nodes_and_edges.py

Combined extraction Prompt。

dedupe_nodes.py

Entity deduplication/disambiguation Prompt。

dedupe_edges.py

Fact dedup / edge resolution / contradiction 判断相关 Prompt。

summarize_nodes.py

Entity summary Prompt。

summarize_sagas.py

Saga summary Prompt。

eval.py

评测/判断相关 Prompt。


26.6 search/

text 复制代码
search/
├─ __init__.py
├─ search.py
├─ search_config.py
├─ search_config_recipes.py
├─ search_filters.py
├─ search_helpers.py
└─ search_utils.py

search.py

高级搜索 orchestrator。

重要函数:

text 复制代码
search
edge_search
node_search
episode_search
community_search

调用:

text 复制代码
SearchConfig
    ↓
search_utils primitive retrieval
    ↓
RRF/MMR/CrossEncoder

search_config.py

定义:

  • SearchMethod enum;
  • Reranker enum;
  • Edge/Node/Episode/Community SearchConfig;
  • SearchResults。

search_config_recipes.py

预设搜索方案,例如:

  • combined hybrid RRF;
  • combined hybrid MMR;
  • combined hybrid Cross-Encoder;
  • edge hybrid;
  • node distance recipe 等。

它让应用不必每次手工搭所有 search config。

search_filters.py

定义过滤条件模型。

典型作用:

text 复制代码
按 group
按时间
按 labels/types
按其他 graph filter

具体可用字段应以当前版本的 Pydantic 定义为准。

search_helpers.py

搜索编排中的 helper:

  • 结果标准化;
  • 过滤;
  • score/result 组合;
  • 其他 search.py 共享逻辑。

search_utils.py

真正的底层检索 primitive 集合。

可将它理解为:

text 复制代码
search.py = "导演"
search_utils.py = "具体镜头"

其中包含/支撑:

  • full-text search;
  • vector similarity;
  • BFS;
  • RRF;
  • MMR;
  • node distance;
  • episode mentions;
  • embeddings retrieval;
  • mentioned nodes / communities retrieval。

26.7 models/

text 复制代码
models/
├─ __init__.py
├─ edges/
│  ├─ __init__.py
│  └─ edge_db_queries.py
└─ nodes/
   ├─ __init__.py
   └─ node_db_queries.py

这里的 models 不等于根目录 nodes.py/edges.py

可以这样理解:

text 复制代码
nodes.py / edges.py
    = Domain Data Models

models/nodes/node_db_queries.py
models/edges/edge_db_queries.py
    = 与这些 Model 相关的数据库 Query 辅助

这是历史结构与 Driver 重构并存时很容易误解的地方。


26.8 namespaces/

text 复制代码
namespaces/
├─ __init__.py
├─ nodes.py
└─ edges.py

nodes.py

实现:

text 复制代码
NodeNamespace
EntityNodeNamespace
EpisodeNodeNamespace
CommunityNodeNamespace
SagaNodeNamespace

职责:

  • 对外提供更清晰 API;
  • 在 DB save 前处理 embedding 等横切逻辑;
  • 调用对应 Driver Operations。

例如概念链:

text 复制代码
graphiti.nodes.entity.save(node)
  ↓
generate name embedding
  ↓
driver.entity_node_ops.save(node)

edges.py

同理:

text 复制代码
graphiti.edges.entity.save(edge)
  ↓
generate fact embedding
  ↓
driver.entity_edge_ops.save(edge)

26.9 driver/

text 复制代码
driver/
├─ driver.py
├─ query_executor.py
├─ record_parsers.py
├─ neo4j_driver.py
├─ falkordb_driver.py
├─ neptune_driver.py
├─ kuzu_driver.py
│
├─ operations/
├─ graph_operations/
├─ search_interface/
│
├─ neo4j/
├─ falkordb/
├─ neptune/
└─ kuzu/

driver.py

核心 GraphDriver ABC。

query_executor.py

统一 query execution contract。

record_parsers.py

把数据库返回 record 转换为:

text 复制代码
EntityNode
EpisodicNode
EntityEdge
...

也就是 Data Mapper 角色。

neo4j_driver.py

Neo4j concrete driver:

  • connection;
  • session;
  • execute query;
  • index/constraint;
  • transaction;
  • operations wiring。

falkordb_driver.py

FalkorDB concrete driver。

neptune_driver.py

AWS Neptune concrete driver。

kuzu_driver.py

Kuzu concrete driver。


26.9.1 driver/operations/

文件 负责对象
entity_node_ops.py EntityNode CRUD / query contract
episode_node_ops.py EpisodicNode
community_node_ops.py CommunityNode
saga_node_ops.py SagaNode
entity_edge_ops.py EntityEdge
episodic_edge_ops.py MENTIONS edge
community_edge_ops.py HAS_MEMBER edge
has_episode_edge_ops.py Saga → Episode
next_episode_edge_ops.py Episode → Episode
search_ops.py Search DB operations
graph_ops.py 整图级操作
graph_utils.py Operations 公用 DB helper
__init__.py export

这些文件主要是 ABC / Contract

Provider 目录下再给出 concrete implementation。


26.9.2 driver/graph_operations/

包含:

text 复制代码
graph_operations.py

这是旧式/兼容期的更大粒度 Graph Operations interface。

在 Driver redesign 的迁移阶段仍保留。


26.9.3 driver/search_interface/

包含:

text 复制代码
search_interface.py

也是旧式搜索 DB interface。

新设计逐步把行为迁到:

text 复制代码
driver.search_ops

26.9.4 Provider 子目录

driver/neo4j/

Neo4j 专用 operations/query 实现。

driver/falkordb/

FalkorDB 专用实现,并包含:

text 复制代码
fulltext.py
operations/

driver/neptune/

Neptune 专用 operations。

driver/kuzu/

Kuzu 专用 operations。

这些目录体现:

text 复制代码
Generic Operations ABC
        ↓
Provider-specific implementation

26.10 utils/

text 复制代码
utils/
├─ __init__.py
├─ bulk_utils.py
├─ content_chunking.py
├─ datetime_utils.py
├─ text_utils.py
├─ maintenance/
└─ ontology_utils/

bulk_utils.py

批量写入核心 helper。

典型职责:

text 复制代码
add_nodes_and_edges_bulk
batch embeddings
bulk persistence orchestration

被:

text 复制代码
Graphiti._process_episode_data
Graphiti.add_episode_bulk

使用。

content_chunking.py

处理长内容切分。

为什么 Graphiti 仍需要 chunk:

Episode 可以很长,而 LLM context 和 extraction 都有 token 限制。

datetime_utils.py

统一时间 parsing / timezone / UTC 相关工具。

时间型知识图非常依赖这一层的一致性。

text_utils.py

文本标准化、清洗、比较等共享工具。


26.10.1 utils/maintenance/

已在第 22 章详细讲解:

text 复制代码
attribute_utils.py
combined_extraction.py
community_operations.py
dedup_helpers.py
edge_operations.py
graph_data_operations.py
node_operations.py

它们是知识构建算法核心。


26.10.2 utils/ontology_utils/

当前包括:

text 复制代码
entity_types_utils.py

用途:

  • 处理自定义 Entity schema;
  • 类型验证;
  • ontology 上下文构建;
  • entity type 映射等。

26.11 migrations/

当前目录很轻,包含:

text 复制代码
__init__.py

它是为 schema/data migration 预留的包结构。


26.12 telemetry/

text 复制代码
telemetry/
├─ __init__.py
└─ telemetry.py

职责:

  • 匿名使用 telemetry;
  • PostHog integration;
  • capture feature/events。

不要与 tracer.py 的 OpenTelemetry execution tracing 混淆。


第27章 服务、示例、测试与工程目录逐项说明

27.1 examples/

当前示例目录包括:

text 复制代码
examples/
├─ azure-openai/
├─ data/
├─ ecommerce/
├─ gliner2/
├─ langgraph-agent/
├─ opentelemetry/
├─ podcast/
├─ quickstart/
└─ wizard_of_oz/

quickstart/

最适合第一次跑通:

text 复制代码
connect DB
→ build indices
→ add episode
→ search

azure-openai/

展示替换:

  • LLM Provider;
  • Embedding Provider。

gliner2/

展示 GLiNER2 extraction client。

langgraph-agent/

展示 Graphiti 作为 LangGraph Agent memory/context backend 的集成思路。

opentelemetry/

展示 tracing。

ecommerce/

业务数据/电商场景示例。

podcast/

长文本/内容类 ingestion 场景。

wizard_of_oz/

用于更完整的叙事/图构建演示和测试数据场景。

data/

示例数据。


27.2 tests/

主要包括:

text 复制代码
tests/
├─ cross_encoder/
├─ driver/
├─ embedder/
├─ evals/
├─ llm_client/
├─ utils/
├─ test_add_triplet.py
├─ test_edge_db_queries.py
├─ test_edge_int.py
├─ test_entity_exclusion_int.py
├─ test_graphiti_int.py
├─ test_graphiti_mock.py
├─ test_handle_multiple_group_ids.py
├─ test_node_int.py
├─ test_node_label_security.py
├─ test_text_utils.py
└─ ...

建议读源码时:

代码和测试一起读。

例如:

text 复制代码
add_triplet()
    ↔
test_add_triplet.py

可以快速知道开发者认为这个函数的 contract 是什么。

_int 一般代表

integration test,集成测试。


27.3 spec/

当前最关键:

text 复制代码
driver-operations-redesign.md

它解释了当前源码"为什么同时有两套 DB 访问方式"。

核心目标:

text 复制代码
Graphiti Client
    ↓
Namespace Wrapper
    ↓
Operations ABC
    ↓
GraphDriver

并希望最终让 data models 更接近纯 Pydantic model。

这个文件非常值得在读 driver/ 前先看。


27.4 .github/

包括:

  • workflows;
  • issue templates;
  • PR template;
  • Dependabot;
  • scripts;
  • secret scanning;
  • 自定义 GitHub Action 等。

它不是 runtime 架构,却反映:

  • CI;
  • security;
  • release/quality process。

27.5 signatures/version1/

包含 CLA 相关 JSON。

CLA:

Contributor License Agreement,贡献者许可协议。

属于项目治理,不参与运行。


27.6 images/

README/文档资源:

  • 图示;
  • GIF;
  • SVG;
  • 论文/演示相关截图。

不参与 runtime。


27.7 根目录工程文件

pyproject.toml

核心 Python 包配置:

  • package metadata;
  • Python version;
  • dependencies;
  • optional dependencies;
  • lint/test/tool config。

当前分析基线版本:

text 复制代码
graphiti-core 0.29.3

Dockerfile

容器化构建。

docker-compose.yml

本地联调多个服务,例如:

text 复制代码
Graphiti service
+
graph database

Makefile

常用开发任务封装。


第28章 完整函数调用关系总图

下面把"写 + 搜"放在一张图中。
#mermaid-svg-EPCWzamNraVIhftv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EPCWzamNraVIhftv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EPCWzamNraVIhftv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EPCWzamNraVIhftv .error-icon{fill:#552222;}#mermaid-svg-EPCWzamNraVIhftv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EPCWzamNraVIhftv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EPCWzamNraVIhftv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EPCWzamNraVIhftv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EPCWzamNraVIhftv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EPCWzamNraVIhftv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EPCWzamNraVIhftv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EPCWzamNraVIhftv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EPCWzamNraVIhftv .marker.cross{stroke:#333333;}#mermaid-svg-EPCWzamNraVIhftv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EPCWzamNraVIhftv p{margin:0;}#mermaid-svg-EPCWzamNraVIhftv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EPCWzamNraVIhftv .cluster-label text{fill:#333;}#mermaid-svg-EPCWzamNraVIhftv .cluster-label span{color:#333;}#mermaid-svg-EPCWzamNraVIhftv .cluster-label span p{background-color:transparent;}#mermaid-svg-EPCWzamNraVIhftv .label text,#mermaid-svg-EPCWzamNraVIhftv span{fill:#333;color:#333;}#mermaid-svg-EPCWzamNraVIhftv .node rect,#mermaid-svg-EPCWzamNraVIhftv .node circle,#mermaid-svg-EPCWzamNraVIhftv .node ellipse,#mermaid-svg-EPCWzamNraVIhftv .node polygon,#mermaid-svg-EPCWzamNraVIhftv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EPCWzamNraVIhftv .rough-node .label text,#mermaid-svg-EPCWzamNraVIhftv .node .label text,#mermaid-svg-EPCWzamNraVIhftv .image-shape .label,#mermaid-svg-EPCWzamNraVIhftv .icon-shape .label{text-anchor:middle;}#mermaid-svg-EPCWzamNraVIhftv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EPCWzamNraVIhftv .rough-node .label,#mermaid-svg-EPCWzamNraVIhftv .node .label,#mermaid-svg-EPCWzamNraVIhftv .image-shape .label,#mermaid-svg-EPCWzamNraVIhftv .icon-shape .label{text-align:center;}#mermaid-svg-EPCWzamNraVIhftv .node.clickable{cursor:pointer;}#mermaid-svg-EPCWzamNraVIhftv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EPCWzamNraVIhftv .arrowheadPath{fill:#333333;}#mermaid-svg-EPCWzamNraVIhftv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EPCWzamNraVIhftv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EPCWzamNraVIhftv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EPCWzamNraVIhftv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EPCWzamNraVIhftv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EPCWzamNraVIhftv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EPCWzamNraVIhftv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EPCWzamNraVIhftv .cluster text{fill:#333;}#mermaid-svg-EPCWzamNraVIhftv .cluster span{color:#333;}#mermaid-svg-EPCWzamNraVIhftv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EPCWzamNraVIhftv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EPCWzamNraVIhftv rect.text{fill:none;stroke-width:0;}#mermaid-svg-EPCWzamNraVIhftv .icon-shape,#mermaid-svg-EPCWzamNraVIhftv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EPCWzamNraVIhftv .icon-shape p,#mermaid-svg-EPCWzamNraVIhftv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EPCWzamNraVIhftv .icon-shape .label rect,#mermaid-svg-EPCWzamNraVIhftv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EPCWzamNraVIhftv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EPCWzamNraVIhftv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EPCWzamNraVIhftv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Persistence
Search Path
AI Providers
Write Path
Application
Graphiti.add_episode
retrieve_episodes
extract_nodes
resolve_extracted_nodes
semantic candidate search
LLM node dedupe
_extract_and_resolve_edges
extract_edges
resolve_edge_pointers
resolve_extracted_edges
extract timestamps
edge dedupe
temporal invalidation
extract_attributes_from_nodes
process_episode_data
build_episodic_edges
add_nodes_and_edges_bulk
Saga / HAS_EPISODE / NEXT_EPISODE
community update
LLMClient.generate_response
Embedder.create/create_batch
CrossEncoder.rank
PromptLibrary
Graphiti.search

search.search
edge_search
node_search
episode_search
community_search
Fulltext / BM25
Cosine
BFS
RRF
MMR
CrossEncoder rerank
Namespaces
Operations
GraphDriver
Neo4j / FalkorDB / Neptune / Kuzu


第29章 如何扩展 Graphiti

Graphiti 的扩展点非常清楚。

29.1 自定义 Entity Type

你的业务可能需要:

text 复制代码
Person
Project
Device
Reminder
Preference
Location
Product

可以通过 Pydantic schema 定义。

例如概念上:

python 复制代码
class Preference(BaseModel):
    category: str
    value: str
    confidence: float | None = None

然后交给 Graphiti extraction pipeline。


29.2 自定义 Edge Type

例如"小搭"记忆系统可定义:

text 复制代码
Person --HAS_PREFERENCE--> Preference
Person --OWNS--> Device
Person --PARTICIPATES_IN--> Project
Reminder --ABOUT--> Entity
Person --LOCATED_IN--> Location

并通过 edge_type_map 约束 source/target。


29.3 自定义 LLM Provider

实现:

text 复制代码
LLMClient

主要保证:

  • messages input;
  • structured output;
  • error/retry semantics;
  • response model compatible。

29.4 自定义 Embedder

实现:

text 复制代码
EmbedderClient.create
EmbedderClient.create_batch

即可替换成本地/私有向量模型。


29.5 自定义 Reranker

实现:

text 复制代码
CrossEncoderClient.rank

即可接:

  • 本地 BGE;
  • 专用 reranker;
  • 企业私有模型。

29.6 自定义图数据库

这是最重的扩展。

需要实现:

text 复制代码
GraphDriver
+
Operations contracts
+
Search Operations
+
record mapping
+
indexes/constraints
+
transaction semantics

建议优先沿新 Operations 架构扩展,不要再复制旧 monolithic interface。


第30章 源码阅读顺序与调试建议

如果你从头读,不建议按文件树从 __init__.py 一个个往下。

建议下面路线。

第一阶段:20 分钟建立模型

先读:

text 复制代码
README
graphiti_core/nodes.py
graphiti_core/edges.py

回答三个问题:

  1. 有哪些 Node?
  2. 有哪些 Edge?
  3. 时间字段分别是什么?

第二阶段:抓住主写入链

读:

text 复制代码
graphiti_core/graphiti.py

只追:

text 复制代码
add_episode()

然后跳到:

text 复制代码
utils/maintenance/node_operations.py
utils/maintenance/edge_operations.py
utils/bulk_utils.py

此时先不看 Driver 的 Cypher。


第三阶段:理解为什么"不会越记越乱"

重点:

text 复制代码
resolve_extracted_nodes
resolve_extracted_edges
resolve_extracted_edge

把:

  • Entity duplicate;
  • Fact duplicate;
  • Fact contradiction;
  • Temporal invalidation

四件事分清。


第四阶段:完整理解搜索

读:

text 复制代码
search/search_config.py
search/search_config_recipes.py
search/search.py
search/search_utils.py

建议用一个 Query 打断点,看:

text 复制代码
query embedding
BM25 candidates
cosine candidates
BFS candidates
RRF result
CrossEncoder result

每阶段候选怎么变化。


第五阶段:最后看 Driver

先读:

text 复制代码
spec/driver-operations-redesign.md

再读:

text 复制代码
driver/driver.py
driver/operations/*
namespaces/*

最后才看:

text 复制代码
neo4j/*
falkordb/*
neptune/*
kuzu/*

这样不会被大量数据库语句淹没。


第六阶段:看 Prompt

重点把 Python 函数与 Prompt 一一对应:

text 复制代码
extract_nodes     -> prompts/extract_nodes.py
extract_edges     -> prompts/extract_edges.py
resolve nodes     -> prompts/dedupe_nodes.py
resolve edges     -> prompts/dedupe_edges.py
summaries         -> prompts/summarize_*.py

你会发现:

Graphiti 的"智能行为"是 算法控制流 + Prompt 合约 + 数据模型 三者共同决定的。


第31章 术语与缩写字典

术语 全称 / 中文 在 Graphiti 中的意思
Agent 智能体 使用上下文、工具、记忆执行任务的 AI 系统
Context Graph 上下文图 保存 Entity/Fact/Episode/时间/来源的动态图
Temporal Context Graph 时间上下文图 事实有有效期、可被后续事实失效的 Context Graph
KG Knowledge Graph,知识图谱 Entity + Relationship 图结构
Episode 事件/片段 一次原始输入或发生过的事件
Entity 实体 人、组织、设备、地点、项目等对象
Fact 事实 两实体之间带自然语言描述和时间的关系
Provenance 溯源 知识来源于哪些 Episode
Ontology 本体/领域模式 允许有哪些实体/关系/属性
Prescribed Ontology 预定义本体 开发者明确指定 schema
Learned Ontology 学习出的本体 系统从数据中发现/形成结构
LLM Large Language Model 大语言模型
NER Named Entity Recognition 命名实体识别
Embedding 向量嵌入 把文本映射到稠密向量
Vector Search 向量检索 根据 embedding 相似度召回
Cosine Similarity 余弦相似度 比较向量方向相似程度
Full-text Search 全文检索 按词项/倒排索引搜索
BM25 Best Matching 25 经典全文相关度算法
BFS Breadth-First Search 广度优先图遍历
RRF Reciprocal Rank Fusion 倒数排名融合
MMR Maximal Marginal Relevance 相关性与多样性平衡的重排
Cross-Encoder 交叉编码器 Query 和 Candidate 联合编码评分
Reranker 重排器 对召回候选进行二次排序
RAG Retrieval-Augmented Generation 检索增强生成
Driver 驱动/适配层 屏蔽不同图数据库实现
Provider 提供方 OpenAI/Gemini/Neo4j 等具体实现
Namespace 命名空间 API 按 Node/Edge 类型组织数据访问 API
Operations 操作契约 每种图对象的 CRUD/Search 抽象接口
ABC Abstract Base Class Python 抽象基类
IoC Inversion of Control 通过注入实现替换 Provider
DTO Data Transfer Object API 层请求/响应对象
UUID Universally Unique Identifier 全局唯一标识
MCP Model Context Protocol Agent/LLM 工具与上下文连接协议
OTEL OpenTelemetry 分布式 tracing/observability 标准
AOSS Amazon OpenSearch Serverless AWS Serverless OpenSearch 服务
Pydantic Python 数据模型库 schema、验证、structured output
Cypher 图查询语言 Neo4j/FalkorDB 等常用图查询语法
CRUD Create/Read/Update/Delete 增删改查
async/await 异步语法 并行调用 LLM、Embedding、DB
Semaphore 信号量 控制并发上限
Dedupe Deduplication 去重
Entity Resolution 实体解析/消歧 判断两个名字是否同一真实实体
Fact Resolution 事实解析 判断新事实与已有事实的关系
group_id 分组 ID 数据隔离/图分区的重要 key
Saga 连续事件链 有顺序的一组 Episodes
MENTIONS 提及边 Episode → Entity
RELATES_TO 实体关系边 Entity → Entity,载体是 EntityEdge
HAS_MEMBER 社区成员边 Community → Entity
HAS_EPISODE Saga 包含事件 Saga → Episode
NEXT_EPISODE 下一事件 Episode → Episode
reference_time 参考时间 Episode 对事实时间解析的锚点
valid_at 生效时间 Fact 开始为真
invalid_at 失效业务时间 Fact 停止为真
expired_at 系统发现失效时间 Graphiti 判断旧事实被新事实替代的时间
created_at 创建时间 数据进入 Graphiti 的系统时间

第32章 最终总结:把 Graphiti 看成什么最准确

如果只用一句话概括:

Graphiti = 一个以 Episode 为输入、以 Entity/Temporal Fact 为长期知识表示、以 Graph + Vector + Full-text 为存储检索基础、以 LLM 做抽取/消歧/时间理解、以 Hybrid Retrieval + Reranker 对外提供上下文的动态 Agent Memory Engine。

最关键的不是其中任何一个单点技术,而是它们的组合。
#mermaid-svg-OoDPMe48ugzlqP6v{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OoDPMe48ugzlqP6v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OoDPMe48ugzlqP6v .error-icon{fill:#552222;}#mermaid-svg-OoDPMe48ugzlqP6v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OoDPMe48ugzlqP6v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OoDPMe48ugzlqP6v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OoDPMe48ugzlqP6v .marker.cross{stroke:#333333;}#mermaid-svg-OoDPMe48ugzlqP6v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OoDPMe48ugzlqP6v p{margin:0;}#mermaid-svg-OoDPMe48ugzlqP6v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OoDPMe48ugzlqP6v .cluster-label text{fill:#333;}#mermaid-svg-OoDPMe48ugzlqP6v .cluster-label span{color:#333;}#mermaid-svg-OoDPMe48ugzlqP6v .cluster-label span p{background-color:transparent;}#mermaid-svg-OoDPMe48ugzlqP6v .label text,#mermaid-svg-OoDPMe48ugzlqP6v span{fill:#333;color:#333;}#mermaid-svg-OoDPMe48ugzlqP6v .node rect,#mermaid-svg-OoDPMe48ugzlqP6v .node circle,#mermaid-svg-OoDPMe48ugzlqP6v .node ellipse,#mermaid-svg-OoDPMe48ugzlqP6v .node polygon,#mermaid-svg-OoDPMe48ugzlqP6v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OoDPMe48ugzlqP6v .rough-node .label text,#mermaid-svg-OoDPMe48ugzlqP6v .node .label text,#mermaid-svg-OoDPMe48ugzlqP6v .image-shape .label,#mermaid-svg-OoDPMe48ugzlqP6v .icon-shape .label{text-anchor:middle;}#mermaid-svg-OoDPMe48ugzlqP6v .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OoDPMe48ugzlqP6v .rough-node .label,#mermaid-svg-OoDPMe48ugzlqP6v .node .label,#mermaid-svg-OoDPMe48ugzlqP6v .image-shape .label,#mermaid-svg-OoDPMe48ugzlqP6v .icon-shape .label{text-align:center;}#mermaid-svg-OoDPMe48ugzlqP6v .node.clickable{cursor:pointer;}#mermaid-svg-OoDPMe48ugzlqP6v .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OoDPMe48ugzlqP6v .arrowheadPath{fill:#333333;}#mermaid-svg-OoDPMe48ugzlqP6v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OoDPMe48ugzlqP6v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OoDPMe48ugzlqP6v .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OoDPMe48ugzlqP6v .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OoDPMe48ugzlqP6v .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OoDPMe48ugzlqP6v .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OoDPMe48ugzlqP6v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OoDPMe48ugzlqP6v .cluster text{fill:#333;}#mermaid-svg-OoDPMe48ugzlqP6v .cluster span{color:#333;}#mermaid-svg-OoDPMe48ugzlqP6v div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OoDPMe48ugzlqP6v .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OoDPMe48ugzlqP6v rect.text{fill:none;stroke-width:0;}#mermaid-svg-OoDPMe48ugzlqP6v .icon-shape,#mermaid-svg-OoDPMe48ugzlqP6v .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OoDPMe48ugzlqP6v .icon-shape p,#mermaid-svg-OoDPMe48ugzlqP6v .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OoDPMe48ugzlqP6v .icon-shape .label rect,#mermaid-svg-OoDPMe48ugzlqP6v .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OoDPMe48ugzlqP6v .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OoDPMe48ugzlqP6v .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OoDPMe48ugzlqP6v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Episode

发生了什么
LLM Extraction

有哪些实体/事实
Resolution

是不是已经知道
Temporal

什么时候为真
Provenance

从哪里得知
Graph

与什么有关
Search

现在该召回什么
Agent Context

从工程视角看,Graphiti 最值得学习的 5 个设计点是:

  1. Episode-first:不只存最终知识,也保存知识来源。
  2. Entity/Fact Resolution:写入前主动消歧和去重,而不是任由知识图膨胀。
  3. Temporal invalidation:新事实不会粗暴覆盖旧事实,而是维护有效时间。
  4. Hybrid Retrieval:BM25、向量和图遍历各做自己擅长的事,再统一重排。
  5. Provider abstraction:LLM、Embedding、Reranker、Graph DB 都通过接口替换。

如果把它用于长期陪伴型 Agent,最值得复用的不是"Graphiti 这个库本身"四个字,而是下面这套记忆思想:

text 复制代码
原始事件层
    ↓
结构化事实层
    ↓
实体消歧层
    ↓
时间生命周期层
    ↓
来源追踪层
    ↓
多路召回层
    ↓
高精度重排层
    ↓
Agent Working Context

这正是一个可长期演进的 Agent 记忆系统所需要的骨架。


附录 A:核心源码路径速查

text 复制代码
graphiti_core/
├─ graphiti.py                         # 总编排器
├─ nodes.py                            # 节点模型
├─ edges.py                            # 边模型
├─ graphiti_types.py                   # 公共类型 / GraphitiClients
├─ helpers.py                          # 通用工具
├─ decorators.py                       # 多 group 等 decorator
├─ graph_queries.py                    # 图查询基础
├─ errors.py                           # 核心异常
├─ tracer.py                           # tracing
│
├─ utils/
│  ├─ bulk_utils.py                    # 批量持久化
│  ├─ content_chunking.py              # 内容切分
│  ├─ datetime_utils.py                # 时间工具
│  ├─ text_utils.py                    # 文本工具
│  ├─ maintenance/
│  │  ├─ node_operations.py            # 节点抽取/去重
│  │  ├─ edge_operations.py            # 边抽取/去重/失效
│  │  ├─ combined_extraction.py        # 节点+边联合抽取
│  │  ├─ community_operations.py       # Community
│  │  ├─ dedup_helpers.py              # 去重 helper
│  │  ├─ attribute_utils.py            # 属性处理
│  │  └─ graph_data_operations.py      # 图数据维护
│  └─ ontology_utils/
│     └─ entity_types_utils.py          # Entity ontology
│
├─ search/
│  ├─ search.py                         # 搜索编排
│  ├─ search_utils.py                   # 底层搜索 primitive
│  ├─ search_config.py                  # 搜索配置类型
│  ├─ search_config_recipes.py          # 预设搜索配方
│  ├─ search_filters.py                 # 过滤
│  └─ search_helpers.py                 # 搜索 helper
│
├─ llm_client/
│  ├─ client.py                         # LLM ABC + orchestration
│  ├─ openai_base_client.py
│  ├─ openai_client.py
│  ├─ openai_generic_client.py
│  ├─ azure_openai_client.py
│  ├─ anthropic_client.py
│  ├─ gemini_client.py
│  ├─ groq_client.py
│  ├─ gliner2_client.py
│  ├─ config.py
│  ├─ cache.py
│  ├─ token_tracker.py
│  ├─ utils.py
│  └─ errors.py
│
├─ embedder/
│  ├─ client.py
│  ├─ openai.py
│  ├─ azure_openai.py
│  ├─ gemini.py
│  └─ voyage.py
│
├─ cross_encoder/
│  ├─ client.py
│  ├─ bge_reranker_client.py
│  ├─ gemini_reranker_client.py
│  └─ openai_reranker_client.py
│
├─ prompts/
│  ├─ lib.py
│  ├─ models.py
│  ├─ prompt_helpers.py
│  ├─ snippets.py
│  ├─ extract_nodes.py
│  ├─ extract_edges.py
│  ├─ extract_nodes_and_edges.py
│  ├─ dedupe_nodes.py
│  ├─ dedupe_edges.py
│  ├─ summarize_nodes.py
│  ├─ summarize_sagas.py
│  └─ eval.py
│
├─ namespaces/
│  ├─ nodes.py
│  └─ edges.py
│
├─ driver/
│  ├─ driver.py
│  ├─ query_executor.py
│  ├─ record_parsers.py
│  ├─ neo4j_driver.py
│  ├─ falkordb_driver.py
│  ├─ neptune_driver.py
│  ├─ kuzu_driver.py
│  ├─ operations/
│  ├─ graph_operations/
│  ├─ search_interface/
│  ├─ neo4j/
│  ├─ falkordb/
│  ├─ neptune/
│  └─ kuzu/
│
├─ models/
│  ├─ nodes/node_db_queries.py
│  └─ edges/edge_db_queries.py
│
├─ telemetry/
│  └─ telemetry.py
└─ migrations/

附录 B:核心 API 到源码入口映射

你想研究的问题 从这里开始
一条记忆怎么写进去 Graphiti.add_episode()
实体怎么抽 maintenance/node_operations.py::extract_nodes
同名实体怎么去重 resolve_extracted_nodes
关系怎么抽 maintenance/edge_operations.py::extract_edges
Fact 怎么去重 resolve_extracted_edges / resolve_extracted_edge
旧 Fact 怎么失效 edge_operations.py temporal invalidation 逻辑
时间怎么抽 _extract_edge_timestamps
Episode 与实体怎么连 build_episodic_edges
数据怎么批量入库 utils/bulk_utils.py
Query 怎么搜索 search/search.py::search
Edge 怎么搜 edge_search
Node 怎么搜 node_search
BM25/Vector/BFS 在哪 search/search_utils.py + Driver Search Ops
Search recipe 在哪 search/search_config_recipes.py
Graph DB 怎么抽象 driver/driver.py
新 DB API 架构 namespaces/* + driver/operations/*
为什么新旧 DB API 共存 spec/driver-operations-redesign.md
Prompt 在哪 prompts/*
LLM retry/cache 在哪 llm_client/client.py
Embedding 在哪 embedder/*
Reranker 在哪 cross_encoder/*
Community 怎么建 maintenance/community_operations.py
Saga 怎么组织 graphiti.py + SagaNode + saga edge ops
FastAPI 在哪 server/graph_service/*
MCP 在哪 mcp_server/src/*
tracing 在哪 tracer.py
匿名 telemetry 在哪 telemetry/telemetry.py

附录 C:建议实际调试的 6 个断点

如果你用 VS Code 调 Graphiti,建议先放:

text 复制代码
1. Graphiti.add_episode()
2. extract_nodes()
3. resolve_extracted_nodes()
4. resolve_extracted_edges()
5. _process_episode_data()
6. search/search.py::edge_search()

观察以下变量:

text 复制代码
previous_episodes
extracted_nodes
resolved_nodes
uuid_map
extracted_edges
resolved_edges
invalidated_edges
new_edges
query_vector
search candidates
reranker scores

只要这几个变量看明白,Graphiti 80% 的核心机制就真正吃透了。


附录 D:源码参考

仓库:

text 复制代码
https://github.com/getzep/graphiti

建议重点打开:

text 复制代码
graphiti_core/graphiti.py
graphiti_core/nodes.py
graphiti_core/edges.py
graphiti_core/utils/maintenance/node_operations.py
graphiti_core/utils/maintenance/edge_operations.py
graphiti_core/search/search.py
graphiti_core/search/search_config.py
graphiti_core/search/search_config_recipes.py
graphiti_core/driver/driver.py
graphiti_core/namespaces/nodes.py
graphiti_core/namespaces/edges.py
graphiti_core/llm_client/client.py
graphiti_core/prompts/lib.py
spec/driver-operations-redesign.md

由于 main 分支会持续变化,阅读时优先以"函数名 + 文件路径"定位,不建议长期依赖固定 GitHub 行号。

相关推荐
canonical_entropy1 小时前
可逆不是逆向运行:DeepSeek Harness 架构的数学本质
人工智能·架构·agent
南城以南溫暖如初1472 小时前
外卖CPS实战指南:从技术选型到运营落地全流程解析
java·spring boot·mysql·架构·vue
冬奇Lab3 小时前
Code Agent 解剖(05):模型怎么知道有哪些工具可以用?Function Calling 如何实现?
人工智能·开源·agent
ZGIAI3 小时前
ZGI Workflow:让知识检索进入业务流程
人工智能·架构
ZGIAI3 小时前
ZGI 模型网关:统一接入与路由大模型
人工智能·架构
易观Analysys3 小时前
中国低空经济产业链架构与图谱——《中国低空经济应用场景分析报告2026》解读一
架构
Elastic 中国社区官方博客3 小时前
ES95:面向 Elasticsearch 时间序列指标的自适应压缩
大数据·运维·elasticsearch·搜索引擎·架构·全文检索
粥里有勺糖4 小时前
分享一下最近做的AI记账 App | 欢迎体验
app·agent·ai编程
张忠琳5 小时前
【deepseek-harness】DeepSeek Harness (dsh) 系统级架构分析之二
ai·agent·deepseek·harness