25 - Graph RAG 与知识图谱增强检索¶
⚠️ 时效性说明:本章涉及的知识图谱工具(Neo4j、LlamaIndex、Microsoft GraphRAG)版本可能随时间更新,请以官方文档为准。
学习目标:理解 Graph RAG 的核心原理,掌握知识图谱构建与检索方法,学会使用 Graph RAG 提升复杂问答质量。 前置知识:05-RAG系统构建、18-高级RAG技术
目录¶
1. Graph RAG 概述¶
1.1 为什么需要 Graph RAG¶
传统 RAG 基于向量相似度检索,存在以下局限:
| 问题 | 说明 |
|---|---|
| 语义鸿沟 | 向量相似度无法捕捉实体间的精确关系 |
| 多跳推理 | 需要跨越多个文档的综合推理能力不足 |
| 全局理解 | 对整个文档集的全局概览能力弱 |
| 实体消歧 | "苹果"是水果还是公司?向量检索难以区分 |
Graph RAG 通过引入知识图谱来解决这些问题:将文档中的实体和关系抽取为结构化的三元组(如 DeepSeek → 开发了 → DeepSeek-V3),构建图结构索引。检索时,先在图谱中定位相关实体,再沿边扩展到关联实体和关系,从而天然支持多跳推理(如"DeepSeek → 总部位于 → 杭州"需要两跳)。同时,知识图谱的实体消歧能力(通过上下文关系判断"苹果"是公司还是水果)也弥补了向量检索的不足。
1.2 Graph RAG 的核心思想¶
graph TD
A[原始文档] --> B[实体抽取]
B --> C[关系抽取]
C --> D[知识图谱]
E[用户查询] --> F[实体链接]
F --> D
D --> G[子图检索]
G --> H[图结构上下文]
H --> I[LLM 生成回答]
J[向量索引] --> K[向量检索]
K --> I
style D fill:#e1f5fe
style I fill:#c8e6c9 2. 知识图谱基础¶
2.1 知识图谱的核心概念¶
# 知识图谱由三元组 (Subject, Predicate, Object) 组成
triples = [
("DeepSeek", "开发了", "DeepSeek-V3"),
("DeepSeek", "总部位于", "杭州"),
("DeepSeek-V3", "参数量", "671B"),
("DeepSeek-V3", "架构", "MoE"),
("MoE", "全称", "Mixture of Experts"),
("DeepSeek-V3", "激活参数", "37B"),
("DeepSeek-V3", "训练数据", "14.8T tokens"),
]
# 知识图谱可以表示为有向图
# 节点 = 实体 (Entities)
# 边 = 关系 (Relations)
2.2 使用 NetworkX 构建知识图谱¶
import networkx as nx
def build_knowledge_graph(triples):
"""从三元组构建知识图谱"""
G = nx.DiGraph()
for subj, pred, obj in triples:
G.add_node(subj, type='entity')
G.add_node(obj, type='entity')
G.add_edge(subj, obj, relation=pred)
return G
# 构建图谱
kg = build_knowledge_graph(triples)
# 查询:DeepSeek 的所有关系
print("DeepSeek 的所有关系:")
for neighbor in kg.successors("DeepSeek"):
relation = kg.edges["DeepSeek", neighbor]["relation"]
print(f" DeepSeek --[{relation}]--> {neighbor}")
# 最短路径查询
path = nx.shortest_path(kg, "DeepSeek", "MoE")
print(f"\nDeepSeek 到 MoE 的路径: {' → '.join(path)}")
2.3 使用 Neo4j 图数据库¶
# Neo4j:生产级图数据库,适合百万级以上节点的大规模知识图谱
# 与 NetworkX(内存图库,适合小规模实验)不同,Neo4j 使用 Cypher 查询语言,
# 原生支持索引、持久化、事务和分布式,适合生产环境部署
# 安装:pip install neo4j
from neo4j import GraphDatabase
class KnowledgeGraphDB:
def __init__(self, uri="bolt://localhost:7687", user="neo4j", password="password"):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
def close(self):
self.driver.close()
def add_triple(self, subject, predicate, object_):
"""添加三元组"""
with self.driver.session() as session:
session.run(
"""
MERGE (s:Entity {name: $subject})
MERGE (o:Entity {name: $object})
MERGE (s)-[r:RELATION {type: $predicate}]->(o)
""",
subject=subject, predicate=predicate, object=object_
)
def query_neighbors(self, entity_name, max_depth=2):
"""查询实体的邻居"""
with self.driver.session() as session:
result = session.run(
"""
MATCH (e:Entity {name: $name})-[r*1..%d]-(neighbor)
RETURN neighbor.name AS name,
type(r[-1]) AS relation,
length(r) AS depth
""" % max_depth,
name=entity_name
)
return [record.data() for record in result]
def multi_hop_query(self, start_entity, end_entity, max_hops=3):
"""多跳查询"""
with self.driver.session() as session:
result = session.run(
"""
MATCH path = (s:Entity {name: $start})-[r*1..%d]-(e:Entity {name: $end})
RETURN [node in nodes(path) | node.name] AS entities,
[rel in relationships(path) | rel.type] AS relations
""" % max_hops,
start=start_entity, end=end_entity
)
return [record.data() for record in result]
# 使用
db = KnowledgeGraphDB()
for s, p, o in triples:
db.add_triple(s, p, o)
# 查询 DeepSeek 的邻居
neighbors = db.query_neighbors("DeepSeek")
3. Graph RAG 架构¶
3.1 Microsoft Graph RAG 架构¶
微软在 2024 年提出的 Graph RAG 是最知名的实现。其核心创新在于:先用 LLM 从文档中抽取实体和关系构建知识图谱,再通过社区检测(Leiden 算法)将图谱划分为多个语义社区,并为每个社区生成摘要。这种"图+社区摘要"的架构使得全局性查询(如"所有文档的核心主题是什么")不再需要遍历所有文本块,而是直接检索相关社区摘要,在效率和全局理解之间取得平衡。
graph TD
A[文档集合] --> B[文本分块]
B --> C[LLM 实体/关系抽取]
C --> D[知识图谱构建]
D --> E[社区检测<br/>Leiden 算法]
E --> F[社区摘要生成]
G[用户查询] --> H{查询类型}
H -->|局部查询| I[实体链接 + 子图检索]
H -->|全局查询| J[社区摘要检索]
I --> K[LLM 生成]
J --> K 3.2 Graph RAG 两种查询模式¶
class GraphRAG:
def __init__(self, llm, graph_db, vector_store):
self.llm = llm
self.graph_db = graph_db
self.vector_store = vector_store
def local_search(self, query: str, top_k: int = 5) -> str:
"""局部搜索:精确实体匹配 + 子图扩展
适合:具体事实型问题
例如:"DeepSeek-V3 的架构是什么?"
"""
# 1. 从查询中抽取实体
entities = self._extract_entities(query)
# 2. 在图谱中查找匹配实体
subgraph_context = []
for entity in entities:
neighbors = self.graph_db.query_neighbors(entity, max_depth=2)
subgraph_context.extend(neighbors)
# 3. 向量检索补充
vector_results = self.vector_store.search(query, top_k=top_k)
# 4. 组合上下文生成回答
context = self._format_context(subgraph_context, vector_results)
return self.llm.generate(query, context)
def global_search(self, query: str) -> str:
"""全局搜索:社区摘要 + Map-Reduce
适合:全局概览型问题
例如:"所有大模型的技术趋势是什么?"
"""
# 1. 检索相关社区摘要
community_summaries = self._search_communities(query)
# 2. Map: 每个社区独立回答
intermediate_answers = []
for summary in community_summaries:
answer = self.llm.generate(
query,
f"基于以下社区信息回答:\n{summary}"
)
intermediate_answers.append(answer)
# 3. Reduce: 合并所有中间回答
final_context = "\n\n".join(intermediate_answers)
return self.llm.generate(
query,
f"基于以下多个来源的信息,综合回答问题:\n{final_context}"
)
def _extract_entities(self, text: str) -> list:
"""使用 LLM 从文本中抽取实体"""
prompt = f"""从以下文本中提取关键实体名称,每行一个:
文本:{text}
实体列表:"""
response = self.llm.generate(prompt)
return [e.strip() for e in response.strip().split('\n') if e.strip()]
4. 实体抽取与关系构建¶
4.1 LLM 驱动的知识抽取¶
import json
def extract_triples_from_text(llm, text: str) -> list:
"""使用 LLM 从文本中抽取三元组"""
prompt = f"""请从以下文本中抽取知识三元组 (subject, predicate, object)。
要求:
1. 实体名称要规范统一
2. 关系要简洁明确
3. 以 JSON 数组格式返回
文本:
{text}
输出格式示例:
[
{{"subject": "实体1", "predicate": "关系", "object": "实体2"}},
...
]
三元组:"""
response = llm.generate(prompt)
try:
triples = json.loads(response)
return [(t["subject"], t["predicate"], t["object"]) for t in triples]
except json.JSONDecodeError:
return []
# 示例
sample_text = """
DeepSeek-V3 是深度求索公司于2024年12月发布的大语言模型。
该模型采用 MoE(混合专家)架构,总参数量671B,激活参数37B。
训练使用了14.8T tokens的数据,在多个基准测试上接近GPT-4水平。
"""
# triples = extract_triples_from_text(llm, sample_text)
# 预期输出:
# [
# ("DeepSeek-V3", "发布者", "深度求索"),
# ("DeepSeek-V3", "发布时间", "2024年12月"),
# ("DeepSeek-V3", "架构", "MoE"),
# ("DeepSeek-V3", "总参数量", "671B"),
# ("DeepSeek-V3", "激活参数", "37B"),
# ("DeepSeek-V3", "训练数据量", "14.8T tokens"),
# ("DeepSeek-V3", "性能水平", "接近GPT-4"),
# ]
4.2 实体消歧与对齐¶
class EntityResolver:
"""实体消歧与对齐"""
def __init__(self, llm):
self.llm = llm
self.entity_aliases = {} # 实体别名映射
def resolve(self, entity_name: str) -> str:
"""将实体名称规范化"""
# 检查已知别名
for canonical, aliases in self.entity_aliases.items():
if entity_name.lower() in [a.lower() for a in aliases]:
return canonical
# 使用 LLM 进行消歧
prompt = f"""以下实体名称是否指向同一个实体?
已知实体:{list(self.entity_aliases.keys())}
待消歧实体:{entity_name}
如果是已知实体的别名,返回规范名称。否则返回原始名称。"""
resolved = self.llm.generate(prompt).strip()
return resolved
def add_alias(self, canonical: str, alias: str):
"""添加实体别名"""
if canonical not in self.entity_aliases:
self.entity_aliases[canonical] = [canonical]
self.entity_aliases[canonical].append(alias)
# 使用
resolver = EntityResolver(llm)
resolver.add_alias("DeepSeek", "深度求索")
resolver.add_alias("DeepSeek", "深度求索公司")
resolver.add_alias("DeepSeek-V3", "DeepSeek V3")
print(resolver.resolve("深度求索")) # → "DeepSeek"
5. 图检索策略¶
5.1 子图检索¶
def retrieve_subgraph(graph_db, query_entities: list, max_depth: int = 2) -> str:
"""基于查询实体的子图检索"""
all_context = []
for entity in query_entities:
# 获取实体周围 N 跳的子图
neighbors = graph_db.query_neighbors(entity, max_depth=max_depth)
for record in neighbors:
context_line = f"- {entity} --[{record['relation']}]--> {record['name']}"
all_context.append(context_line)
return "\n".join(all_context)
# 示例
context = retrieve_subgraph(db, ["DeepSeek-V3"])
print(context)
# - DeepSeek-V3 --[发布者]--> 深度求索
# - DeepSeek-V3 --[架构]--> MoE
# - DeepSeek-V3 --[参数量]--> 671B
# - MoE --[全称]--> Mixture of Experts
5.2 混合检索(向量 + 图谱)¶
class HybridGraphRAG:
"""混合检索:向量检索 + 图谱检索"""
def __init__(self, vector_store, graph_db, llm):
self.vector_store = vector_store
self.graph_db = graph_db
self.llm = llm
def search(self, query: str, top_k: int = 5) -> dict:
"""混合检索"""
# 1. 向量检索(语义相似度)
vector_results = self.vector_store.search(query, top_k=top_k)
# 2. 图谱检索(结构化关系)
entities = self._extract_entities(query)
graph_context = []
for entity in entities:
neighbors = self.graph_db.query_neighbors(entity, max_depth=2)
graph_context.extend(neighbors)
# 3. 合并上下文
combined_context = self._merge_contexts(vector_results, graph_context)
# 4. 生成回答
answer = self.llm.generate(
query=query,
context=combined_context
)
return {
"answer": answer,
"vector_results": len(vector_results),
"graph_triples": len(graph_context),
"entities_found": entities,
}
def _merge_contexts(self, vector_results, graph_context):
"""合并向量检索和图谱检索的上下文"""
context_parts = ["=== 相关文档片段 ==="]
for i, result in enumerate(vector_results):
context_parts.append(f"[文档{i+1}] {result['text']}")
context_parts.append("\n=== 知识图谱关系 ===")
for record in graph_context:
context_parts.append(f"- {record}")
return "\n".join(context_parts)
6. Graph RAG 实战¶
6.1 使用 LlamaIndex 构建 Graph RAG¶
from llama_index.core import KnowledgeGraphIndex, SimpleDirectoryReader
from llama_index.core.graph_stores import SimpleGraphStore
from llama_index.llms.openai import OpenAI
# 1. 加载文档
documents = SimpleDirectoryReader("./data").load_data()
# 2. 构建知识图谱索引
graph_store = SimpleGraphStore()
llm = OpenAI(model="gpt-5-mini")
kg_index = KnowledgeGraphIndex.from_documents(
documents,
graph_store=graph_store,
llm=llm,
max_triplets_per_chunk=10, # 每个文本块最多抽取10个三元组
show_progress=True,
)
# 3. 查询
query_engine = kg_index.as_query_engine(
include_text=True, # 包含原文
response_mode="tree_summarize",
embedding_mode="hybrid", # 混合模式:向量 + 图谱
sim_score_threshold=0.5,
)
response = query_engine.query("DeepSeek-V3 的技术特点是什么?")
print(response)
6.2 使用 Microsoft GraphRAG¶
# 安装
pip install graphrag
# 初始化项目
graphrag-init --root ./my_graphrag_project
# 配置 settings.yaml
# 设置 LLM API key 和模型
# 索引文档
graphrag index --root ./my_graphrag_project
# 查询
graphrag query --root ./my_graphrag_project \
--method local \
--query "所有大模型的技术趋势"
7. Graph RAG vs 传统 RAG¶
7.1 性能对比¶
| 维度 | 传统 RAG | Graph RAG |
|---|---|---|
| 事实型问答 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 多跳推理 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 全局概览 | ⭐⭐ | ⭐⭐⭐⭐ |
| 构建成本 | 低 | 高(需构建图谱) |
| 查询延迟 | 低 | 中(图遍历) |
| 存储需求 | 低 | 中(图数据库) |
| 可解释性 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
7.2 适用场景¶
选择决策:
├── 简单问答(单文档检索)→ 传统 RAG
├── 多跳推理(跨文档关联)→ Graph RAG
├── 全局概览(数据集总结)→ Graph RAG (Global Search)
├── 实体密集型(人物/组织/地点)→ Graph RAG
├── 实时性要求高 → 传统 RAG
└── 需要可解释性 → Graph RAG
7.3 最佳实践:混合架构¶
class AdaptiveRAG:
"""自适应 RAG:根据查询类型选择策略"""
def __init__(self, vector_rag, graph_rag, llm):
self.vector_rag = vector_rag
self.graph_rag = graph_rag
self.llm = llm
def query(self, question: str) -> str:
# 1. 分类查询类型
query_type = self._classify_query(question)
if query_type == "simple_fact":
return self.vector_rag.query(question)
elif query_type == "multi_hop":
return self.graph_rag.local_search(question)
elif query_type == "global_overview":
return self.graph_rag.global_search(question)
else:
# 混合模式
vector_answer = self.vector_rag.query(question)
graph_answer = self.graph_rag.local_search(question)
return self._merge_answers(question, vector_answer, graph_answer)
def _classify_query(self, question: str) -> str:
prompt = f"""将以下问题分类为:
- simple_fact: 简单事实查询
- multi_hop: 需要多步推理
- global_overview: 全局概览
问题:{question}
分类:"""
return self.llm.generate(prompt).strip().lower()
💡 进阶补充:Graph RAG 前沿框架对比
除了 Microsoft GraphRAG,近年来涌现了多个优秀的 Graph RAG 框架,各有特色:
框架 核心思想 优势 局限 Microsoft GraphRAG 社区检测 + Map-Reduce 全局摘要 全局概览能力强,适合总结性问题 索引成本高,社区摘要消耗大量 Token LightRAG 轻量级图构建,增量更新友好 构建速度快,支持流式增量 全局推理能力弱于 GraphRAG FRAG (Fast RAG) 跳过社区检测,直接用实体链接 极简架构,延迟低 缺乏全局视角 GraphIRAG 结合信息检索与图推理 检索精度高,支持多跳推理 实现复杂度高 选型决策树:
Text OnlyGraph RAG 选型 ├── 需要全局概览 / 总结性问答 → Microsoft GraphRAG ├── 需要快速原型 / 增量更新 → LightRAG ├── 追求低延迟 / 简单部署 → FRAG └── 需要多跳推理 + 高精度 → GraphIRAG生产环境建议:先用 LightRAG 做快速验证,确认 Graph RAG 路线可行后,再根据场景迁移到 Microsoft GraphRAG 或 GraphIRAG。
🔥 实战经验:智能查询路由与降级策略
在生产级 Graph RAG 系统中,不同复杂度的查询应走不同的检索路径。参考 all-in-rag 的智能路由设计:
Pythonclass IntelligentQueryRouter: """基于查询复杂度的智能路由""" def route(self, query: str) -> str: complexity = self._analyze_complexity(query) if complexity == "simple": # 简单查询:直接向量检索 return "vector_search" elif complexity == "medium": # 中等查询:向量检索 + 图谱扩展 return "hybrid_search" else: # 复杂查询:全局图谱搜索 + 社区摘要 return "global_graph_search" def _analyze_complexity(self, query: str) -> str: """分析查询复杂度""" # 启发式规则 + LLM 判断 if any(kw in query for kw in ["所有", "总结", "趋势", "对比"]): return "complex" # 全局性问题 elif any(kw in query for kw in ["关系", "之间", "连接"]): return "medium" # 关系型问题 else: return "simple" # 事实型问题降级策略:当图谱检索失败或超时时,自动降级到纯向量检索,确保系统可用性:
📝 本章小结¶
| 概念 | 核心要点 |
|---|---|
| 知识图谱 | 三元组 (S, P, O) 构建实体关系网络 |
| Graph RAG | 图结构增强检索,支持多跳推理 |
| Local Search | 实体链接 + 子图检索,适合事实型查询 |
| Global Search | 社区摘要 + Map-Reduce,适合全局概览 |
| 混合架构 | 向量检索 + 图谱检索互补 |
✏️ 练习与面试¶
练习 1 :知识图谱构建(⭐)¶
使用 NetworkX 从一段文本中构建知识图谱,实现实体和关系的可视化展示。
💡 参考答案
# 练习1:知识图谱构建参考实现
import networkx as nx
import json
from openai import OpenAI
client = OpenAI()
def extract_triples(text: str) -> list[dict]:
"""使用 LLM 从文本中抽取三元组"""
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": """从文本中抽取知识图谱三元组。
输出JSON格式:{"triples": [{"subject": "...", "predicate": "...", "object": "..."}]}
只输出JSON。"""},
{"role": "user", "content": text},
],
max_tokens=500,
)
return json.loads(resp.choices[0].message.content)["triples"]
def build_knowledge_graph(triples: list[dict]) -> nx.DiGraph:
"""构建有向知识图谱"""
G = nx.DiGraph()
for t in triples:
G.add_edge(t["subject"], t["object"], relation=t["predicate"])
return G
def query_graph(G: nx.DiGraph, entity: str) -> list[str]:
"""查询实体的所有关系"""
if entity not in G:
return []
results = []
for neighbor in G.successors(entity):
relation = G.edges[entity, neighbor]["relation"]
results.append(f"{entity} --[{relation}]--> {neighbor}")
for neighbor in G.predecessors(entity):
relation = G.edges[neighbor, entity]["relation"]
results.append(f"{neighbor} --[{relation}]--> {entity}")
return results
# 使用示例
text = """
DeepSeek 是一家中国 AI 公司,总部位于杭州。
DeepSeek 发布了 DeepSeek-V3 模型,该模型采用 MoE 架构。
DeepSeek-V3 的参数量为 671B,其中激活参数为 37B。
DeepSeek 还发布了 DeepSeek-R1,这是一个推理模型。
"""
triples = extract_triples(text)
G = build_knowledge_graph(triples)
print(f"图谱统计: {G.number_of_nodes()} 个节点, {G.number_of_edges()} 条边")
print(f"\nDeepSeek 的所有关系:")
for r in query_graph(G, "DeepSeek"):
print(f" {r}")
# 可视化(需要 matplotlib)
# import matplotlib.pyplot as plt
# pos = nx.spring_layout(G)
# nx.draw(G, pos, with_labels=True, node_color='lightblue', arrows=True)
# plt.savefig("knowledge_graph.png")
练习 2 : Graph RAG 检索实现(⭐⭐)¶
实现一个简单的 Graph RAG 检索系统:构建知识图谱 → 实体链接 → 子图检索 → 生成回答。对比 Graph RAG 和普通向量 RAG 在多跳推理问题上的表现。
💡 参考答案
# 练习2:Graph RAG 检索实现参考
import networkx as nx
from openai import OpenAI
import json
client = OpenAI()
class SimpleGraphRAG:
def __init__(self):
self.graph = nx.DiGraph()
self.documents = {}
def add_documents(self, docs: list[str]):
"""添加文档并构建知识图谱"""
for i, doc in enumerate(docs):
self.documents[i] = doc
triples = self._extract_triples(doc)
for t in triples:
self.graph.add_edge(
t["subject"], t["object"],
relation=t["predicate"],
doc_id=i
)
def _extract_triples(self, text: str) -> list[dict]:
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "抽取三元组,输出JSON: {\"triples\": [{\"subject\":\"\", \"predicate\":\"\", \"object\":\"\"}]}"},
{"role": "user", "content": text},
],
max_tokens=300,
)
try:
return json.loads(resp.choices[0].message.content)["triples"]
except:
return []
def _entity_linking(self, query: str) -> list[str]:
"""从查询中识别实体"""
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "从问题中提取关键实体名词,输出JSON: {\"entities\": [\"实体1\", \"实体2\"]}"},
{"role": "user", "content": query},
],
max_tokens=100,
)
try:
return json.loads(resp.choices[0].message.content)["entities"]
except:
return []
def _get_subgraph(self, entities: list[str], hops: int = 2) -> list[str]:
"""获取实体周围的子图"""
doc_ids = set()
for entity in entities:
if entity in self.graph:
# BFS 获取 hops 跳内的邻居
visited = {entity}
current_level = {entity}
for _ in range(hops):
next_level = set()
for node in current_level:
for neighbor in self.graph.successors(node):
if neighbor not in visited:
next_level.add(neighbor)
visited.add(neighbor)
edge_data = self.graph.edges[node, neighbor]
doc_ids.add(edge_data.get("doc_id", -1))
for neighbor in self.graph.predecessors(node):
if neighbor not in visited:
next_level.add(neighbor)
visited.add(neighbor)
edge_data = self.graph.edges[neighbor, node]
doc_ids.add(edge_data.get("doc_id", -1))
current_level = next_level
return [self.documents[doc_id] for doc_id in doc_ids if doc_id in self.documents]
def query(self, question: str) -> str:
"""Graph RAG 查询"""
# Step 1: 实体链接
entities = self._entity_linking(question)
# Step 2: 子图检索
context_docs = self._get_subgraph(entities, hops=2)
if not context_docs:
return "无法找到相关信息"
# Step 3: 生成回答
context = "\n".join(context_docs)
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": f"基于以下信息回答问题:\n{context}"},
{"role": "user", "content": question},
],
max_tokens=500,
)
return resp.choices[0].message.content
# 使用示例
docs = [
"张三毕业于清华大学计算机系,现在在字节跳动担任AI工程师。",
"字节跳动开发了豆包大模型,该模型支持多轮对话。",
"清华大学位于北京海淀区,是中国顶尖大学之一。",
"豆包大模型的参数量未公开,但据估计超过千亿级别。",
]
graph_rag = SimpleGraphRAG()
graph_rag.add_documents(docs)
# 多跳推理问题
answer = graph_rag.query("张三工作的地方开发了什么大模型?这个模型有什么特点?")
print(f"回答: {answer}")
练习 3 :混合架构设计(⭐⭐⭐)¶
设计并实现一个"向量 + 图谱"混合 RAG 系统:简单事实型问题走向量检索,复杂关联型问题走图谱检索,系统自动判断使用哪种方式。
💡 参考答案
# 练习3:混合架构参考实现
from openai import OpenAI
import json
client = OpenAI()
class HybridRAG:
"""向量 + 图谱混合 RAG"""
def __init__(self):
self.vector_docs = [] # 向量检索文档
self.graph_triples = [] # 图谱三元组
self.route_prompt = """判断以下问题适合用哪种检索方式:
- "vector": 简单事实型问题(单实体、定义、描述)
- "graph": 复杂关联型问题(多实体关系、多跳推理、比较分析)
- "both": 需要两种检索结合
输出JSON: {"route": "vector"或"graph"或"both", "reason": "..."}"""
def add_documents(self, docs: list[str]):
"""同时建立向量索引和图谱"""
self.vector_docs.extend(docs)
for doc in docs:
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "抽取三元组: {\"triples\": [{\"subject\":\"\", \"predicate\":\"\", \"object\":\"\"}]}"},
{"role": "user", "content": doc},
],
max_tokens=300,
)
try:
triples = json.loads(resp.choices[0].message.content)["triples"]
self.graph_triples.extend(triples)
except:
pass
def _route_query(self, question: str) -> str:
"""查询路由"""
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": self.route_prompt},
{"role": "user", "content": question},
],
max_tokens=100,
)
try:
return json.loads(resp.choices[0].message.content)["route"]
except:
return "vector" # 默认走向量
def _vector_search(self, query: str, top_k: int = 3) -> list[str]:
"""向量检索(简化版:关键词匹配模拟)"""
scored = []
query_words = set(query.split())
for doc in self.vector_docs:
doc_words = set(doc.split())
overlap = len(query_words & doc_words)
scored.append((overlap, doc))
scored.sort(reverse=True)
return [doc for _, doc in scored[:top_k]]
def _graph_search(self, query: str) -> list[str]:
"""图谱检索"""
# 提取查询实体
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "提取实体: {\"entities\": [\"...\"]}"},
{"role": "user", "content": query},
],
max_tokens=100,
)
try:
entities = json.loads(resp.choices[0].message.content)["entities"]
except:
entities = []
# 查找相关三元组
relevant = []
for t in self.graph_triples:
for e in entities:
if e in t["subject"] or e in t["object"]:
relevant.append(f"{t['subject']} {t['predicate']} {t['object']}")
return relevant
def query(self, question: str) -> str:
route = self._route_query(question)
contexts = []
if route in ("vector", "both"):
contexts.extend(self._vector_search(question))
if route in ("graph", "both"):
contexts.extend(self._graph_search(question))
if not contexts:
contexts = self._vector_search(question) # 降级到向量
context = "\n".join(contexts[:5])
resp = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": f"基于以下信息回答:\n{context}"},
{"role": "user", "content": question},
],
max_tokens=500,
)
return f"[路由: {route}] {resp.choices[0].message.content}"
# 测试
hybrid = HybridRAG()
hybrid.add_documents([
"DeepSeek-V3 采用 MoE 架构,671B 参数。",
"DeepSeek 是中国 AI 公司,总部在杭州。",
"DeepSeek-R1 是推理模型,使用 GRPO 训练。",
"GRPO 是一种无需外部奖励模型的强化学习方法。",
])
print(hybrid.query("DeepSeek-V3 的参数量是多少?")) # → vector
print(hybrid.query("DeepSeek 的模型之间有什么关系?")) # → graph
print(hybrid.query("DeepSeek-R1 使用了什么训练方法?这个方法有什么特点?")) # → both
面试题¶
Q1: Graph RAG 和传统向量 RAG 各自的优劣势是什么?
向量 RAG 优势:实现简单、适合事实型查询、生态成熟。劣势:无法处理多跳推理、缺乏实体关系理解。Graph RAG 优势:支持多跳推理、关系发现、全局概览(社区摘要)。劣势:构建成本高(实体抽取+图谱构建)、查询延迟较高、需要领域知识设计 Schema。
Q2: Microsoft GraphRAG 的 Local Search 和 Global Search 有什么区别?
Local Search:从查询实体出发,获取相关子图和关联文档片段,适合具体事实型问题(如"DeepSeek-V3 的参数量")。Global Search:利用社区检测(Leiden 算法)生成的社区摘要,通过 Map-Reduce 聚合多个社区的答案,适合全局概览型问题(如"总结所有 AI 公司的技术路线")。
Q3: 如何评估 Graph RAG 系统的效果?
三个维度:①检索质量(实体链接准确率、子图覆盖率);②生成质量(忠实度、相关性,可用 RAGAS);③推理能力(多跳问答准确率,如 HotpotQA 数据集)。建议构建包含事实型、关联型、全局型三类问题的评估集。
🔗 参考资料¶
- Graph RAG: Unlocking LLM Discovery on Complex Data (Microsoft, 2024)
- From Local to Global: A Graph RAG Approach to Query-Focused Summarization
- all-in-rag 第九章:Graph RAG (Datawhale)
- LlamaIndex Knowledge Graph
- Neo4j Graph Database
导航:上一章 24-多模态 RAG 与向量数据库进阶 | 返回目录
最后更新日期: 2026-04-21 适用版本: LLM 应用指南 v2026
⚠️ 核验说明(2026-04-21):本页已纳入深度优化批次。若文中涉及外部工具、API、版本号或第三方产品名称,请以官方文档和实际运行环境为准。