知识图谱与大模型融合:GraphRAG 实战
“向量检索告诉你’答案在哪里’,知识图谱告诉你’答案为什么是这样’。”
一、RAG 的局限性 在《RAG 实战指南》和《Advanced RAG 实战指南》中,我们介绍了基于向量检索的 RAG 系统。但向量 RAG 有两个根本局限:
1.1 缺乏关系推理能力 1 2 3 4 5 6 7 8 9 10 用户问题:"马化腾和张小龙是什么关系?" 向量 RAG 检索: - 检索"马化腾"的相关文档 → 找到"马化腾创办腾讯" - 检索"张小龙"的相关文档 → 找到"张小龙开发微信" 但向量 RAG 不知道: - 马化腾是腾讯创始人 - 张小龙是腾讯高管,负责微信 - 因此马化腾和张小龙是上下级关系(同事)
向量 RAG 只能找到”相关文档”,无法理解实体之间的关系链路 。
1.2 缺乏全局理解能力 1 2 3 4 5 6 7 8 9 10 11 用户问题:"腾讯的主要业务有哪些?" 向量 RAG:找到 N 个相关文档片段 但无法告诉你这些片段之间的层次关系 知识图谱: 腾讯 ├── 社交业务 → QQ, 微信, QQ空间 ├── 游戏业务 → 王者荣耀, 英雄联盟 ├── 金融科技 → 微信支付, QQ钱包 └── 云服务 → 腾讯云
知识图谱提供了结构化的全局视图 ,这是向量检索无法提供的。
二、GraphRAG 的核心思想 2.1 什么是 GraphRAG? GraphRAG = Knowledge Graph + Vector RAG,将知识图谱的结构化关系 与向量检索的语义匹配 结合。
1 2 3 4 5 6 7 传统向量 RAG: Query → Vector Search → Retrieve Chunks → LLM Generate GraphRAG: Query → Knowledge Graph Query → 获取关系路径 → Vector Search → 获取相关文档 → LLM Generate (基于图结构 + 文档)
2.2 GraphRAG 的优势
维度
向量 RAG
GraphRAG
关系推理
❌ 无法理解实体关系
✅ 支持多跳关系查询
全局理解
❌ 只能返回局部片段
✅ 提供全局结构视图
可解释性
一般
高(答案有明确的关系路径)
幻觉减少
一般
显著(关系事实更可靠)
查询复杂度
简单
中等(需要构建和维护知识图谱)
三、GraphRAG 的实现模式 3.1 模式一:知识图谱增强的 RAG(KG-Enhanced RAG) 最简单的方式:在 RAG 流程中先用知识图谱找到关系路径 ,再结合向量检索结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 class KGEnhancedRAG : def __init__ (self, vector_store, kg_graph, llm ): self .vector_store = vector_store self .kg = kg_graph self .llm = llm def query (self, question: str ) -> str : entities = self .extract_entities(question) kg_context = "" for e1 in entities: for e2 in entities: if e1 != e2: paths = self .kg.find_shortest_path(e1, e2) kg_context += self .format_paths(paths) docs = self .vector_store.similarity_search(question, k=5 ) doc_context = "\n" .join([doc.content for doc in docs]) prompt = f""" 基于以下信息回答问题。 知识图谱中的关系信息: {kg_context} 相关文档: {doc_context} 问题:{question} 回答: """ return self .llm.invoke(prompt) def extract_entities (self, text: str ) -> list : """用 LLM 提取问题中的实体""" prompt = f""" 从以下问题中提取所有实体(人物、公司、地点等)。 只输出实体名称,用逗号分隔。 问题:{text} """ response = self .llm.invoke(prompt) return [e.strip() for e in response.split("," )] def format_paths (self, paths: list ) -> str : """格式化关系路径为文本""" formatted = [] for path in paths: path_str = " → " .join(path) formatted.append(f"- {path_str} " ) return "\n" .join(formatted)
3.2 模式二:子图检索 RAG(Subgraph RAG) 将知识图谱的局部子图 作为上下文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 def subgraph_rag (question: str , vector_store, kg_graph, llm ): """子图检索 RAG""" entities = extract_entities(question) subgraph = kg_graph.extract_subgraph( center_entity="腾讯" , depth=2 , relation_types=["产品" , "创始人" , "竞争对手" , "合作伙伴" ] ) docs = vector_store.similarity_search(question, k=3 ) prompt = f""" 基于以下知识图谱子图和文档回答问题。 知识图谱子图: {subgraph} 相关文档: {' ' .join([d.content for d in docs])} 问题:{question} 请基于上述信息给出准确、完整的回答。 """ return llm.invoke(prompt)
3.3 模式三:路径增强 RAG(Path-Augmented RAG) 专注于关系路径 的检索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 def path_augmented_rag (question: str , kg_graph, llm ): """路径增强 RAG:找出问题中实体间的关系路径""" entities = extract_entities(question) all_paths = kg_graph.find_all_paths( from_entity="马化腾" , to_entity="微信" , max_hops=4 ) valid_paths = [] for path in all_paths: if is_valid_path(path): valid_paths.append((path, score_path_relevance(path, question))) valid_paths.sort(key=lambda x: x[1 ], reverse=True ) best_paths = [p for p, s in valid_paths[:3 ]] path_context = format_paths(best_paths) prompt = f""" 基于以下关系路径回答问题。 关系路径: {path_context} 问题:{question} 请根据以上路径关系给出准确回答。如果路径不足以回答问题,请说明。 """ return llm.invoke(prompt)
四、实战:使用 LlamaIndex 构建 GraphRAG 4.1 安装依赖 1 pip install llama-index llama-index-graph-stores-neo4j neo4j
4.2 构建知识图谱索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from llama_index import SimpleDirectoryReader, KnowledgeGraphIndexfrom llama_index.graph_stores.neo4j import Neo4jGraphStorefrom llama_index.llms import OpenAIfrom llama_index import ServiceContextgraph_store = Neo4jGraphStore( username="neo4j" , password="password" , url="bolt://localhost:7687" ) llm = OpenAI(model="gpt-4" ) service_context = ServiceContext.from_defaults(llm=llm) documents = SimpleDirectoryReader("./data" ).load_data() kg_index = KnowledgeGraphIndex.from_documents( documents, kg_graph_store=graph_store, service_context=service_context, max_triplets_per_chunk=10 , space_name="knowledge_graph" , )
4.3 构建向量索引 1 2 3 4 from llama_index import VectorStoreIndexvector_index = VectorStoreIndex.from_documents(documents)
4.4 GraphRAG 查询引擎 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from llama_index.query_engine import KnowledgeGraphQueryEngine, RetrieverQueryEnginefrom llama_index.querying import KGTableRetriever, VectorIndexRetrieverkg_retriever = KGTableRetriever( kg_index, retriever_top_n=5 , ) vector_retriever = VectorIndexRetriever( vector_index, similarity_top_k=5 , ) from llama_index.query_engine import CombineResultsQueryEnginequery_engine = CombineResultsQueryEngine( vector_retriever=vector_retriever, kg_retriever=kg_retriever, service_context=service_context, ) response = query_engine.query( "马化腾和张小龙是什么关系?微信是怎么发展起来的?" ) print (response)
4.5 自定义 GraphRAG 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 from llama_index import QueryBundlefrom typing import List , Tuple class CustomGraphRAG : def __init__ (self, kg_index, vector_index, llm ): self .kg_index = kg_index self .vector_index = vector_index self .llm = llm def query (self, question: str ) -> str : entities = self .extract_entities(question) kg_context = self .query_kg(entities, question) vector_context = self .query_vector(question) needs_graph = self .should_use_graph(question) if needs_graph and kg_context: prompt = self .build_prompt(question, kg_context, vector_context) else : prompt = self .build_prompt(question, "" , vector_context) return self .llm.invoke(prompt) def extract_entities (self, text: str ) -> list : """LLM 提取实体""" prompt = f""" 从问题中提取关键实体(人物、公司、产品、地点等),返回 JSON 数组格式。 问题:{text} 示例输出:["马化腾", "腾讯", "微信"] """ response = self .llm.invoke(prompt) import json try : return json.loads(response) except : return [] def query_kg (self, entities: list , question: str ) -> str : """查询知识图谱""" if not entities: return "" triplets = [] for entity in entities: result = self .kg_index.query(f"关于{entity} 的所有关系" ) triplets.extend(result) unique_triplets = self .deduplicate_triplets(triplets) return self .format_triplets(unique_triplets) def query_vector (self, question: str , top_k: int = 3 ) -> str : """向量检索""" retriever = self .vector_index.as_retriever(similarity_top_k=top_k) nodes = retriever.retrieve(question) return "\n" .join([node.text for node in nodes]) def should_use_graph (self, question: str ) -> bool : """判断是否需要图知识""" graph_keywords = ["关系" , "是什么" , "如何" , "发展" , "合作" , "竞争" ] return any (kw in question for kw in graph_keywords) def build_prompt (self, question: str , kg_context: str , vector_context: str ) -> str : """构建最终 prompt""" parts = [] if kg_context: parts.append(f"【知识图谱关系】\n{kg_context} " ) if vector_context: parts.append(f"【相关文档】\n{vector_context} " ) return f""" 基于以下信息回答问题。如果知识图谱和文档信息冲突,以知识图谱为准。 {';' .join(parts)} 问题:{question} 回答: """
五、知识图谱的自动构建 GraphRAG 的一个核心挑战是如何从非结构化文本自动构建知识图谱 。
5.1 使用 LLM 自动抽取三元组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 def extract_triplets_with_llm (text: str , llm ) -> list : """用 LLM 从文本中抽取三元组""" prompt = f""" 从以下文本中抽取所有知识三元组(实体-关系-实体)。 要求: 1. 只抽取明确陈述的关系,不推测 2. 关系类型使用简洁词语:如"创办"、"任职"、"开发"、"总部位于" 3. 如果同一实体有多个名称,统一为最常用名称 4. 输出 JSON 数组格式 文本: {text} 输出格式: [ {{"subject": "实体1", "relation": "关系", "object": "实体2"}}, ... ] 如果文本中没有明确的三元组关系,返回空数组 []。 """ response = llm.invoke(prompt) try : import json triplets = json.loads(response) return triplets except : return [] text = """ 马化腾是腾讯公司的创始人。1998年,他与张志东等人共同创办了腾讯。 腾讯的主要产品包括微信和QQ。微信由张小龙团队开发。 """ triplets = extract_triplets_with_llm(text, gpt4)
5.2 批量构建知识图谱 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 def build_knowledge_graph_from_documents ( documents: list [str ], llm, graph_store: Neo4jGraphStore, batch_size: int = 10 ): """从文档批量构建知识图谱""" all_triplets = [] for doc in documents: triplets = extract_triplets_with_llm(doc, llm) all_triplets.extend(triplets) print (f"从文档中抽取了 {len (triplets)} 个三元组" ) unique_triplets = deduplicate_triplets(all_triplets) print (f"去重后共 {len (unique_triplets)} 个三元组" ) for triplet in unique_triplets: graph_store upsert_triplet( subject=triplet["subject" ], predicate=triplet["relation" ], obj=triplet["object" ] ) graph_store.create_indexes() print (f"知识图谱构建完成!共 {len (unique_triplets)} 个三元组" ) return graph_store
六、GraphRAG 的评估 6.1 评估指标 GraphRAG 的评估需要同时考虑知识图谱质量 和问答质量 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class GraphRAGEvaluator : def __init__ (self, rag_system, kg_graph ): self .rag = rag_system self .kg = kg_graph def evaluate_retrieval (self, question: str , ground_truth: dict ) -> dict : """评估检索质量""" entities_in_answer = self .kg.extract_entities(ground_truth["answer" ]) retrieved_context = self .rag.retrieve_context(question) entities_in_context = self .kg.extract_entities(retrieved_context) kg_recall = len (set (entities_in_answer) & set (entities_in_context)) / len (set (entities_in_answer)) path_recall = self .evaluate_path_completeness( question, ground_truth["expected_paths" ] ) return { "kg_recall" : kg_recall, "path_recall" : path_recall } def evaluate_answer (self, question: str , generated_answer: str , ground_truth: str ) -> dict : """评估生成质量""" evaluation = llm_judge_evaluate( question=question, answer=generated_answer, ground_truth=ground_truth ) claims = extract_claims(generated_answer) hallucination_rate = self .check_hallucination(claims) return { "llm_judge_score" : evaluation["overall" ], "hallucination_rate" : hallucination_rate, "faithfulness" : 1 - hallucination_rate }
6.2 常见问题
问题
原因
解决方案
图谱太稀疏
抽取的三元组太少
增加抽取 prompt 的详细度,用 LLM 多次抽取
实体消歧失败
同名实体混淆
引入实体链接,结合上下文消歧
关系不准确
抽取模型幻觉
人工抽检,加入置信度过滤
查询效率低
图太大
分层索引,只检索相关子图
七、GraphRAG vs 传统 RAG
维度
向量 RAG
GraphRAG
数据基础
向量化的文档片段
结构化的知识图谱 + 文档
关系理解
弱
强(多跳推理)
全局理解
无
有(子图全局视图)
实现复杂度
低
中
维护成本
低
中(需要维护图谱)
适用场景
事实性问答、文档检索
关系推理、对比分析、趋势分析
查询类型
“是什么”
“是什么” + “为什么” + “如何关联”
八、GraphRAG 的最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1. 知识图谱 + 向量检索 = 互补双赢 - 知识图谱提供结构化的关系事实 - 向量检索提供语义相关的文档片段 2. 图谱不需要完美 - 即使有些噪音,只要核心关系准确就能提升效果 - 后续可以通过反馈循环不断优化 3. 选择合适的抽取方式 - 小规模:LLM 抽取(成本高,质量好) - 大规模:LLM + 规则混合(成本低,覆盖广) 4. 评估先行 - 先在几个典型问题上评估 GraphRAG vs 向量 RAG - 找到最适合的场景再推广
总结
GraphRAG 解决向量 RAG 的两大局限 :关系推理和全局理解
三种实现模式 :KG增强RAG、子图检索RAG、路径增强RAG
实现工具 :LlamaIndex 提供了开箱即用的 GraphRAG 支持
核心挑战 :如何从非结构化文本自动构建高质量知识图谱
最佳拍档 :GraphRAG + 向量 RAG 双引擎,互补提升
相关文章: