我们现在做 AI Agent,经常会遇到一个很尴尬的问题:
模型明明很聪明,但就是记不住事。
比如你跟一个 AI 助手聊了半年。
三月份你告诉它:
我现在主要写 Java。
五月份你又说:
最近项目全部转 Go 了。
到了九月份,你问它:
我现在主要用什么语言?
如果只是简单把聊天记录丢进向量数据库,那么检索的时候很可能:
Java Go两条记录全给你搜出来。
于是模型懵了。
到底哪个才是真的?
这就是传统 RAG 很容易遇到的问题:
它擅长找“相关内容”,却不一定知道“现在什么才是真的”。
而今天要介绍的这个开源项目Graphiti,解决的恰好就是这个问题。
GitHub:
https://github.com/getzep/graphiti
Graphiti 官方把它定位为一个面向 AI Agent 的Temporal Knowledge Graph,也就是时序知识图谱 / Context Graph 框架。
它可以持续把聊天记录、业务数据和结构化数据转换成实体、关系和事实,并且记录这些事实在什么时间有效、什么时候失效。新数据可以增量进入图,而不是每次都重新构建整个知识库。
这东西,我觉得非常适合拿来理解:
Ontology → Knowledge Graph → GraphRAG → Agent Memory
这整条技术路线。
一、普通 RAG 到底哪里不够?
传统 RAG 大概是这么工作的:
用户数据 ↓ 切 Chunk ↓ Embedding ↓ Vector Database ↓ Similarity Search ↓ LLM比如:
2026-03-01 王小明主要使用 Java。 2026-05-01 王小明已经开始主要使用 Go。我们可能生成两个 Chunk:
documents=["王小明主要使用 Java","王小明已经开始主要使用 Go"]然后用户搜索:
query="王小明主要使用什么编程语言?"向量数据库非常可能两个都命中。
因为:
王小明 编程语言 Java Go语义都高度相关。
问题在于:
Vector Database 本身并不知道 Java 那条信息已经过期。
Graphiti 想解决的就是这一层。
它不只是存:
文本 → Embedding而是尽量把数据变成:
王小明 │ ├── USES ──> Java │ valid: 2026-03 ~ 2026-05 │ └── USES ──> Go valid: 2026-05 ~ now这一下味道就完全不一样了。
二、Graphiti 存的到底是什么?
先理解一个最基本的知识图谱结构:
Entity ↓ Relationship ↓ Entity比如:
王小明 --WORKS_AT--> OpenAI可以拆成:
Entity: 王小明 Relationship: WORKS_AT Entity: OpenAI这就是一个最简单的 Triplet:
王小明 -> WORKS_AT -> OpenAI但是 Graphiti 又多了一层非常重要的信息:
时间例如:
王小明 │ │ WORKS_AT ↓ 公司 A valid_at: 2024-01-01 invalid_at: 2026-05-01后来:
王小明 │ │ WORKS_AT ↓ 公司 B valid_at: 2026-05-01这样 AI 不只是知道:
王小明工作过哪些公司。
还可以理解:
王小明现在在哪家公司?
以及:
王小明 2025 年在哪家公司?
Graphiti 当前的 Context Graph 会保存 Entity、Fact/Relationship 和 Episode 等信息,其中 Episode 用于追踪原始数据来源;事实还能携带时间有效性。
三、Episode 是理解 Graphiti 的关键
Graphiti 里面有一个非常重要的概念:
Episode可以把它理解成:
一次真实世界的信息输入。
例如今天用户说:
我最近开始学习 Go。这是一个 Episode。
明天用户说:
我已经不用 Java 做新项目了。这又是一个 Episode。
再过几天,CRM 系统传进来:
{"customer":"Alice","plan":"Pro"}同样可以作为 Episode。
Graphiti 目前支持 text、message 和 JSON 等 Episode 类型。Episode 自己也是图中的节点,并能够帮助追踪某个实体或者关系究竟来自哪次原始输入。
于是你可以把整个 Agent Memory 理解成:
Conversation ↓ Episode ↓ Entity Extraction ↓ Relationship Extraction ↓ Temporal Knowledge Graph ↓ Search ↓ Agent Context这已经不是简单的“聊天记录数据库”了。
四、先把 Graphiti 跑起来
按照目前官方文档,Graphiti 推荐 Python 3.10+,常见后端可以使用 Neo4j 或 FalkorDB;默认可以配合 OpenAI 完成 LLM 推理和 Embedding,同时也支持其他模型提供商。
安装:
pipinstallgraphiti-core或者:
uvaddgraphiti-core准备环境变量:
exportOPENAI_API_KEY="sk-xxx"exportNEO4J_URI="bolt://localhost:7687"exportNEO4J_USER="neo4j"exportNEO4J_PASSWORD="your-password"如果习惯.env:
OPENAI_API_KEY=sk-xxx NEO4J_URI=bolt://localhost:7687 NEO4J_USER=neo4j NEO4J_PASSWORD=passwordPython 读取:
importosfromdotenvimportload_dotenv load_dotenv()NEO4J_URI=os.getenv("NEO4J_URI","bolt://localhost:7687")NEO4J_USER=os.getenv("NEO4J_USER","neo4j")NEO4J_PASSWORD=os.getenv("NEO4J_PASSWORD")五、初始化一个 Graphiti
最简单的代码:
importasyncioimportosfromgraphiti_coreimportGraphitiasyncdefmain():graphiti=Graphiti(os.getenv("NEO4J_URI","bolt://localhost:7687"),os.getenv("NEO4J_USER","neo4j"),os.getenv("NEO4J_PASSWORD","password"),)try:awaitgraphiti.build_indices_and_constraints()print("Graphiti 初始化完成")finally:awaitgraphiti.close()if__name__=="__main__":asyncio.run(main())第一次使用时:
awaitgraphiti.build_indices_and_constraints()会初始化 Graphiti 需要的索引和约束。
后面我们所有的数据都可以不断往这里写。
六、第一次给 AI 添加“记忆”
例如用户说:
王小明是一名后端工程师,目前主要使用 Java, 最近正在学习 Go。可以直接写入:
fromdatetimeimportdatetime,timezonefromgraphiti_core.nodesimportEpisodeTypeawaitgraphiti.add_episode(name="user_profile_001",episode_body=""" 王小明是一名后端工程师, 目前主要使用 Java, 最近正在学习 Go。 """,source=EpisodeType.text,source_description="用户个人介绍",reference_time=datetime.now(timezone.utc),)注意这里我们并没有自己写:
王小明 -> IS_A -> 后端工程师 王小明 -> USES -> Java 王小明 -> LEARNING -> GoGraphiti 会利用模型帮助抽取其中的实体和关系。
这就是它比普通 Neo4j CRUD 更有意思的地方。
七、把聊天记录直接变成知识图谱
Agent 最大的数据来源其实不是文档。
而是:
Conversation例如用户说:
王小明: 我最近项目开始使用 Go 了, Java 项目以后只负责维护。可以写:
awaitgraphiti.add_episode(name="conversation_002",episode_body=""" 王小明: 我最近的新项目已经开始主要使用 Go, Java 项目以后只负责维护。 """,source=EpisodeType.message,source_description="AI Assistant conversation",reference_time=datetime.now(timezone.utc),)这个场景特别重要。
因为 Agent 每一次聊天都可以成为:
Episode然后逐渐形成:
王小明 / | \ / | \ Java Go 后端开发 | | 过去使用 当前使用Agent 的长期记忆就这样慢慢长出来了。
八、JSON 业务数据一样可以进去
Graphiti 不只是处理自然语言。
比如电商系统有:
product={"product_id":"P10001","name":"AI Coding 实战课","price":499,"category":"AI","status":"active"}可以:
importjsonawaitgraphiti.add_episode(name="product_P10001",episode_body=json.dumps(product,ensure_ascii=False),source=EpisodeType.json,source_description="商品系统",reference_time=datetime.now(timezone.utc),)这里要注意:
episode_body传入的是:
JSON String而不是 Python Dict。官方的 JSON Episode 示例也是先json.dumps()后再写入。
于是以后甚至可以把:
聊天记录 CRM 订单 商品 客服记录 邮件 企业文档全部连接起来。
最终形成一个企业级 Context Graph。
九、真正有意思的地方来了:信息会变化
假设:
2026-01 王小明主要使用 Java。后来:
2026-06 王小明主要使用 Go。我们模拟两条数据。
第一条:
fromdatetimeimportdatetime,timezoneawaitgraphiti.add_episode(name="profile_2026_01",episode_body=""" 王小明目前主要使用 Java 开发后端项目。 """,source=EpisodeType.text,source_description="用户资料",reference_time=datetime(2026,1,10,tzinfo=timezone.utc),)第二条:
awaitgraphiti.add_episode(name="profile_2026_06",episode_body=""" 王小明的新项目已经全面转向 Go, Java 现在只维护历史系统。 """,source=EpisodeType.text,source_description="用户资料更新",reference_time=datetime(2026,6,20,tzinfo=timezone.utc),)这就是 Graphiti 非常重要的一点:
它不是简单粗暴地:
DELETE old memory INSERT new memory而是试图维护:
过去发生过什么 现在什么是真的 这个变化什么时候发生对于 Agent 来说,这比简单的 Vector RAG 有价值得多。
十、开始查询我们的“记忆”
最基础:
results=awaitgraphiti.search("王小明目前主要使用什么编程语言?")forresultinresults:print(result.fact)可能得到类似:
王小明的新项目主要使用 Go。 王小明过去主要使用 Java。 王小明仍维护部分 Java 系统。Graphiti 默认搜索会结合语义检索和全文检索,并通过排序策略把结果组合起来。官方文档目前介绍的基础search()会结合 semantic similarity 与 BM25;更高级的搜索还可以利用图距离等信号进行重排。
这实际上已经有一点:
Vector Search + Keyword Search + Graph Search的味道了。
十一、为什么这才叫 GraphRAG?
普通 RAG:
Query ↓ Vector Search ↓ Chunk 1 Chunk 2 Chunk 3 ↓ LLMGraphRAG:
Query ↓ Entity ↓ Relationship ↓ Connected Entity ↓ Facts ↓ LLM例如查询:
王小明现在负责哪个项目?如果知识图谱中有:
王小明 | | WORKS_ON ↓ 支付系统 | | USES ↓ Go | | USES ↓ Redis那么我们实际上可以顺着图继续寻找:
王小明 ↓ 支付系统 ↓ Go ↓ Redis这就是图结构相对于一堆孤立 Chunk 最大的价值:
关系本身也是知识。
十二、使用 Center Node 提高搜索准确度
假设图里面同时存在:
王小明 李小明 张小明用户问:
他现在在做什么?只做纯语义搜索可能比较模糊。
Graphiti 支持根据某个节点作为中心进行距离重排。
例如:
results=awaitgraphiti.search("王小明")ifresults:user_node_uuid=(results[0].source_node_uuid)results=awaitgraphiti.search("他最近主要负责什么项目?",center_node_uuid=user_node_uuid)foredgeinresults:print(edge.fact)这样搜索时:
距离“王小明”更近的事实会获得更高优先级。
这类 Graph Distance Reranking 特别适合:
Agent Memory 用户画像 企业人物关系 CRM 项目关系 组织架构官方搜索文档也专门提供了以中心节点进行 node-distance reranking 的查询方式。
十三、给不同用户做数据隔离
真正做 SaaS 的时候一定会遇到:
User A User B User C总不能全部混在一个知识图谱命名空间里面。
Graphiti 提供:
group_id例如:
awaitgraphiti.add_episode(name="alice_memory",episode_body=""" Alice 喜欢使用 Go, 最近正在学习 AI Agent。 """,source=EpisodeType.text,source_description="Alice Chat",reference_time=datetime.now(timezone.utc),group_id="user_alice",)Bob:
awaitgraphiti.add_episode(name="bob_memory",episode_body=""" Bob 是 Java 开发者, 最近正在学习 Spring AI。 """,source=EpisodeType.text,source_description="Bob Chat",reference_time=datetime.now(timezone.utc),group_id="user_bob",)查询 Alice:
results=awaitgraphiti.search(query="最近在学习什么?",group_id="user_alice")查询 Bob:
results=awaitgraphiti.search(query="最近在学习什么?",group_id="user_bob")于是就能形成:
Graphiti ├── user_alice │ ├── user_bob │ └── user_tom官方把这种机制称作 Graph Namespacing,可以在检索时使用group_id将查询限制在对应的命名空间。
这对多用户 Agent 非常实用。
十四、再往前一步:Ontology 来了
到这里,我们终于可以把 Graphiti 和前面说的:
Ontology串起来了。
假如我们做一个程序员知识图谱。
希望图里面主要有:
Developer Project Technology Company而不是让模型随便生成各种类型。
我们可以自己定义。
例如:
fromtypingimportOptionalfrompydanticimportBaseModel,FieldclassDeveloper(BaseModel):"""软件开发人员"""role:Optional[str]=Field(None,description="开发者职位,例如 Backend Engineer")years_of_experience:Optional[int]=Field(None,description="开发经验年限")primary_language:Optional[str]=Field(None,description="主要编程语言")classProject(BaseModel):"""软件项目"""project_type:Optional[str]=Field(None,description="项目类型")status:Optional[str]=Field(None,description="项目当前状态")classTechnology(BaseModel):"""编程技术"""category:Optional[str]=Field(None,description="技术类别,例如 Database、Language")这就是:
Ontology Schema然后:
entity_types={"Developer":Developer,"Project":Project,"Technology":Technology,}告诉 Graphiti:
尽量按照这套类型理解我的数据。
Graphiti 当前允许开发者通过 Pydantic 模型自定义 Entity Type 和 Edge Type,让领域数据以更明确的 Ontology 进入图。
十五、不只是 Entity,关系也可以定义
比如:
Developer | | DEVELOPS ↓ Project以及:
Project | | USES ↓ Technology我们定义:
classDevelops(BaseModel):"""开发者参与项目"""responsibility:Optional[str]=Field(None,description="开发者在项目中的职责")classUsesTechnology(BaseModel):"""项目使用某项技术"""purpose:Optional[str]=Field(None,description="该技术在项目中的用途")然后:
edge_types={"Develops":Develops,"UsesTechnology":UsesTechnology,}告诉系统什么实体之间可以出现什么关系:
edge_type_map={("Developer","Project"):["Develops"],("Project","Technology"):["UsesTechnology"],}最终调用:
awaitgraphiti.add_episode(name="developer_profile",episode_body=""" 王小明是一名后端工程师, 有 8 年开发经验。 他目前负责支付系统, 主要使用 Go。 支付系统使用 Redis 做缓存和分布式锁。 """,source=EpisodeType.text,source_description="研发人员信息",reference_time=datetime.now(timezone.utc),entity_types=entity_types,edge_types=edge_types,edge_type_map=edge_type_map,)于是原来的自然语言:
王小明是一名拥有 8 年经验的后端工程师, 负责支付系统, 项目使用 Go 和 Redis。就有机会逐渐变成:
王小明 Developer │ DEVELOPS ↓ 支付系统 Project │ USES ├────────> Go │ Technology │ └────────> Redis Technology这就是:
Ontology + Knowledge Graph + LLM Extraction。
Graphiti 的自定义类型机制实际上就是把 Pydantic Schema 参与到实体分类、属性抽取和关系抽取流程中。
十六、还可以直接手动插入 Triplet
有些业务数据根本不需要 LLM 抽取。
比如数据库已经明确告诉你:
Bob likes bananas那么完全可以直接写图。
importuuidfromdatetimeimportdatetimefromgraphiti_core.nodesimportEntityNodefromgraphiti_core.edgesimportEntityEdge bob_uuid=str(uuid.uuid4())banana_uuid=str(uuid.uuid4())bob=EntityNode(uuid=bob_uuid,name="Bob",group_id="demo")banana=EntityNode(uuid=banana_uuid,name="Banana",group_id="demo")likes=EntityEdge(group_id="demo",source_node_uuid=bob_uuid,target_node_uuid=banana_uuid,created_at=datetime.now(),name="LIKES",fact="Bob likes bananas",)awaitgraphiti.add_triplet(bob,likes,banana)也就是直接得到:
Bob │ │ LIKES ↓ Banana官方也提供了add_triplet()接口用于这种已经明确知道实体和关系的场景,并会尝试对节点和关系进行去重。
这意味着实际项目中完全可以:
LLM 抽取 + 数据库直接同步同时存在。
十七、批量导入企业数据
如果有十万条商品数据,总不能一个一个:
awaitadd_episode()Graphiti 提供了批量 Episode 导入。
例如:
importjsonfromdatetimeimportdatetime,timezonefromgraphiti_core.nodesimportEpisodeTypefromgraphiti_core.utils.bulk_utilsimportRawEpisode products=[{"id":1001,"name":"MacBook Pro","category":"Computer"},{"id":1002,"name":"Mac mini","category":"Computer"},{"id":1003,"name":"iPhone","category":"Phone"}]转换:
episodes=[]forproductinproducts:episode=RawEpisode(name=f"product_{product['id']}",content=json.dumps(product,ensure_ascii=False),source=EpisodeType.json,source_description="商品数据库",reference_time=datetime.now(timezone.utc))episodes.append(episode)批量写入:
awaitgraphiti.add_episode_bulk(episodes)不过这里有一个很重要的区别。
官方明确说明:
add_episode_bulk()更适合初始化空图的大批量加载,因为 Bulk Pipeline 不执行普通增量写入里的 edge invalidation。
所以:
初始化 100 万历史数据适合 Bulk。
而:
用户刚刚说了一句话这种实时数据更适合正常的:
add_episode()十八、最后把 Graphiti 接到 AI Agent
做到这里,你会发现其实已经只差最后一步了。
流程:
用户提问 ↓ Graphiti Search ↓ Relevant Facts ↓ System Context ↓ LLM ↓ Answer伪代码非常简单:
asyncdefget_memory(graphiti,user_id,query):results=awaitgraphiti.search(query=query,group_id=user_id)facts=[result.factforresultinresults]return"\n".join(facts)然后:
memory=awaitget_memory(graphiti,"user_10001","我最近主要在研究什么?")得到:
用户最近正在学习 Go。 用户最近关注 AI Agent。 用户正在研究 GraphRAG。 用户准备做一个知识图谱项目。接着:
prompt=f""" 你是一名 AI 助手。 下面是用户长期记忆中检索出来的事实:{memory}用户问题: 我最近主要在研究什么? 请根据可靠事实回答。 """再交给大模型。
这实际上就是一个非常基础的:
Agent Memory Layer十九、Graphiti 和传统 RAG 到底有什么区别?
可以用这张表快速理解:
| 能力 | Vector RAG | Graphiti |
|---|---|---|
| 文本语义检索 | 强 | 支持 |
| Keyword Search | 看实现 | 支持 |
| Entity | 弱 | 强 |
| Relationship | 弱 | 强 |
| 时间变化 | 弱 | 核心能力 |
| 历史事实 | 不擅长 | 支持 |
| 数据来源追踪 | 需要自己做 | Episode |
| 增量更新 | 支持 | 支持 |
| Ontology | 通常没有 | 支持 |
| Graph Traversal | 没有 | 支持 |
| Agent 长期记忆 | 需要自己封装 | 非常适合 |
所以我更愿意这样理解:
传统 RAG 解决的是:
“这句话在哪?”而 Knowledge Graph / Graphiti 更擅长解决:
“谁和谁有什么关系?”再进一步,Graphiti 还希望回答:
“这个关系什么时候成立?” “现在还成立吗?” “以前是什么状态?” “这条事实是从哪里来的?”这就是它真正值得研究的地方。
二十、这也是为什么 Ontology 又重新重要起来了
很多人第一次看到 Ontology 会觉得:
OWL RDF SPARQL 本体 知识图谱这些是不是十几年前的技术?
其实恰恰相反。
到了 Agent 时代以后,我们越来越需要解决一个问题:
怎么让 AI 对现实世界形成稳定、结构化、可维护的认识?
LLM 非常擅长:
理解自然语言但是企业系统真正需要的是:
Customer Order Product Developer Project Company以及:
PURCHASED WORKS_AT DEVELOPS USES BELONGS_TO DEPENDS_ON所以:
LLM负责理解非结构化世界。
Ontology负责定义世界有哪些概念。
Knowledge Graph负责保存这些概念之间的关系。
GraphRAG负责把这些知识取出来。
Agent最后根据这些知识执行任务。
把它们放在一起:
Natural Language │ ↓ LLM │ ↓ Ontology │ ↓ Knowledge Graph │ ↓ GraphRAG │ ↓ Agent整个链路一下就通了。
所以如果你正在研究 AI Agent,我建议不要只盯着:
Prompt Function Calling MCP往下面再挖一层,你迟早会碰到三个问题:
Agent 怎么长期记忆? Agent 怎么理解关系? Agent 怎么知道信息已经变了?而这三个问题的背后,很可能就是:
Ontology + Knowledge Graph + Temporal Graph + GraphRAG。
Graphiti 正好是一个非常适合拿来把这些概念串起来的开源项目。
GitHub:
https://github.com/getzep/graphiti
如果说传统 RAG 是给 AI 加了一个“搜索框”,那么 Graphiti 这类项目做的事情,更像是在尝试:
给 AI 建一个会随着时间不断更新的长期记忆网络。