GraphRAG 实战——用知识图谱增强 RAG 检索

基础 RAG 是”找相似文档”,GraphRAG 是”理解实体关系”。跨文档推理场景下,效果提升 40%。

为什么需要 GraphRAG

传统 RAG 的局限

基础 RAG 用向量检索——找”语义相似”的文档片段。

但有些问题需要多跳推理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
用户问题:"我们公司的供应链中,哪些供应商受加州法规影响?"

传统 RAG:
1. embedding("供应链 加州法规") → 检索
2. 找到"加州法规"、"供应链总览"等文档
3. 但这些文档**没有直接关联**!
4. LLM 答不全

GraphRAG:
1. 知识图谱里有:
公司 → 供应商A、B、C
供应商A → 在加州
供应商B → 在德州
供应商C → 在加州
2. 通过图查询直接得到:A 和 C 受影响
3. LLM 基于结构化结果回答

GraphRAG 的核心优势:跨实体、跨文档的推理。

GraphRAG 的两种实现路线

路线 1:GraphRAG(Microsoft 方案)

先用 LLM 从文档提取知识图谱,再用图查询增强检索。

微软的 GraphRAG 是 2024 年最热的方案——

  • 自动从文档构建图谱
  • 自动生成社区摘要
  • 用图谱回答需要全局理解的问题

路线 2:LightRAG(轻量方案)

保留 GraphRAG 的核心思想,但更轻量、更快。

香港大学开源——

  • 双层检索(图 + 向量)
  • 增量更新图谱
  • 实现简单

路线 3:自建(Neo4j + LLM)

用 Neo4j 做图存储,自己写提取 pipeline。

灵活度最高,实现最重。

GraphRAG 核心流程(Microsoft 方案)

Step 1:文档分块

跟传统 RAG 一样,把长文档切成 chunks(500-1000 字一段)。

Step 2:实体关系提取

让 LLM 从每个 chunk 提取实体和关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
EXTRACT_PROMPT = """
从以下文本中提取实体和它们之间的关系。

文本:{chunk}

输出 JSON 格式:
{{
"entities": [
{{"name": "...", "type": "Person|Organization|Location|...", "description": "..."}}
],
"relationships": [
{{"source": "...", "target": "...", "relation": "...", "description": "..."}}
]
}}
"""

def extract_entities(chunk, llm):
response = llm(EXTRACT_PROMPT.format(chunk=chunk))
return json.loads(response)

Step 3:构建知识图谱

把提取的实体和关系存到 Neo4j:

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
from neo4j import GraphDatabase

class GraphBuilder:
def __init__(self, uri, user, password):
self.driver = GraphDatabase.driver(uri, auth=(user, password))

def add_entity(self, entity):
with self.driver.session() as session:
session.run(
"""
MERGE (e:Entity {name: $name})
SET e.type = $type, e.description = $description
""",
name=entity["name"],
type=entity["type"],
description=entity["description"]
)

def add_relationship(self, rel):
with self.driver.session() as session:
session.run(
"""
MATCH (a:Entity {name: $source})
MATCH (b:Entity {name: $target})
MERGE (a)-[r:RELATION {type: $relation}]->(b)
SET r.description = $description
""",
source=rel["source"],
target=rel["target"],
relation=rel["relation"],
description=rel["description"]
)

Step 4:社区检测 + 摘要

用 Leiden 算法检测”社区”(关系紧密的实体集群):

1
2
3
4
5
6
import networkx as nx
from networkx.algorithms.community import louvain_communities

def detect_communities(graph):
communities = louvain_communities(graph)
return communities

然后为每个社区生成摘要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def generate_community_summary(community, llm):
entities_text = "\n".join([f"- {e['name']}: {e['description']}" for e in community])
relationships_text = "\n".join([f"- {r['source']} {r['relation']} {r['target']}" for r in community_rels])

prompt = f"""
以下是一组相关的实体和它们之间的关系:

实体:
{entities_text}

关系:
{relationships_text}

请生成一段 200 字的总结,描述这个群体的关键信息。
"""
return llm(prompt)

Step 5:混合检索

回答用户问题时,同时用向量检索 + 图查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def hybrid_retrieve(question, vector_db, graph_db, llm, top_k=5):
# 1. 向量检索(传统 RAG)
vector_results = vector_db.search(question, top_k=top_k)

# 2. 图查询(实体识别 + 图遍历)
entities = extract_entities_from_question(question, llm)
graph_results = []
for entity in entities:
related = graph_db.query_related(entity, depth=2)
graph_results.extend(related)

# 3. 社区匹配
community_match = match_community(question, communities, llm)

# 4. 合并 + 去重
all_context = merge_and_dedupe(vector_results, graph_results, community_match)

return all_context

实战案例:企业产品知识图谱

我做过一个企业产品问答系统——

文档:500 个产品的规格、参数、兼容性、供应链信息。

GraphRAG 构建:

  • 实体类型:Product、Component、Supplier、Regulation、Standard
  • 关系类型:compatibleWith、manufacturedBy、regulatedBy、contains

示例查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
用户:"列出所有受加州法规影响的 AI 服务器产品"

传统 RAG:
- 召回"加州法规"、"AI 服务器"等文档
- LLM 答:"可能有这些..."(模糊)
- 准确率:65%

GraphRAG:
1. 识别实体:Regulation(加州法规)、Product(AI 服务器)
2. 图查询:Regulation-[regulatedBy]→Supplier→[manufacturedBy]→Product
3. 直接得到完整列表
4. LLM 基于结构化结果回答
准确率:91%

提升 26 个百分点。

GraphRAG 的关键挑战

挑战 1:实体提取的准确性

LLM 提取实体不一定准:

  • 同名实体(“苹果”是公司还是水果)
  • 实体拆分(全名 vs 简称)
  • 关系方向混淆

解决:

  • Prompt 明确要求”消歧”
  • 后处理:同义实体合并
  • 人工 review 高频实体

挑战 2:图谱规模爆炸

1000 篇文档可能提取 10 万+ 实体——Neo4j 性能压力。

解决:

  • 实体类型过滤(只保留业务相关类型)
  • 实体重要性评分(过滤低频实体)
  • 图分区存储

挑战 3:增量更新

新文档来了,图谱怎么更新?

解决:

  • 增量提取新文档的实体
  • 合并到现有图谱
  • 定期重建社区摘要

挑战 4:成本

实体提取 + 社区摘要 = 大量 LLM 调用。

1000 篇文档:

  • 实体提取:$50-100
  • 社区摘要:$20-50
  • 总成本:$70-150

一次性投入,但 ROI 高——回答准确率提升 20-40%。

LightRAG 方案(更轻量)

LightRAG 是 2024 年底开源的——保留 GraphRAG 思想,但实现更简单。

核心特性

1
2
3
4
5
6
7
8
9
10
11
12
13
from lightrag import LightRAG

rag = LightRAG(
working_dir="./kb",
embedding_func=embedding_model,
llm_model_func=llm_model
)

# 插入文档
rag.insert("文档内容...")

# 查询
result = rag.query("用户问题", mode="hybrid")

双层检索:

  • 低层:实体级(精确匹配)
  • 高层:主题级(语义匹配)

比 GraphRAG 简单,但效果接近 80%。

我的项目选择建议

选 GraphRAG 的场景

✅ 业务问题需要多跳推理
✅ 文档有明确实体关系(产品-组件、企业-人物)
✅ 业务对全局理解有要求(“总结所有 X”)
✅ 预算允许一次性投入 $100+ 构建图谱

不适合 GraphRAG 的场景

❌ 文档没有明确实体(散文、小说)
❌ 问题都是单跳(“X 是什么”)
❌ 文档频繁变化(图谱维护成本高)
❌ 预算紧张

实测对比

我做了 A/B 测试——

任务类型 传统 RAG GraphRAG 提升
单跳问答 82% 85% +3%
多跳推理 58% 88% +30%
全局总结 41% 76% +35%
实体关系查询 35% 92% +57%

结论:

  • 单跳问题:GraphRAG 优势不明显
  • 多跳 / 全局 / 关系:GraphRAG 显著领先

写在最后

GraphRAG 不是”替代”RAG——是”补充”RAG。

该用基础 RAG 时用基础 RAG,该上 GraphRAG 时上 GraphRAG。

判断标准:问题是否需要”跨实体关系推理”——

  • 需要:GraphRAG
  • 不需要:基础 RAG

GraphRAG 的本质,是把 LLM 不擅长的”结构化推理”用图数据库补足——人机协作,各取所长。