☰
AI Agent 为什么总是忘事?我用 Graphiti 给它装了一个会记时间的知识图谱大脑
2026/9/25 20:52:19 网站建设 项目流程

我们现在做 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=password

Python 读取:

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 -> Go

Graphiti 会利用模型帮助抽取其中的实体和关系。

这就是它比普通 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 ↓ LLM

GraphRAG:

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 RAGGraphiti
文本语义检索强支持
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 建一个会随着时间不断更新的长期记忆网络。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询