知识图谱与大模型融合: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 # Neo4j / RDF graph
self.llm = llm

def query(self, question: str) -> str:
# Step 1: 从问题中提取实体
entities = self.extract_entities(question)
# ["马化腾", "腾讯"]

# Step 2: 在知识图谱中查询实体间的关系路径
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)

# Step 3: 向量检索获取相关文档
docs = self.vector_store.similarity_search(question, k=5)
doc_context = "\n".join([doc.content for doc in docs])

# Step 4: 结合图知识和文档生成回答
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"""

# Step 1: 识别问题中的核心实体
entities = extract_entities(question)
# ["腾讯"]

# Step 2: 在知识图谱中提取以核心实体为中心的局部子图
# 扩展范围:2跳内的所有实体和关系
subgraph = kg_graph.extract_subgraph(
center_entity="腾讯",
depth=2,
relation_types=["产品", "创始人", "竞争对手", "合作伙伴"]
)
# 返回:
# 腾讯
# ├── 产品: 微信, QQ, 王者荣耀
# ├── 创始人: 马化腾
# ├── 竞争对手: 阿里巴巴, 字节跳动
# └── 合作伙伴: 京东

# Step 3: 向量检索相关文档
docs = vector_store.similarity_search(question, k=3)

# Step 4: 生成
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)
# ["马化腾", "微信"]

# Step 1: 在知识图谱中查找实体间的所有路径
all_paths = kg_graph.find_all_paths(
from_entity="马化腾",
to_entity="微信",
max_hops=4
)

# 返回路径:
# 路径1: 马化腾 --[创办]--> 腾讯 --[开发]--> 微信
# 路径2: 马化腾 --[是]--> 腾讯创始人 --[负责]--> 微信负责人
# 路径3: 马化腾 --[是]--> 腾讯创始人 --[收购]--> 微信

# Step 2: 过滤和排序路径
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)

# Step 3: 用最相关的路径生成回答
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, KnowledgeGraphIndex
from llama_index.graph_stores.neo4j import Neo4jGraphStore
from llama_index.llms import OpenAI
from llama_index import ServiceContext

# 1. 配置连接
graph_store = Neo4jGraphStore(
username="neo4j",
password="password",
url="bolt://localhost:7687"
)

# 2. 配置 LLM
llm = OpenAI(model="gpt-4")
service_context = ServiceContext.from_defaults(llm=llm)

# 3. 加载文档
documents = SimpleDirectoryReader("./data").load_data()

# 4. 构建知识图谱索引
# LlamaIndex 会自动从文档中抽取实体和关系,构建知识图谱
kg_index = KnowledgeGraphIndex.from_documents(
documents,
kg_graph_store=graph_store,
service_context=service_context,
max_triplets_per_chunk=10, # 每个 chunk 最多抽取 10 个三元组
space_name="knowledge_graph", # Neo4j 图空间名称
)

4.3 构建向量索引

1
2
3
4
from llama_index import VectorStoreIndex

# 文档向量索引(作为知识图谱的补充)
vector_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, RetrieverQueryEngine
from llama_index.querying import KGTableRetriever, VectorIndexRetriever

# 1. 知识图谱检索器
kg_retriever = KGTableRetriever(
kg_index,
retriever_top_n=5, # 检索 5 个相关三元组
)

# 2. 向量检索器
vector_retriever = VectorIndexRetriever(
vector_index,
similarity_top_k=5,
)

# 3. 融合检索器(CombineResultsSynthesizer)
from llama_index.query_engine import CombineResultsQueryEngine

query_engine = CombineResultsQueryEngine(
vector_retriever=vector_retriever,
kg_retriever=kg_retriever,
service_context=service_context,
)

# 4. 查询
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 QueryBundle
from 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:
# Step 1: 实体识别
entities = self.extract_entities(question)

# Step 2: 知识图谱查询
kg_context = self.query_kg(entities, question)

# Step 3: 向量检索
vector_context = self.query_vector(question)

# Step 4: 判断是否需要图知识
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)
# [
# {"subject": "马化腾", "relation": "创办", "object": "腾讯"},
# {"subject": "腾讯", "relation": "创办时间", "object": "1998年"},
# {"subject": "马化腾", "relation": "合作伙伴", "object": "张志东"},
# {"subject": "腾讯", "relation": "产品", "object": "微信"},
# {"subject": "腾讯", "relation": "产品", "object": "QQ"},
# {"subject": "微信", "relation": "开发者", "object": "张小龙"},
# ]

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 = []

# Step 1: 从每个文档抽取三元组
for doc in documents:
triplets = extract_triplets_with_llm(doc, llm)
all_triplets.extend(triplets)
print(f"从文档中抽取了 {len(triplets)} 个三元组")

# Step 2: 去重(同一关系只保留一次)
unique_triplets = deduplicate_triplets(all_triplets)
print(f"去重后共 {len(unique_triplets)} 个三元组")

# Step 3: 导入 Neo4j
for triplet in unique_triplets:
graph_store upsert_triplet(
subject=triplet["subject"],
predicate=triplet["relation"],
obj=triplet["object"]
)

# Step 4: 建立索引
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:
"""评估检索质量"""

# 1. 知识图谱召回率:正确答案的实体是否被召回
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))

# 2. 关系路径完整性
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:
"""评估生成质量"""

# 使用 LLM Judge 评估
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
- 找到最适合的场景再推广

总结

  1. GraphRAG 解决向量 RAG 的两大局限:关系推理和全局理解
  2. 三种实现模式:KG增强RAG、子图检索RAG、路径增强RAG
  3. 实现工具:LlamaIndex 提供了开箱即用的 GraphRAG 支持
  4. 核心挑战:如何从非结构化文本自动构建高质量知识图谱
  5. 最佳拍档:GraphRAG + 向量 RAG 双引擎,互补提升

相关文章: