知识图谱作为智能体长期记忆的一种关系记忆(Relational Memory)形态,擅长表达实体之间的结构化关系,支持多跳推理、关系查询和事实一致性维护。本文从概念、建模、存储、检索到与智能体集成做系统梳理。
目录
一、为什么用知识图谱做记忆
二、核心概念
三、数据建模
四、存储与查询
五、从对话中抽取知识图谱
六、检索与推理
七、与智能体集成架构
八、代码示例
九、更新与遗忘
十、与向量记忆的对比与融合
十一、进阶方向
一、为什么用知识图谱做记忆
向量记忆擅长语义相似度检索,但存在明显短板:
结论:知识图谱适合存储实体、关系、事实类长期记忆,如"用户 A 是 B 公司的工程师"“项目 X 依赖库 Y”。
二、核心概念
三、数据建模
- 模式设计(Schema)
为智能体记忆设计一个轻量本体,避免过度复杂:
实体类型:-Person# 用户、联系人-Organization# 公司、团队-Project# 项目-Technology# 技术、工具、语言-Event# 事件、会议-Preference# 偏好关系类型:-WORKS_FOR# Person -> Organization-USES# Person/Project -> Technology-DEPENDS_ON# Project -> Technology-PREFERS# Person -> Preference-PARTICIPATED_IN# Person -> Event-KNOWS# Person -> Person-LOCATED_IN# Organization -> Location- 实体消歧(Entity Resolution)
同一实体可能有多种表述(“张三” / “张先生” / “Zhang San”),需要归一化:
规则匹配:别名表、同义词库
向量相似度:embedding 相近则合并
LLM 判断:让模型判断两个提及是否指同一实体
defresolve_entity(mention,existing_entities,llm):prompt=f"""判断"{mention}"是否与以下任一实体指同一对象, 返回实体ID或"NONE":\n{existing_entities}"""returnllm.invoke(prompt)- 时间维度
记忆需要时间戳,支持"某时刻的事实":
triple={"head":"张三","relation":"WORKS_FOR","tail":"B公司","valid_from":"2023-01-01","valid_to":None,# None 表示当前有效"source":"对话-2024-05-01"}四、存储与查询
4. 图数据库选型
- Cypher 查询示例(Neo4j)
// 创建实体与关系 MERGE (p:Person {name: "张三"}) MERGE (o:Organization {name: "B公司"}) MERGE (p)-[:WORKS_FOR {since: "2023-01-01"}]->(o) // 查询:张三任职的公司 MATCH (p:Person {name: "张三"})-[:WORKS_FOR]->(o) RETURN o.name // 多跳:张三的同事使用的技术 MATCH (p:Person {name:"张三"})-[:WORKS_FOR]->(o)<-[:WORKS_FOR]-(colleague) -[:USES]->(tech:Technology) RETURN DISTINCT colleague.name, tech.name- Python 驱动
fromneo4jimportGraphDatabase driver=GraphDatabase.driver("bolt://localhost:7687",auth=("neo4j","password"))defadd_triple(tx,head,relation,tail):query=f""" MERGE (h:Entity {{name: $head}}) MERGE (t:Entity {{name: $tail}}) MERGE (h)-[r:{relation}]->(t) """tx.run(query,head=head,tail=tail)withdriver.session()assession:session.execute_write(add_triple,"张三","WORKS_FOR","B公司")五、从对话中抽取知识图谱
- LLM 抽取流程
对话文本 → LLM 抽取 → 三元组列表 → 实体消歧 → 写入图谱- 抽取 Prompt 设计
EXTRACT_PROMPT="""从以下对话中抽取实体和关系,以 JSON 返回。 格式: { "entities": [{"name": "...", "type": "Person|Organization|Technology|..."}], "relations": [{"head": "...", "relation": "WORKS_FOR|USES|...", "tail": "..."}] } 只抽取明确、稳定的事实,忽略闲聊。 对话: {dialogue} """- 抽取代码
importjsondefextract_kg(dialogue,llm):resp=llm.invoke(EXTRACT_PROMPT.format(dialogue=dialogue))data=json.loads(resp)returndata["entities"],data["relations"]defwrite_to_graph(entities,relations,session):foreinentities:session.run("MERGE (n:Entity {name: $name}) SET n.type = $type",name=e["name"],type=e["type"])forrinrelations:session.run(f""" MATCH (h:Entity {{name: $head}}) MATCH (t:Entity {{name: $tail}}) MERGE (h)-[:{r['relation']}]->(t) """,head=r["head"],tail=r["tail"])安全提示:关系类型不能直接字符串拼接进 Cypher(有注入风险),生产环境应使用白名单校验。
六、检索与推理
- 子图检索
给定查询,找到相关实体,抽取其邻域子图:
defget_subgraph(session,entity_name,depth=2):query=f""" MATCH path = (n:Entity {{name: $name}})-[*1..{depth}]-(m) RETURN path """returnsession.run(query,name=entity_name).data()- 多跳推理
// 张三的同事中,谁使用 Python? MATCH (p:Person {name:"张三"})-[:WORKS_FOR]->(o)<-[:WORKS_FOR]-(c) -[:USES]->(t:Technology {name:"Python"}) RETURN c.name- 图 + 向量混合检索
先用向量找到入口实体,再在图谱中扩展:
defhybrid_retrieve(query,vectorstore,session,depth=2):# 1. 向量检索定位入口实体candidates=vectorstore.similarity_search(query,k=3)entry_entities=[c.metadata["entity"]forcincandidates]# 2. 图谱扩展subgraph=[]foreinentry_entities:subgraph.extend(get_subgraph(session,e,depth))returnsubgraph- 子图转文本注入 Prompt
defsubgraph_to_text(subgraph):lines=[]forrelinsubgraph:forseginrel.get("path",[]):pass# 简化:三元组转自然语言return"\n".join(f"{h}{r}{t}"forh,r,tinsubgraph)七、与智能体集成架构
┌────────────────────────────────────────────────┐ │ Agent │ │ │ │ 用户输入 ──► 查询理解 ──► 混合检索 │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ 向量库 知识图谱 KV画像 │ │ └───────────────┼───────────────┘ │ │ ▼ │ │ 子图 + 片段 │ │ │ │ │ ▼ │ │ Prompt 组装 ──► LLM │ │ │ │ │ ▼ │ │ 回复 + 抽取新三元组 │ │ │ │ │ ▼ │ │ 写入图谱 │ └────────────────────────────────────────────────┘关键点:
图谱与向量库互补,不是替代
检索阶段用向量定位入口,图谱做关系扩展
写入阶段用 LLM 抽取 + 消歧 + 去重
八、代码示例
完整最小实现(NetworkX 版,便于本地跑通)
importnetworkxasnxclassKnowledgeGraphMemory:def__init__(self):self.g=nx.MultiDiGraph()defadd_triple(self,head,relation,tail,**attrs):self.g.add_node(head)self.g.add_node(tail)self.g.add_edge(head,tail,key=relation,relation=relation,**attrs)defquery_neighbors(self,entity,depth=1):ifentitynotinself.g:return[]nodes=nx.single_source_shortest_path_length(self.g.to_undirected(),entity,cutoff=depth)sub=self.g.subgraph(nodes.keys())return[(u,d["relation"],v)foru,v,dinsub.edges(data=True)]defto_text(self,entity,depth=1):triples=self.query_neighbors(entity,depth)return"\n".join(f"{h}--{r}-->{t}"forh,r,tintriples)# 使用kg=KnowledgeGraphMemory()kg.add_triple("张三","WORKS_FOR","B公司")kg.add_triple("张三","USES","Python")kg.add_triple("B公司","LOCATED_IN","北京")print(kg.to_text("张三",depth=2))# 输出:# 张三 --WORKS_FOR--> B公司# 张三 --USES--> Python# B公司 --LOCATED_IN--> 北京Neo4j 生产版封装
classNeo4jKG:def__init__(self,uri,user,password):self.driver=GraphDatabase.driver(uri,auth=(user,password))defclose(self):self.driver.close()defadd_triple(self,head,relation,tail):assertrelation.isidentifier(),"非法关系名"withself.driver.session()ass:s.run(f""" MERGE (h:Entity {{name: $head}}) MERGE (t:Entity {{name: $tail}}) MERGE (h)-[:{relation}]->(t) """,head=head,tail=tail)defget_context(self,entity,depth=2):withself.driver.session()ass:result=s.run(f""" MATCH (n:Entity {{name: $name}})-[r*1..{depth}]-(m) RETURN n.name AS h, [x IN r | type(x)] AS rels, m.name AS t LIMIT 50 """,name=entity)return[dict(rec)forrecinresult]九、更新与遗忘
- 事实更新
当出现新事实与旧事实冲突时:
// 关闭旧关系(时间维度) MATCH (p:Person {name:"张三"})-[r:WORKS_FOR]->(o:Organization {name:"A公司"}) SET r.valid_to = date() // 新增新关系 MERGE (p)-[:WORKS_FOR {since: date()}]->(:Organization {name:"B公司"})- 遗忘策略
TTL 过期:给关系加 expire_at,定期清理
置信度衰减:给关系加 confidence,随时间或反例衰减
重要性淘汰:按访问频次(LRU)或重要性分数淘汰
显式删除:用户要求"忘掉 X"时删除相关子图
defforget_entity(session,name):session.run(""" MATCH (n:Entity {name: $name}) DETACH DELETE n """,name=name)十、与向量记忆的对比与融合
融合架构(GraphRAG 思路):
原始文本 → 向量库存原文
抽取实体关系 → 图谱存结构
检索时:向量找入口 → 图谱扩展邻居 → 合并原文片段
注入 Prompt:结构化三元组 + 原文证据
defgraph_rag_retrieve(query,vectorstore,kg,depth=2):# 向量定位docs=vectorstore.similarity_search(query,k=3)entities=[d.metadata.get("entity")fordindocsifd.metadata.get("entity")]# 图谱扩展graph_ctx=[]foreinentities:graph_ctx.extend(kg.get_context(e,depth))return{"docs":docs,"graph":graph_ctx}十一、进阶方向
GraphRAG:微软提出的图谱增强 RAG,含社区检测与分层摘要
时序知识图谱:带时间维度的推理(TKG)
事件图谱:把事件作为一等公民,建模因果链
本体自动演化:让 LLM 根据新数据动态扩展 schema
神经符号结合:GNN + LLM,图嵌入用于检索排序
多智能体共享图谱:多个 Agent 共享一个知识图谱作为公共记忆
图谱压缩:大图摘要成子图或文本,适配上下文窗口
参考实现速查
LangChain LLMGraphTransformer 示例:
fromlangchain_experimental.graph_transformersimportLLMGraphTransformerfromlangchain_openaiimportChatOpenAI transformer=LLMGraphTransformer(llm=ChatOpenAI(model="gpt-4o-mini"))graph_docs=transformer.convert_to_graph_documents([document])# graph_docs[0].nodes / graph_docs[0].relationships```知识图谱记忆的核心价值在于结构化、可推理、可解释。实际落地时通常与向量记忆组合使用,形成"向量找入口、图谱理关系、原文做证据"的混合记忆架构。如果你有具体场景(如企业知识助手、个人助理、多智能体协作),可以进一步定制 schema 与检索策略。