知识图谱与RAG的结合使用
知识图谱 RAG(GraphRAG)完整通俗解释
一、基础概念区分
普通文本 RAG(传统 RAG)数据源:非结构化文档(PDF、Word、网页文本) 处理方式:文本切分→向量嵌入→向量库检索→大模型回答短板:只能捕捉浅层语义相似度,无法理解实体之间的关系。 例如: 文档:
张三在A公司任职,A公司总部在北京提问:张三所在公司总部在哪? 传统 RAG 依靠文本片段匹配,容易断裂关联,复杂多跳推理极易出错。知识图谱(KG,Knowledge Graph)核心结构:实体 - 关系 - 实体三元组标准格式:
(头实体,关系,尾实体)示例:(张三,任职于,A 公司)、(A 公司,总部位于,北京) 特点:结构化存储,天然支持多跳关联查询。知识图谱 RAG = GraphRAG融合方案:知识图谱 + 向量检索 + 大语言模型两种主流路线:
- 轻量:混合 RAG(知识图谱 + 文本向量库并行检索)
- 深度:原生 GraphRAG(微软 GraphRAG 方案,文本自动构建图谱 + 社区摘要)
二、两种主流 GraphRAG 架构
方案 1:混合式知识图谱 RAG(工业最常用)
整体流程分为构建阶段 + 推理阶段
1)离线构建(数据预处理)
① 原始非结构化文本(报告、知识库、论文) ②实体 & 关系抽取(LLM / 信息抽取模型,抽取出三元组) ③ 存入知识图谱数据库(Neo4j、NebulaGraph、TuGraph) ④ 同时把原始文本切片,生成向量存入向量数据库(Chroma、Milvus、PGVector)
两套索引并存:图谱(存关系)+ 向量库(存原始上下文)
2)在线问答(检索生成)
- 用户问题输入
- 分支 1:问题解析 → 提取实体,生成 Cypher/nGQL 图谱查询语句 在 KG 中查询实体、关联链路,拿到结构化事实
- 分支 2:问题向量化,向量库检索相似文本片段
- 融合两路检索结果,拼接 Prompt 送入大模型
- LLM 结合结构化知识 + 原文上下文生成答案
✅优势:
- 结构化知识保证事实准确,解决大模型幻觉
- 向量文本保留细节描述,弥补图谱信息缺失
- 改造简单,可以在现有 RAG 系统上叠加知识图谱
方案 2:微软 GraphRAG(原生图谱 RAG)
核心创新:不只抽取三元组,对图谱做社区聚类 + 高层摘要流程:
- 文本分块 → 抽取实体、关系构建全局知识图谱
- 使用图算法(Louvain)对图谱划分社区(高度关联实体集群)
- 对每个社区生成高层文本摘要(宏观知识)
- 查询时支持两级检索:
- 局部检索:精准实体、近距离关系(细粒度事实)
- 全局检索:社区摘要,适合宏观、综合性问题 适合:长篇书籍、大量文档综合分析、跨文档多跳推理
三、知识图谱 RAG 解决了传统 RAG 哪些痛点
✅多跳推理能力增强传统 RAG:多步关联信息分散在不同文本块,很难一次性召回; GraphRAG:沿着图谱关系链式查询,轻松实现 A→B→C→D 多跳问答。
✅减少事实幻觉知识图谱存储确定性三元组,模型可以严格基于结构化事实作答,不容易编造不存在的关联。
✅精准关联检索,避免语义漂移向量检索依靠相似度,容易出现 “字面相似但无关”;图谱基于实体精确匹配。
✅支持复杂逻辑类问题例如统计、链路溯源、关系梳理、因果查询,单纯向量 RAG 很难胜任。
四、知识图谱 RAG 存在的短板(重点)
- 实体关系抽取成本高非结构化文本自动抽取三元组总会存在错误,脏数据会传导至问答;高质量图谱往往需要人工校验。
- 查询门槛高需要 LLM 把自然语言正确转换成图查询语句 (Cypher),转换失败则图谱检索失效(常见难点)。
- 不擅长开放式细节描述知识图谱只存结构化关系,长段落、细节描述依然依赖原始文本向量。
结论:几乎没有纯图谱 RAG 落地,工业界都是「图谱 + 向量双检索混合架构」
五、极简对比:传统 RAG VS GraphRAG
表格
| 维度 | 普通向量 RAG | 知识图谱 RAG (GraphRAG) |
|---|---|---|
| 数据形态 | 文本块、向量 | 实体 + 关系(三元组)+ 向量文本 |
| 擅长场景 | 浅层问答、细节查找 | 多跳推理、关联溯源、知识脉络梳理 |
| 推理方式 | 语义相似度匹配 | 图遍历 + 语义检索融合 |
| 依赖能力 | 嵌入模型 | 实体抽取、图查询、向量模型 |
| 维护成本 | 低 | 较高(图谱治理、清洗) |
六、典型落地场景
- 企业知识库:人员、项目、组织关系查询
- 医疗:疾病、症状、药物多跳关联问诊
- 政务 / 法律:法条、案件主体关系梳理
- 环境监测、智能制造设备故障溯源
- 金融:企业股权、担保关系穿透查询
七、最简工作示例
知识库两段文本:
文本 1:华为发布鸿蒙操作系统。 文本 2:鸿蒙系统搭载方舟编译器。
- 抽取三元组: (华为,发布,鸿蒙 OS)、(鸿蒙 OS, 搭载,方舟编译器)
- 用户提问:华为发布的系统使用什么编译器?
- 图谱执行两跳查询:华为→鸿蒙 OS→方舟编译器
- 同时召回原始文本作为补充上下文
- LLM 整合信息输出答案
补充关键术语
二、LangChain + Neo4j GraphRAG 最简可运行 Demo
环境依赖安装
bash
pip install langchain langchain-openai langchain-neo4j neo4j python-dotenv前置条件
完整代码 graph_rag_demo.py
python
运行
from dotenv import load_dotenv import os from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_neo4j import Neo4jGraph, GraphQAChain from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.vectorstores import Neo4jVector from langchain.chains import RetrievalQA load_dotenv() # ====================== 1. 配置连接信息 ====================== NEO4J_URI = "bolt://localhost:7687" NEO4J_USER = "neo4j" NEO4J_PASSWORD = "你的neo4j密码" LLM_API_KEY = os.getenv("OPENAI_API_KEY") LLM_BASE_URL = "https://api.openai.com/v1" EMBEDDING_MODEL = "text-embedding-ada-002" LLM_MODEL = "gpt-3.5-turbo" # 初始化图数据库连接 graph = Neo4jGraph( url=NEO4J_URI, username=NEO4J_USER, password=NEO4J_PASSWORD ) # 初始化大模型 & Embedding llm = ChatOpenAI( model=LLM_MODEL, api_key=LLM_API_KEY, base_url=LLM_BASE_URL, temperature=0 ) embeddings = OpenAIEmbeddings(model=EMBEDDING_MODEL, api_key=LLM_API_KEY) # ====================== 2. 初始化向量库(Neo4j内置向量索引,简化部署) ====================== # 如果你有独立Milvus,可以替换成Milvus neo4j_vector_store = Neo4jVector( embedding=embeddings, url=NEO4J_URI, username=NEO4J_USER, password=NEO4J_PASSWORD, index_name="document_vector", node_label="DocumentChunk", text_node_property="text", embedding_node_property="embedding" ) retriever = neo4j_vector_store.as_retriever(search_kwargs={"k": 3}) # ====================== 3. 导入测试知识(三元组+文档文本) ====================== # 清空测试数据 graph.query("MATCH (n) DETACH DELETE n") # 写入知识图谱三元组 cypher_insert = """ CREATE (huawei:Company{name:"华为"}) CREATE (harmonyos:System{name:"鸿蒙操作系统"}) CREATE (ark:Compiler{name:"方舟编译器"}) CREATE (huawei)-[:RELEASE]->(harmonyos) CREATE (harmonyos)-[:DEPEND_ON]->(ark) """ graph.query(cypher_insert) # 写入原始文档片段,存入向量库 texts = [ "华为正式发布鸿蒙操作系统,面向全场景智能设备。", "鸿蒙操作系统深度搭载方舟编译器,提升应用运行效率。", "方舟编译器支持静态编译,提升移动端程序性能。" ] neo4j_vector_store.add_texts(texts) # ====================== 4. 构建两条链路:图谱问答链 + 向量检索链 ====================== # GraphQAChain:自然语言自动生成Cypher查询图谱 graph_qa_chain = GraphQAChain(llm=llm, graph=graph, verbose=True) # 普通向量RAG链 vector_qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=retriever, verbose=True ) # ====================== 5. 融合GraphRAG主逻辑(混合检索) ====================== def graph_rag_query(question: str): print("=====【1.知识图谱结构化查询结果】=====") try: graph_result = graph_qa_chain.invoke({"query": question}) graph_context = graph_result["result"] except Exception as e: print(f"Cypher生成/执行失败,降级:{e}") graph_context = "图谱未查询到相关信息" print("\n=====【2.向量文本检索结果】=====") vector_result = vector_qa_chain.invoke({"query": question}) vector_context = vector_result["result"] # 融合上下文,交给LLM最终汇总回答 merge_prompt = f""" 【图谱结构化知识】 {graph_context} 【文档原文上下文】 {vector_context} 用户问题:{question} 要求:结合上面所有信息回答,不要编造不存在事实;如果信息不足如实说明。 """ final_answer = llm.invoke(merge_prompt).content return final_answer # ====================== 测试调用 ====================== if __name__ == "__main__": query = "华为发布的操作系统使用什么编译器?" ans = graph_rag_query(query) print("\n=====最终GraphRAG回答=====") print(ans)适配改造提示:
- 国内大模型:替换 ChatOpenAI 为 LangChain 对应厂商类(DashScope、ZhipuAI)
- 分离向量库:Milvus/PGVector 替代 Neo4jVector
- 增量数据:新增文档自动抽取三元组(可搭配 LLM 做 NER+RE)
三、自然语言转 Cypher(NL2Cypher)提示词模板
模板 A:标准版(日常业务知识库推荐,LangChain GraphQAChain 默认替换 prompt)
plaintext
你是Neo4j Cypher查询专家,根据知识图谱结构,将用户自然语言问题生成正确、可直接执行的Cypher语句。 【图谱Schema信息(必须同步最新实体、关系)】 实体标签:Company, System, Compiler 关系类型: - (Company)-[:RELEASE]->(System) - (System)-[:DEPEND_ON]->(Compiler) 实体属性:name 规则约束: 1. 只输出Cypher代码,不要额外解释,不要markdown代码块以外多余文字; 2. 实体名称严格模糊匹配,使用toLower()或者contains避免大小写问题; 3. 禁止执行删除、修改、创建类语句(只允许MATCH查询); 4. 如果无法构建有效查询,直接返回文本:NO_QUERY; 5. 不要使用不存在的实体标签、关系名称; 6. 不要返回图中不存在的关联; 用户问题:{query} Cypher:模板 B:增强容错版(生产环境首选,增加异常处理、多跳查询引导)
plaintext
# 任务:自然语言转Neo4j Cypher只读查询 # 数据库Schema 节点标签与属性: Company{name}, System{name}, Compiler{name} 关系: [:RELEASE] 公司发布系统 [:DEPEND_ON] 系统依赖编译器 # 严格规则 1. 仅生成MATCH查询,严禁CREATE、DELETE、SET、MERGE写操作; 2. 识别问题中的实体,优先匹配name属性;支持同义词模糊检索; 3. 需要多跳推理时,正确书写链式路径 (a)-[r1]->(b)-[r2]->(c) 4. 如果问题信息不足、找不到对应关系,输出 NO_QUERY 5. 输出内容只能是Cypher语句,禁止额外说明、中文解释; 用户提问:{question} 输出Cypher:模板 C:带结果解释增强版(GraphRAG 端到端,查询后自动解析)
使用场景:生成 Cypher + 同时指导 LLM 读懂查询返回的图结构数据
plaintext
你需要完成两步工作: 第一步:根据图谱Schema生成合法只读Cypher; 第二步:想好如何利用Cypher返回的图数据回答用户问题。 【图谱Schema】 节点:Company, System, Compiler 关系:RELEASE, DEPEND_ON 约束: - 禁止写操作;实体基于name匹配;多跳链路完整书写; - 无法构建查询输出 NO_QUERY; 输出格式: CYPHER:【生成的语句】 GUIDE:【简要说明拿到查询结果后如何组织答案】 用户问题:{query}使用关键工程技巧
拓展可选优化方向(如果你后续要落地)
- 三元组 Triplet:知识图谱基础单元 (s,p,o)
- Cypher:Neo4j 查询语言
- 实体链接 Entity Linking:把文本名词匹配图谱中标准实体,解决同义词问题
- 关系抽取 RE:识别两个实体之间存在何种关联
包含:1. 文字架构图|2.LangChain+Neo4j 最简 Demo|3.NL2Cypher 提示词模板
方案:混合式 GraphRAG(工业主流)架构:知识图谱检索 + 向量文本检索两路融合,区别于微软原生 GraphRAG
一、GraphRAG 系统工程架构图【文字版】
分为两大阶段:离线构建流水线+在线推理问答流水线
plaintext
==================== 离线构建阶段(数据入库,一次性/定时增量)==================== 原始数据源 │ ├─ 非结构化文档(PDF/MD/TXT) ──文档加载器──> 文本切分(RecursiveCharacterTextSplitter) │ │ │ ├─────────────────────┐ │ ▼ ▼ │ 文本块向量化嵌入 LLM实体关系抽取(RE+NER) │ │ │ │ ▼ ▼ │ 向量数据库(Milvus/PGVector) 三元组(s,p,o) │ │ │ ▼ │ 实体链接 & 消歧(同义词归一) │ │ │ ▼ │ 知识图谱数据库(Neo4j) │ └─ 结构化业务数据(Excel/数据库) ──ETL──> 直接生成三元组导入Neo4j ==================== 在线推理阶段(用户问答实时链路)==================== 用户自然语言 Question │ ├────────────────────────────────┐ ▼ ▼ 【分支A:图谱检索链路】 【分支B:向量文本检索链路】 问题实体抽取 Question向量化 │ │ ▼ ▼ LLM 将自然语言 → Cypher语句 向量库TopK相似文本片段检索 │ ▼ Neo4j执行Cypher查询,获取结构化知识(实体+关系路径) │ └──────────────────┬──────────────────┘ ▼ 检索结果融合模块 (图谱三元组事实 + 原始文本上下文) │ ▼ 组装Prompt送入大模型LLM │ ▼ 输出最终答案模块说明
- 实体链接:解决别名问题(如 “华为技术有限公司”= 华为),防止图谱重复实体
- 结果融合策略:
- 图谱数据提供确定关系、多跳链路事实
- 向量文本提供细节描述、佐证原文
- 容错机制:Cypher 生成失败时,自动降级为纯向量 RAG
- 本地启动 Neo4j,创建数据库,修改连接地址、账号密码
- 准备 OpenAI 兼容接口(可替换通义千问 / DeepSeek 等)
- 预先导入图谱数据,或使用代码内置样例三元组
- Schema 动态注入不要写死 Schema!程序运行时自动调用
CALL db.schema.visualization()获取节点、关系,动态填充 Prompt,图谱更新不用改提示词。 - 防注入安全规则限制只能 MATCH;禁止所有写入语句;增加关键词黑名单(DELETE, MERGE, CREATE)
- 失败降级策略LLM 输出 NO_QUERY / Cypher 执行报错 → 直接跳过图谱检索,仅使用向量 RAG
- 实体前置预处理先用 NER 提取问题实体,做实体链接归一化,再送入 NL2Cypher,大幅降低生成错误
- 增加 Cypher 语法校验模块(执行前先校验语句合法性)
- 示例少样本 Prompt (Few-shot),加入多条「问题→Cypher」样例,提升复杂问题准确率
- 构建修正链:Cypher 执行报错 → 将报错信息丢回 LLM 自动修正查询语句
- 混合检索 Hybrid Retrieval:向量检索 + 图谱检索并行融合
新增交付内容
- 文档文本自动抽取三元组 + 批量导入 Neo4j 完整代码
- GraphRAG 生产级融合回答 Prompt(图谱结构化数据 + 向量原文融合)
前置依赖
bash
pip install langchain langchain-openai neo4j python-dotenv一、文本自动抽取三元组 + 写入 Neo4j 完整代码
文件:triple_extractor.py
核心能力:
- 输入段落文本,LLM 抽取标准化三元组
(head, relation, tail) - 实体简单归一化、去重
- 自动转换成 Cypher MERGE 语句,避免重复创建实体
- 支持批量文本循环导入
- 可适配任意 OpenAI 兼容大模型(DeepSeek、通义千问、智谱等)
python
运行
from dotenv import load_dotenv import os import json from langchain_openai import ChatOpenAI from langchain_neo4j import Neo4jGraph load_dotenv() # ========== 配置区 ========== NEO4J_URI = "bolt://localhost:7687" NEO4J_USER = "neo4j" NEO4J_PASSWORD = "你的Neo4j密码" LLM_API_KEY = os.getenv("LLM_API_KEY") LLM_BASE_URL = "https://api.openai.com/v1" LLM_MODEL = "gpt-3.5-turbo" # 初始化 graph = Neo4jGraph(url=NEO4J_URI, username=NEO4J_USER, password=NEO4J_PASSWORD) llm = ChatOpenAI( model=LLM_MODEL, api_key=LLM_API_KEY, base_url=LLM_BASE_URL, temperature=0 ) # ========== 三元组抽取Prompt ========== TRIPLET_EXTRACT_PROMPT = """ 任务:从给定文本中抽取知识三元组。 三元组格式:[{"head":"头实体","relation":"关系","tail":"尾实体"}] 规则: 1. 输出严格JSON数组,不要任何额外文字、注释、markdown; 2. 实体尽量精简,统一称谓,避免别名泛滥; 3. 关系动词化,简短明确,不要过长句子; 4. 不要抽取模糊、猜测、不确定的信息; 5. 一条事实生成一条三元组,一条文本可以输出多条; 6. 没有有效事实返回空数组[]。 待抽取文本: {text} JSON输出: """ def extract_triples(text: str): """文本抽取三元组,返回列表""" prompt = TRIPLET_EXTRACT_PROMPT.format(text=text) resp = llm.invoke(prompt) content = resp.content.strip() try: triples = json.loads(content) return triples except Exception as e: print(f"三元组解析失败:{e}, raw:{content}") return [] def save_triples_to_neo4j(triples): """批量将三元组写入Neo4j,使用MERGE避免重复实体与关系""" if not triples: return # 批量执行Cypher cypher = """ UNWIND $triples AS item MERGE (h:Entity{name: item.head}) MERGE (t:Entity{name: item.tail}) MERGE (h)-[r:REL{name: item.relation}]->(t) """ graph.query(cypher, {"triples": triples}) print(f"成功导入 {len(triples)} 条三元组") def process_document_text(text: str): """入口函数:输入文本 → 抽取 → 入库""" triples = extract_triples(text) save_triples_to_neo4j(triples) return triples if __name__ == "__main__": # 测试样例文本,可以替换成你的文档段落 doc_text = """ 华为正式发布鸿蒙操作系统,鸿蒙操作系统搭载方舟编译器。 方舟编译器能够提升智能终端应用运行效率。 """ res_triples = process_document_text(doc_text) print("抽取三元组结果:") for t in res_triples: print(t)工程优化说明(生产改造点)
- 实体分层标签当前全部节点统一标签
Entity;业务场景可优化:自动识别实体类型(公司、产品、人物),创建不同 LabelCompany,Product,Person - 实体链接消歧增强增加同义词字典,例如 {"华为技术有限公司":"华为"},抽取后统一标准化实体名称
- 长文档处理超长文本先做文本切分,分段抽取,防止 LLM 上下文溢出
- 数据校验新增入库前过滤无效三元组(head/tail 为空)
- 增量更新增加来源文档标识,节点新增属性
source_text,source_file,方便溯源
拓展:与之前 GraphRAG Demo 联动
读取文档 → 文本切片 切片一路:向量化存入向量库 切片二路:调用process_document_text(chunk_text)抽取三元组进图谱
二、GraphRAG 生产级融合回答 Prompt
场景: 两路检索完成【图谱结构化结果】+【向量检索原文片段】送入 LLM 生成最终答案
分为两套: Prompt 1:标准版(通用知识库、企业内部问答) Prompt 2:严谨风控版(政务、法律、金融、溯源场景,强防幻觉)
Prompt 1:通用生产融合 Prompt(推荐绝大多数场景)
plaintext
# 角色 你是专业知识问答助手,结合【知识图谱结构化事实】和【原始参考文档】回答用户问题。 ## 可用参考资料 【知识图谱结构化事实(实体与关系链路)】 {graph_context} 【原始文档片段(文本细节补充)】 {vector_context} ## 硬性规则 1. 优先采信知识图谱中明确的实体关系;文档内容作为细节补充; 2. 禁止编造不存在的实体、关系、数据;参考资料不存在的信息,不要猜测,直接说明“暂无相关信息”; 3. 如果图谱信息和文档信息出现冲突,优先保留原始文档内容,并在答案中注明存在信息冲突; 4. 回答逻辑清晰,涉及多主体关联关系时,梳理清楚链路; 5. 不要输出无关内容,不要复述全部参考资料,精炼作答; 6. 不要输出markdown代码块,正常自然语言回答。 用户问题:{question} 你的回答:Prompt 2:严谨风控版(高可信场景:环境溯源、法律、金融、政企)
plaintext
# 任务说明 基于提供的结构化图谱数据与原始文档素材回答问题,严格防范AI幻觉,所有结论必须有据可查。 ## 参考素材 【知识图谱查询结果】 {graph_context} 【原始检索文档上下文】 {vector_context} ## 强制约束 1. 所有结论必须能够在上方素材中找到依据;任何无法证实的推论禁止写入答案; 2. 区分两类信息: - 确定事实:图谱/原文明确记载内容 - 信息缺失:素材中无相关内容,如实告知,禁止主观推断 3. 当图谱仅提供关系链路、缺少细节描述时,可以引用原始文档补充细节; 4. 若多处资料信息矛盾,需要清晰标注冲突点,不要自行选择采信某一方; 5. 如果用户问题需要多跳关联,请清晰展示推理链路; 6. 禁止拓展回答素材以外的延伸知识。 用户提问:{question} 请给出严谨回答:代码中如何嵌入使用示例
python
运行
# 填充模板 final_prompt_template = """ # 角色 你是专业知识问答助手,结合【知识图谱结构化事实】和【原始参考文档】回答用户问题。 ## 可用参考资料 【知识图谱结构化事实(实体与关系链路)】 {graph_context} 【原始文档片段(文本细节补充)】 {vector_context} ## 硬性规则 1. 优先采信知识图谱中明确的实体关系;文档内容作为细节补充; 2. 禁止编造不存在的实体、关系、数据;参考资料不存在的信息,不要猜测,直接说明“暂无相关信息”; 3. 如果图谱信息和文档信息出现冲突,优先保留原始文档内容,并在答案中注明存在信息冲突; 4. 回答逻辑清晰,涉及多主体关联关系时,梳理清楚链路; 5. 不要输出无关内容,不要复述全部参考资料,精炼作答; 6. 不要输出markdown代码块,正常自然语言回答。 用户问题:{question} 你的回答: """ prompt_fill = final_prompt_template.format( graph_context=graph_context, vector_context=vector_context, question=user_query ) final_answer = llm.invoke(prompt_fill).content