知识图谱与LLM融合:GraphRAG实战
原创
灵阙教研团队
S 精选 进阶 |
约 8 分钟阅读
更新于 2026-02-28 AI 导读
知识图谱与LLM融合:GraphRAG实战 GraphRAG将知识图谱的结构化推理能力与LLM的自然语言生成能力深度融合,解决了传统RAG在多跳推理、全局摘要和事实一致性上的不足。本文基于Microsoft GraphRAG和LlamaIndex的实现,提供从原理到代码的完整实战指南。 一、为什么需要GraphRAG 1.1 传统RAG的局限 传统向量RAG流程: 问题 → 向量化 →...
知识图谱与LLM融合:GraphRAG实战
GraphRAG将知识图谱的结构化推理能力与LLM的自然语言生成能力深度融合,解决了传统RAG在多跳推理、全局摘要和事实一致性上的不足。本文基于Microsoft GraphRAG和LlamaIndex的实现,提供从原理到代码的完整实战指南。
一、为什么需要GraphRAG
1.1 传统RAG的局限
传统向量RAG流程:
问题 → 向量化 → Top-K检索 → 拼接上下文 → LLM生成
局限:
├── 局部视角:只检索最相似的chunk,缺乏全局理解
├── 多跳推理困难:无法自然串联"A→B→C"的推理链
├── 上下文碎片化:检索到的chunk可能来自不相关的段落
├── 事实一致性差:LLM可能在不同chunk间产生矛盾
└── 全局摘要缺失:无法回答需要综合全文的问题
GraphRAG的解决思路:
├── 全局视角:知识图谱提供领域的结构化全景
├── 多跳推理:沿图谱路径自然串联信息
├── 上下文结构化:实体+关系提供有组织的上下文
├── 事实锚定:图谱三元组提供可验证的事实依据
└── 社区摘要:分层社区结构支持全局概括
1.2 GraphRAG核心架构
GraphRAG两阶段架构:
阶段1: 索引构建(离线)
文档语料
│
▼
┌─────────────────┐
│ LLM实体/关系抽取 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 知识图谱构建 │
│ (实体消歧+融合) │
└─────────────────┘
│
▼
┌─────────────────┐ ┌──────────────┐
│ 社区检测 │──→ │ 社区摘要生成 │
│ (Leiden算法) │ │ (LLM摘要) │
└─────────────────┘ └──────────────┘
│
▼
图谱 + 社区摘要 + 向量索引
阶段2: 查询(在线)
用户问题
│
├── 局部搜索(Local Search)
│ └── 实体识别 → 子图检索 → 上下文组装 → LLM回答
│
└── 全局搜索(Global Search)
└── 社区摘要检索 → Map-Reduce → LLM回答
二、Microsoft GraphRAG实现
2.1 环境搭建
# 安装GraphRAG
pip install graphrag
# 初始化项目
mkdir my-graphrag-project && cd my-graphrag-project
graphrag init --root .
# 项目结构
my-graphrag-project/
├── settings.yaml # 配置文件
├── .env # API密钥
├── input/ # 输入文档目录
│ └── *.txt # 待索引的文档
├── output/ # 输出目录(索引后生成)
│ ├── entities.parquet
│ ├── relationships.parquet
│ ├── communities.parquet
│ └── community_reports.parquet
└── prompts/ # 自定义提示词
├── entity_extraction.txt
└── community_report.txt
2.2 配置
# settings.yaml 关键配置
llm:
api_key: ${GRAPHRAG_API_KEY}
type: openai_chat
model: gpt-4o-mini # 索引构建用较便宜的模型
max_tokens: 4000
embeddings:
llm:
api_key: ${GRAPHRAG_API_KEY}
type: openai_embedding
model: text-embedding-3-small
chunks:
size: 1200
overlap: 100
entity_extraction:
max_gleanings: 1 # 多次抽取以提高召回
entity_types:
- organization
- person
- event
- technology
- location
community_reports:
max_length: 2000
claim_extraction:
enabled: true # 提取声明/事实
2.3 索引构建
# 执行索引构建
graphrag index --root .
# 索引过程:
# 1. 文档分块 (Chunking)
# 2. 实体和关系抽取 (Entity/Relationship Extraction)
# 3. 实体消歧和图构建 (Graph Construction)
# 4. 社区检测 (Community Detection - Leiden Algorithm)
# 5. 社区摘要生成 (Community Summarization)
# 6. 向量嵌入生成 (Embedding Generation)
2.4 查询
# 局部搜索(适合具体问题)
graphrag query --root . --method local \
--query "华为在AI芯片领域有哪些核心产品?"
# 全局搜索(适合宏观概括问题)
graphrag query --root . --method global \
--query "这些文档的主要主题和趋势是什么?"
三、LlamaIndex PropertyGraphIndex实现
3.1 基于LlamaIndex的GraphRAG
from llama_index.core import (
SimpleDirectoryReader,
PropertyGraphIndex,
Settings,
)
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.graph_stores.neo4j import Neo4jPropertyGraphStore
# 配置
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
# 连接Neo4j图存储
graph_store = Neo4jPropertyGraphStore(
username="neo4j",
password="password",
url="bolt://localhost:7687",
database="neo4j"
)
# 加载文档
documents = SimpleDirectoryReader("./data").load_data()
# 构建属性图索引
index = PropertyGraphIndex.from_documents(
documents,
property_graph_store=graph_store,
show_progress=True,
)
# 查询
query_engine = index.as_query_engine(
include_text=True,
response_mode="tree_summarize",
similarity_top_k=5,
)
response = query_engine.query(
"知识图谱在企业中的主要应用场景有哪些?"
)
print(response)
3.2 自定义实体抽取
from llama_index.core.indices.property_graph import (
SchemaLLMPathExtractor,
DynamicLLMPathExtractor,
)
# 方案A: Schema约束的抽取(更精确)
schema_extractor = SchemaLLMPathExtractor(
llm=Settings.llm,
possible_entities=[
"Person", "Company", "Product", "Technology",
"Location", "Event", "Policy"
],
possible_relations=[
"WORKS_AT", "FOUNDED", "PRODUCES", "USES",
"LOCATED_IN", "PARTICIPATED_IN", "GOVERNS"
],
strict=False, # 允许schema外的实体/关系
)
# 方案B: 动态抽取(更灵活)
dynamic_extractor = DynamicLLMPathExtractor(
llm=Settings.llm,
max_triplets_per_chunk=20,
num_workers=4,
)
# 使用自定义抽取器构建索引
index = PropertyGraphIndex.from_documents(
documents,
property_graph_store=graph_store,
kg_extractors=[schema_extractor],
show_progress=True,
)
3.3 混合检索
from llama_index.core.indices.property_graph import (
LLMSynonymRetriever,
VectorContextRetriever,
)
# 检索器1: LLM同义词扩展检索
synonym_retriever = LLMSynonymRetriever(
index.property_graph_store,
llm=Settings.llm,
include_text=True,
synonym_prompt=(
"给定以下查询,生成相关的同义词和相关术语列表,"
"用于在知识图谱中搜索相关实体。\n"
"查询: {query_str}\n"
"同义词: "
),
)
# 检索器2: 向量相似度检索
vector_retriever = VectorContextRetriever(
index.property_graph_store,
embed_model=Settings.embed_model,
include_text=True,
similarity_top_k=5,
)
# 组合检索器
query_engine = index.as_query_engine(
sub_retrievers=[synonym_retriever, vector_retriever],
response_mode="tree_summarize",
)
四、自定义GraphRAG实现
4.1 实体抽取模块
def extract_entities_and_relations(text, llm_client, schema=None):
"""从文本中抽取实体和关系"""
schema_hint = ""
if schema:
schema_hint = f"""
可用的实体类型: {', '.join(schema.get('entity_types', []))}
可用的关系类型: {', '.join(schema.get('relation_types', []))}
"""
prompt = f"""从以下文本中抽取所有实体和关系。
{schema_hint}
文本:
{text}
输出JSON格式:
{{
"entities": [
{{"id": "entity_唯一标识", "name": "实体名称", "type": "实体类型",
"description": "一句话描述"}}
],
"relations": [
{{"source": "源实体id", "target": "目标实体id", "type": "关系类型",
"description": "关系描述", "weight": 0.9}}
]
}}
要求:
1. 实体去重(相同实体只出现一次)
2. 关系必须有明确的文本依据
3. weight表示关系的置信度(0-1)
4. description用一句话描述实体或关系的含义"""
response = llm_client.generate(prompt, temperature=0)
return json.loads(response)
4.2 社区检测与摘要
import networkx as nx
from cdlib import algorithms
def detect_communities(graph_data):
"""使用Leiden算法检测社区"""
G = nx.Graph()
for entity in graph_data["entities"]:
G.add_node(entity["id"], **entity)
for rel in graph_data["relations"]:
G.add_edge(
rel["source"], rel["target"],
weight=rel.get("weight", 1.0),
type=rel["type"]
)
# Leiden社区检测
communities = algorithms.leiden(G)
return communities
def generate_community_summaries(communities, graph_data, llm_client):
"""为每个社区生成摘要报告"""
summaries = []
for i, community in enumerate(communities.communities):
# 收集社区内的实体和关系
community_entities = [
e for e in graph_data["entities"]
if e["id"] in community
]
community_relations = [
r for r in graph_data["relations"]
if r["source"] in community and r["target"] in community
]
# 用LLM生成摘要
context = format_community_data(community_entities, community_relations)
prompt = f"""基于以下知识图谱社区的实体和关系,生成一份结构化摘要报告。
社区数据:
{context}
报告格式:
1. 主题概述(2-3句话)
2. 核心实体列表
3. 关键关系和发现
4. 重要性评估(高/中/低)"""
summary = llm_client.generate(prompt)
summaries.append({
"community_id": i,
"members": list(community),
"summary": summary,
"size": len(community)
})
return summaries
4.3 查询引擎
class GraphRAGQueryEngine:
def __init__(self, graph_store, community_summaries, llm_client, embed_model):
self.graph_store = graph_store
self.summaries = community_summaries
self.llm = llm_client
self.embed = embed_model
def local_search(self, query, top_k=5):
"""局部搜索: 从相关实体出发遍历子图"""
# 1. 识别查询中的实体
entities = self._extract_query_entities(query)
# 2. 在图谱中检索相关子图
subgraph = self._retrieve_subgraph(entities, max_hops=2)
# 3. 同时检索相关文本块
text_chunks = self._vector_search(query, top_k=top_k)
# 4. 组装上下文
context = self._assemble_context(subgraph, text_chunks)
# 5. LLM生成回答
answer = self.llm.generate(f"""
基于以下知识图谱和文本信息回答问题。
知识图谱上下文:
{context['graph']}
相关文本:
{context['text']}
问题: {query}
要求:基于提供的信息回答,标注信息来源(图谱/文本),不确定时说明。
""")
return answer
def global_search(self, query):
"""全局搜索: Map-Reduce社区摘要"""
# Map阶段: 每个社区独立回答
community_answers = []
for summary in self.summaries:
partial = self.llm.generate(f"""
基于以下社区知识摘要,回答问题(如不相关回答N/A)。
社区摘要:
{summary['summary']}
问题: {query}
""")
if "N/A" not in partial:
community_answers.append(partial)
# Reduce阶段: 汇总所有社区答案
if not community_answers:
return "无法从现有知识中找到相关信息。"
final = self.llm.generate(f"""
以下是从不同知识社区中获得的部分答案,请综合它们生成最终回答。
部分答案:
{chr(10).join(f'[来源{i+1}] {a}' for i, a in enumerate(community_answers))}
问题: {query}
要求: 综合所有来源,给出完整、连贯的回答。
""")
return final
五、效果评估
5.1 对比基准
| 维度 | 向量RAG | GraphRAG | 提升 |
|---|---|---|---|
| 单跳事实问答 | 85% | 88% | +3% |
| 多跳推理问答 | 45% | 72% | +27% |
| 全局摘要 | 30% | 78% | +48% |
| 对比分析 | 50% | 75% | +25% |
| 事实一致性 | 70% | 85% | +15% |
| 延迟 | 1-2s | 2-5s | -50-150% |
| 成本 | 低 | 中-高(索引构建) | +200-500% |
5.2 适用场景判断
使用GraphRAG的场景:
├── 需要跨文档多跳推理
├── 需要全局主题概括
├── 实体关系是核心信息
├── 数据量中等(<10万文档)
└── 对准确性要求高于延迟
使用向量RAG的场景:
├── 简单的信息检索
├── 对延迟敏感(<1s)
├── 数据量极大(>100万文档)
├── 成本敏感
└── 信息相对独立(不需要关联推理)
混合方案(推荐):
├── 向量RAG作为基础层
├── GraphRAG作为增强层
├── 路由器根据查询类型分发
└── 简单问题走向量,复杂问题走图谱
六、总结
GraphRAG代表了RAG技术的重要演进方向。通过引入知识图谱的结构化表达和社区检测的层次化组织,它显著提升了LLM在多跳推理和全局理解上的能力。核心挑战在于索引构建的成本和延迟,但对于知识密集型场景,这些投入通常能够获得显著的质量回报。
Maurice | maurice_wen@proton.me