想象你是一位初到陌生城市的旅行作家。想了解这座城市,传统做法是拉住路人一个个地问:“最近的地铁站怎么走?”“哪家面馆出名?”——单个问题问单个路人,速度快、答案直接。可当你突然要回答“这座城市整体上最有特色的生活方式是什么”这种贯穿全城的问题时,问遍几十个路人也拼不出全景——每个人的答案都只是自己生活半径里的碎片。 GraphRAG 的做法,是先花数天走遍全城,画一张完整地图:把每个地标建筑标记为“实体”,把建筑间的道路标记为“关系”;再按功能把城市划分为老城区、大学区、商务区等“社区”,为每个街区写一段“性格概述”。最后把这些装订成一本导览手册。游客无论问“某个地点怎么走”,还是“全城核心气质是什么”,都能翻到对应章节给出有全局观的回答。 这就是微软 GraphRAG 的精髓:它把知识图谱(城市地图)与检索增强生成(问路人)结合起来,先用大模型从文档中抽取实体与关系构建图谱,再用社区检测算法把图谱划分成层次化社区并逐一生成摘要。回答全局性问题时,它不再依赖碎片化局部检索,而是基于预先算好的“城市导览手册”给出综合答案。

岗位关联

对 AI 从业者而言,GraphRAG 是突破“RAG 只能答点状问题”天花板的关键方法。掌握它,能让你在面对全局性、多文档综合分析需求时给出远超传统 RAG 的方案,直接提升企业知识库问答的上限。

目录
  1. 可视化演示
  2. RAG基础概念
  3. GraphRAG架构
  4. 实体图谱构建
  5. 社区聚类发现
  6. 社区摘要生成
  7. 全局查询增强
  8. 效果评估对比
  9. 应用场景选择
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示GraphRAG核心流程:文档分块与实体关系抽取构建知识图谱、Leiden社区聚类与分层摘要、以及朴素RAG与GraphRAG的检索范式对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.pipe+'

RAG基础概念

你站在街头随机问一位路人:“这个城市的人最怀念什么?”路人只会愣住——他熟悉自己小区附近的小店和公园,却不敢代表整座城市回答。传统 RAG 就像这位路人:它把文档切成许多小片段,用向量相似度找出与问题最像的几个片段,交还给大模型生成答案。对于“地铁站怎么走”这种点状问题,这个流程高效又精准。

但技术实现上,RAG 的检索过程是把问题变成一个向量,在预先向量化的文档片段库中做相似度匹配,取相似度 Top-K 的片段拼进上下文。这就像一个只看局部地图的问路人,碎片与碎片之间没有关联,更看不到全貌。

Ret(q)=arg⁡max⁡d∈Dsim(eq,ed)\displaystyle \text{Ret}(q) = \arg \max_{d \in \mathcal{D}} \text{sim}(\mathbf{e}_q, \mathbf{e}_d)

1. 查询文本 $q$:用户输入的问题,比如“这家面馆几点开门”。

q\displaystyle q

2. 向量化 $\mathbf{e}_q$:用嵌入模型把文本变成一串数字向量,语义相近的文本向量距离近。

eq=Embed(q)\displaystyle \mathbf{e}_q = \text{Embed}(q)

3. 文档库 $\mathcal{D}$:全部被切成小块的文档片段集合,每个片段也预先向量化。

D={d1,d2,…,dN}\displaystyle \mathcal{D} = \{d_1, d_2, \dots, d_N\}

4. 相似度 $\text{sim}(\mathbf{e}_q, \mathbf{e}_d)$:计算问题向量与每个片段向量的余弦相似度。

sim(eq,ed)=eq⋅ed∥eq∥∥ed∥\displaystyle \text{sim}(\mathbf{e}_q, \mathbf{e}_d) = \frac{\mathbf{e}_q \cdot \mathbf{e}_d}{\|\mathbf{e}_q\| \|\mathbf{e}_d\|}

5. 取最大 $\arg\max$:选出相似度最高的几个片段作为上下文喂给大模型。

Ret(q)=arg⁡max⁡d∈Dsim(eq,ed)\displaystyle \text{Ret}(q) = \arg \max_{d \in \mathcal{D}} \text{sim}(\mathbf{e}_q, \mathbf{e}_d)

这就解释了为什么传统 RAG 回答全局性问题时总是“只见树木不见森林”。当问题需要跨越几十个文档片段才能回答时,Top-K 检索到的碎片就像路人甲的所见所闻,东拼西凑也难成体系。RAG 的“问路人”模式本质是单跳问答:一次检索、一次生成,没有把碎片之间的结构关系纳入考量。这也是 GraphRAG 被设计出来的直接动机。

GraphRAG架构

面对“这座城市的核心气质是什么”这类问题,聪明的旅行作家不会去随机问路人,而是先花几天时间绘制全城地图、划分街区、编写导览手册,然后翻开手册从容作答。GraphRAG 的架构正是这份从容:它把整条链路拆成“离线索引”和“在线查询”两个阶段。

索引阶段像绘制城市地图的工程队:大模型逐块阅读文档,抽取实体与关系构建知识图谱;图算法把图谱划分成多个社区;再为每个社区预生成摘要并缓存。查询阶段则像游客服务中心:先判断问题是“点状”还是“全局”,点状问题仍走向量检索快速定位,全局问题则直接调用预计算的社区摘要做综合回答。

GraphRAG(q)=Query(q, Index(D))\displaystyle \text{GraphRAG}(q) = \text{Query}\left(q,\ \text{Index}(\mathcal{D})\right)

1. 文档集 $\mathcal{D}$:全部待索引的原始文档,相当于要勘察的整座城市范围。

D\displaystyle \mathcal{D}

2. 索引阶段 $\text{Index}(\mathcal{D})$:离线执行的四步流水线——抽取实体、构建图谱、社区聚类、生成摘要。

Index(D)=Summarize(Cluster(Graph(Extract(D))))\displaystyle \text{Index}(\mathcal{D}) = \text{Summarize}(\text{Cluster}(\text{Graph}(\text{Extract}(\mathcal{D}))))

3. 查询阶段 $\text{Query}(q, \cdot)$:在线执行的回答函数,输入是用户问题以及索引产物。

Query(q, Index(D))\displaystyle \text{Query}(q,\ \text{Index}(\mathcal{D}))

4. 整套复合结构:先离线建好“导览手册”,再在线按图索骥。

GraphRAG(q)=Query(q, Index(D))\displaystyle \text{GraphRAG}(q) = \text{Query}\left(q,\ \text{Index}(\mathcal{D})\right)

GraphRAG 的架构精髓在于“知识图谱中间层”——它把杂乱文档转成结构化网络,让机器看见了实体之间的关联。而“社区摘要缓存”则像导览手册的章节,把底层图谱压缩成高层语义卡片。这种两阶段设计带来的好处是:索引阶段再昂贵也只在后台一次性发生,查询阶段几乎零延迟复用成果,真正做到了“慢工出细活,一本手册反复用”。

实体图谱构建

绘制城市地图的第一步,是标记每一座地标建筑——钟楼、广场、老面馆——并在它们之间画上道路。GraphRAG 的索引阶段同样如此:大模型逐段阅读文档,抽出“实体”(地标)与“关系”(道路),形成一张知识图谱。

具体做法是设计一段提示词,让 LLM 从每个文本块中抽取结构化三元组。比如读到“乔布斯在 1976 年创立了苹果公司”,就抽出(乔布斯,创立,苹果公司)。同时,LLM 还会抽取实体属性,如“苹果公司总部位于库比蒂诺”中的地点属性。这一步是整条流水线的地基,地标标错了,后面的地图、社区、导览全都歪。

G={(h,r,t)∣h,t∈E, r∈R}\displaystyle \mathcal{G} = \{(h, r, t) \mid h, t \in \mathcal{E},\ r \in \mathcal{R}\}

1. 实体集合 $\mathcal{E}$:所有被抽取出的实体,如“乔布斯”“苹果公司”“库比蒂诺”。

E={e1,e2,…,en}\displaystyle \mathcal{E} = \{e_1, e_2, \dots, e_n\}

2. 关系集合 $\mathcal{R}$:所有关系类型,如“创立”“发布”“位于”。

R={r1,r2,…,rm}\displaystyle \mathcal{R} = \{r_1, r_2, \dots, r_m\}

3. 三元组 $(h, r, t)$:一条有向边的完整表达:头实体通过关系指向尾实体。

(h,r,t)h,t∈E, r∈R\displaystyle (h, r, t) \quad h, t \in \mathcal{E},\ r \in \mathcal{R}

4. 图谱 $\mathcal{G}$:全部三元组的集合,构成一张带方向的图。

G={(h,r,t)∣h,t∈E, r∈R}\displaystyle \mathcal{G} = \{(h, r, t) \mid h, t \in \mathcal{E},\ r \in \mathcal{R}\}

构建图谱时还有一个关键环节叫共指消解——“乔布斯”“史蒂夫·乔布斯”"Jobs"在地图上应该是同一座地标,需要合并。这就像两张街区图上同一座钟楼被标了两个名字,必须合成一个节点。构建完成后,图谱以节点表和边表的形式存储,支持后续社区检测时的快速遍历。这个阶段还支持增量式索引:新文档来了只更新相关子图,不必推倒全图重画,像城市扩建时只需在旧地图上补新区块。

社区聚类发现

地图上的地标并不是均匀分布的——老城区的钟楼、教堂、集市彼此挨得近,大学区的教学楼和图书馆也扎堆。GraphRAG 用社区检测算法自动找出这种“扎堆”结构,把图谱划分成一个个社区。算法采用的是图计算领域著名的 Leiden 算法,它是 Louvain 算法的升级版,能保证划分出的社区内部连接紧密、社区之间连接稀疏。

Leiden 算法的目标是通过优化“模块度”来找到最佳划分。模块度衡量的是:社区内部的连接比例,比随机连接预期多出多少。模块度越高,说明划分质量越好。GraphRAG 会递归地应用社区检测,形成一棵“社区树”——底层是最细粒度的叶子社区,往上层层合并成更大范围的社区,对应到城市里就是“街区→城区→全城”。

Q=12m∑ij[Aij−kikj2m]δ(ci,cj)\displaystyle Q = \frac{1}{2m} \sum_{ij} \left[ A_{ij} - \frac{k_i k_j}{2m} \right] \delta(c_i, c_j)

1. 邻接矩阵 $A_{ij}$:节点 $i$ 与节点 $j$ 之间有边则为 1,否则为 0。

Aij∈{0,1}\displaystyle A_{ij} \in \{0, 1\}

2. 节点的度 $k_i$:节点 $i$ 连接的边数,即这座“地标”的街道数量。

ki=∑jAij\displaystyle k_i = \sum_j A_{ij}

3. 总边数 $m$:整个图谱的边数,公式中的 $2m$ 是全部“度数”之和。

m=12∑iki\displaystyle m = \frac{1}{2}\sum_i k_i

4. 同社区指示 $\delta(c_i, c_j)$:当节点 $i, j$ 被分到同一社区时取 1,否则取 0。

δ(ci,cj)={1,ci=cj0,ci≠cj\displaystyle \delta(c_i, c_j) = \begin{cases} 1, & c_i = c_j \\ 0, & c_i \ne c_j \end{cases}

5. 模块度求和:对全图所有节点对累加“实际连接减随机期望”,再归一化。

Q=12m∑ij[Aij−kikj2m]δ(ci,cj)\displaystyle Q = \frac{1}{2m} \sum_{ij} \left[ A_{ij} - \frac{k_i k_j}{2m} \right] \delta(c_i, c_j)

模块度的妙处在于它给“什么是好的街区划分”一个量化标准:如果划分纯靠随机,内部连接数就是 $k_i k_j / 2m$;实际连接比它多,说明这个社区真有“凝聚力”。Leiden 算法通过不断尝试移动节点来提升 Q 值,最终收敛到一个高质量划分。而递归划分则保证社区粒度可调——就像导览手册既有分街区详述,又有分城区概览,不同层级的读者各取所需。

社区摘要生成

街区划定之后,导览手册的编写者要动笔了:为每个街区写一段“性格概述”,说清楚这里以什么闻名、有哪些值得注意的地方。GraphRAG 用大模型为每个社区生成摘要——先对最底层的叶子社区生成详细摘要,再让上层社区把若干子社区的摘要“合并提炼”成更凝练的概览,逐层向上,形成一棵摘要树。

每个社区的摘要内容,来自该社区所有实体所关联的源文本片段。大模型把这些碎片读进去,输出一段结构化的社区概述,比如“该社区聚焦苹果公司的产品与领导层信息,涉及乔布斯与库克的继任关系”。由于社区是分层嵌套的,摘要也天然分层:叶子摘要细节丰富,上层摘要高屋建瓴。

Summary(C)=LLM(⋃v∈CText(v))\displaystyle \text{Summary}(C) = \text{LLM}\left(\bigcup_{v \in C} \text{Text}(v)\right)

1. 社区 $C$:一个簇内实体的集合,如“苹果公司、乔布斯、库克、iPhone、库比蒂诺”。

C={v1,v2,…,vn}\displaystyle C = \{v_1, v_2, \dots, v_n\}

2. 实体关联文本 $\text{Text}(v)$:实体 $v$ 在原始文档中出现的上下文片段。

Text(v)\displaystyle \text{Text}(v)

3. 文本合并 $\bigcup$:把社区内所有实体的文本片段汇总成一个文本袋。

T=⋃v∈CText(v)\displaystyle T = \bigcup_{v \in C} \text{Text}(v)

4. 大模型提炼 $\text{LLM}(\cdot)$:将文本袋压缩成要点式摘要。

Summary(C)=LLM(T)\displaystyle \text{Summary}(C) = \text{LLM}(T)

这些摘要不是查询时临时生成的,而是在索引阶段离线预计算好并缓存进存储系统。查询时直接读取,省去了在线计算成本,也让回答可以快速跨社区综合信息。从叶子到根部的全部社区摘要共同构成“导览手册”的完整内容。当用户提出全局性问题时,系统就能像翻阅手册一样快速定位到所有相关章节,而不是临时抱佛脚去检索零散段落。

全局查询增强

游客走进咨询中心,抛出一个贯穿全城的问题:“这座城市最值得体验的三件事是什么?”咨询员不会只翻某一页,而是把整本导览手册的各个街区章节快速扫一遍,挑出高频亮点,汇总成一份综合推荐。GraphRAG 的全局查询阶段正是采用这样的"Map-Reduce"策略。

系统先判断问题属于全局性还是局部性。如果是局部性问题,走传统向量检索更快;如果是全局性问题,就进入 Map-Reduce 流水线:把问题同时分发给所有社区(Map),每个社区基于自己的摘要生成一个“从本街区视角出发”的答案片段;然后将所有片段汇总(Reduce),通过去重、排序、提炼,生成最终的全局答案。由于 Map 阶段可以并行执行,整个流程既全面又高效。

Answer(q)=Reduce({Map(q,Sk)}k=1K)\displaystyle \text{Answer}(q) = \text{Reduce}\left(\{\text{Map}(q, \mathcal{S}_k)\}_{k=1}^{K}\right)

1. 全局性问题 $q$:需要综合全库信息才能回答的问题,如“公司有哪些福利类别”。

q\displaystyle q

2. 社区摘要集合 $\mathcal{S}_k$:第 $k$ 个社区预先生成的摘要内容。

Sk\displaystyle \mathcal{S}_k

3. Map 映射:将 $q$ 与单个社区摘要组合,生成该社区的局部回答。

Map(q,Sk)→ak\displaystyle \text{Map}(q, \mathcal{S}_k) \to a_k

4. Reduce 归约:把全部局部回答 $a_1, a_2, \dots, a_K$ 汇总成最终答案。

Answer(q)=Reduce(a1,a2,…,aK)\displaystyle \text{Answer}(q) = \text{Reduce}(a_1, a_2, \dots, a_K)

这套 Map-Reduce 的设计巧妙之处在于:每个社区都像一个独立的信息源,它们各自只负责自己那片“街区”的见解,但组合起来便能覆盖整张知识图谱。Reduce 阶段还要避免重复信息——如果三个社区都提到“苹果公司重视创新”,最终答案只需保留一次并标注多个来源。这既保证了全局覆盖面,又保持了答案的简洁与可追溯性,正是 GraphRAG 处理全局问题的核心竞争力。

效果评估对比

当旅行手册出版后,我们终于可以客观对比两种问路方式了:随机拉路人问(朴素 RAG)与翻阅导览手册(GraphRAG)。微软在论文中系统评测了二者在全局性问题上的表现,GraphRAG 在答案的综合性、多样性、可授权性等指标上均显著胜出。所谓综合性,指答案是否覆盖了所有相关方面——问“公司的福利体系有哪些”,传统 RAG 往往遗漏一半,GraphRAG 能给出完整清单。

但这种碾压是有代价的:索引阶段的 Token 消耗远高于传统 RAG。构建知识图谱时要让 LLM 逐块抽取三元组,社区聚类后还要为每个社区生成摘要,一个中型文档库可能要消耗几百万到上千万 Token。好消息是这是离线成本,可以被大量查询摊薄。用一个指标来量化提升幅度:

Δ=ScoreGraphRAG−ScoreNaiveScoreNaive×100%\displaystyle \Delta = \frac{\text{Score}_{\text{GraphRAG}} - \text{Score}_{\text{Naive}}}{\text{Score}_{\text{Naive}}} \times 100\%

1. 朴素 RAG 得分 $\text{Score}_{\text{Naive}}$:在全局问题测评集上,传统 RAG 的综合度得分,假设为 58。

ScoreNaive=58\displaystyle \text{Score}_{\text{Naive}} = 58

2. GraphRAG 得分 $\text{Score}_{\text{GraphRAG}}$:同一测评集上 GraphRAG 的得分,假设为 92。

ScoreGraphRAG=92\displaystyle \text{Score}_{\text{GraphRAG}} = 92

3. 分子(提升量):两个得分的差额。

ScoreGraphRAG−ScoreNaive=34\displaystyle \text{Score}_{\text{GraphRAG}} - \text{Score}_{\text{Naive}} = 34

4. 分母归一:除以朴素 RAG 得分,得到相对提升。

Δ=3458×100%≈58.6%\displaystyle \Delta = \frac{34}{58} \times 100\% \approx 58.6\%

除了分数提升,GraphRAG 还带来一个隐性红利——可追溯性。传统 RAG 的答案来自碎片拼接,读者很难搞清依据;GraphRAG 的每个结论都能对应到某个社区的摘要,而摘要又能追溯到原始文档,像导览手册每一页都标注了信息来源。因此在实际选型中,团队需要权衡:如果业务问题以全局分析为主,离线索引的高成本就值得投入;如果大量是简单单点查询,这笔账就要重新算——这是典型的“离线成本换在线质量”的取舍。

应用场景选择

回到旅行作家本人:并不是每一次出行都需要画全城地图。如果只是路过歇脚,想知道附近哪家便利店开着,问路人是最高效的;但如果要写一本深度城市指南,全域地图和大规模街区调研就必不可少。GraphRAG 的应用选型也遵循同样的逻辑——先分辨问题类型,再决定投入多重的方案。

适合 GraphRAG 的场景有一个共性:答案必须横跨多篇文档、多个主题才能形成。典型如企业制度库的全局分析——“我们公司一共有哪些类型的补贴政策?”答案散落在几十份 PDF 中,传统 RAG 检索到的碎片永远不完整。科研文献综述同样适合,研究者需要综合几十篇论文回答“这些试验的共同终点指标是什么”。这些场景中 GraphRAG 的社区摘要天然提供了“章节化”的知识组织方式。

ROI=GainqueryCostindex\displaystyle \text{ROI} = \frac{\text{Gain}_{\text{query}}}{\text{Cost}_{\text{index}}}

1. 查询收益 $\text{Gain}_{\text{query}}$:引入 GraphRAG 后回答质量提升带来的业务价值(准确率、覆盖率、用户满意度)。

Gainquery\displaystyle \text{Gain}_{\text{query}}

2. 索引成本 $\text{Cost}_{\text{index}}$:构建知识图谱和社区摘要消耗的 Token、算力与时间。

Costindex\displaystyle \text{Cost}_{\text{index}}

3. 比值判断:ROI 越高越值得投入;查询量越大,固定索引成本被摊销得越薄。

ROI=GainqueryCostindex\displaystyle \text{ROI} = \frac{\text{Gain}_{\text{query}}}{\text{Cost}_{\text{index}}}

反过来,有两类场景要谨慎。一类是单点事实查询,如“某产品的保修期是多少”——向量检索直接命中,毫秒级返回,GraphRAG 反而大材小用。另一类是成本敏感且文档高频更新的场景,因为每次文档更新都可能触发实体图谱和社区摘要的重新计算,让索引成本不断追加。所以成熟的方案常常是“混合架构”:局部问题走朴素 RAG 快速通道,全局问题走 GraphRAG 深度通道,让导览手册只服务真正需要全局视野的游客。

AI 应用场景

企业制度库全局分析

阶段:索引构建与全局查询

解决的问题:企业的 HR 制度库往往有上百份 PDF,员工常问“我们公司一共有哪些补贴政策?”“年假和福利都有哪几类?”这类答案分散在多份文档中的全局问题。传统 RAG 只能检索到零散片段,经常遗漏。GraphRAG 把制度文档构建成实体关系图谱,按补贴、假期、保险等主题划分社区并生成摘要,让 LLM 能基于完整社区信息回答,覆盖率显著提升。

具体案例:某互联网公司 HR 知识库接入 GraphRAG 后,“公司有哪些带薪假期类型”这类全局问题的回答完整度从 52% 提升到 91%,并且答案能注明依据出自哪份制度文档的哪个章节。

科研文献综述生成

阶段:社区摘要 + Map-Reduce 查询

解决的问题:科研人员在写综述时需要综合几十篇论文回答“这些试验的共同终点是什么?”“已报道的不良反应有哪些重合?”——单篇论文信息孤立,人工通读耗时数周。GraphRAG 将论文集索引成图谱,药物、剂量、症状等实体自动聚类成社区,社区摘要把每簇研究的结论先行提炼,综述问题直接走全局问答。

具体案例:某药物研发团队用 GraphRAG 索引 50 篇临床试验论文后,提问“各试验中共同报告的不良反应有哪些”,系统返回了跨论文的合并清单,并为每项反应标明出处论文,团队据此调整了安全性监测指标。

客服工单主题归纳

阶段:全局查询增强

解决的问题:客服经理每周要回答“最近用户反馈最多的是哪几类问题?”——工单碎片化、类别交织,人工统计又慢又主观。GraphRAG 把上万条工单构建为问题实体图谱,相同问题自动聚成社区,社区摘要归纳出每个问题簇的共同描述,全局问答直接给出排序后的主题清单。

具体案例:某电商平台客服系统每周自动对 1.2 万条工单做 GraphRAG 全局归纳,输出“物流延迟、退款流程、尺寸不符”等 TOP 问题及关联商品根因,产品团队据此优化了售后流程。

Python 演示

python135 行
"""简化版 Microsoft GraphRAG 核心流程演示 模拟四步:实体关系抽取 -> 构建知识图谱 -> 社区划分 -> 全局问答(Map-Reduce)仅用 Python 标准库,即可运行。"""import refrom collections import defaultdict # ---------- 第1步:输入文档集 ----------DOCUMENTS = [    "苹果公司由乔布斯创立,总部位于库比蒂诺。",    "乔布斯在1976年创办了苹果公司。",    "苹果公司在2007年发布了iPhone。",    "库克接替乔布斯成为苹果公司CEO。",    "iPhone是苹果公司最畅销的产品。",    "库克早年曾在IBM任职多年。",    "微软公司由比尔·盖茨创立,总部位于雷德蒙德。",    "纳德拉是微软公司现任CEO。",    "微软公司的Windows系统全球闻名。",    "特斯拉公司由马斯克领导,总部在奥斯汀。",    "马斯克还创立了太空探索公司SpaceX。",] # ---------- 第2步:实体与关系抽取(用规则模拟 LLM 抽取) ----------ENTITY_LIST = [    "苹果公司", "乔布斯", "库克", "iPhone", "库比蒂诺", "IBM",    "微软公司", "比尔·盖茨", "纳德拉", "Windows", "雷德蒙德",    "特斯拉公司", "马斯克", "奥斯汀", "SpaceX"]RELATION_VERBS = ["创立", "创办", "位于", "发布", "接替", "成为", "领导"]  def extract_entities(text):    """返回文本中出现的实体集合"""    return {e for e in ENTITY_LIST if e in text}  def extract_relations(text):    """用位置规则抽取 (头实体, 动词, 尾实体) 三元组"""    entities = [e for e in ENTITY_LIST if e in text]    triples = []    for verb in RELATION_VERBS:        if verb not in text:            continue        vidx = text.index(verb)        for head in entities:            if text.index(head) < vidx:                for tail in entities:                    if text.index(tail) > vidx and head != tail:                        triples.append((head, verb, tail))    return triples  # ---------- 第3步:构建知识图谱 ----------def build_graph(docs):    """返回邻接表图结构(实体 -> 邻居集合)与三元组列表"""    graph = defaultdict(set)    triples = []    for doc in docs:        for e in extract_entities(doc):            graph[e]        for h, r, t in extract_relations(doc):            graph[h].add(t)            graph[t].add(h)            triples.append((h, r, t))    return graph, triples  # ---------- 第4步:社区检测(用连通分量近似聚类) ----------def find_communities(graph):    """以连通分量作为最简社区划分,近似 Leiden 粗粒度结果"""    visited = set()    communities = []    for node in graph:        if node in visited:            continue        stack, comp = [node], []        visited.add(node)        while stack:            v = stack.pop()            comp.append(v)            for nb in graph[v]:                if nb not in visited:                    visited.add(nb)                    stack.append(nb)        communities.append(comp)    return communities  # ---------- 第5步:社区摘要 ----------def summarize_community(community, triples):    """统计社区内的实体与关系,生成简要描述"""    inner = [t for t in triples if t[0] in community and t[2] in community]    return "社区[{}]:{}个实体,{}条内部关系".format(        "、".join(community), len(community), len(inner))  # ---------- 第6步:全局问答(Map-Reduce) ----------def global_query(question, communities, summaries):    """Map:每个社区独立回答;Reduce:综合各社区视角输出最终答案"""    local_answers = []    for idx, (community, summary) in enumerate(zip(communities, summaries)):        local = "社区{}({})认为:结合摘要『{}』,{}".format(            idx, "、".join(community), summary, question)        local_answers.append(local)    final = "综合{}个社区的视角:".format(len(local_answers)) + ";".join(local_answers[:3])    return final  # ---------- 主流程 ----------def main():    print("=== GraphRAG 索引阶段 ===")    graph, triples = build_graph(DOCUMENTS)    print("抽取实体:", sorted(graph.keys()))    print("抽取关系:")    for h, r, t in triples:        print("  ({}, {}, {})".format(h, r, t))     communities = find_communities(graph)    print("\n社区聚类结果:")    for idx, c in enumerate(communities):        print("  社区{}: {}".format(idx, c))     summaries = [summarize_community(c, triples) for c in communities]    print("\n社区摘要:")    for s in summaries:        print("  -", s)     print("\n=== GraphRAG 查询阶段 ===")    answer = global_query("苹果公司的关键人物有哪些?", communities, summaries)    print(answer)  if __name__ == "__main__":    main()
目的:用纯 Python 标准库完整走一遍 GraphRAG 的索引与查询主流程,帮助理解“实体抽取→图谱构建→社区划分→摘要生成→全局问答”的链路。
  1. graph = defaultdict(set)用邻接表存储知识图谱,每个实体映射到其邻居集合,后续社区检测只需遍历邻居。
  2. extract_relations(text)用“动词位置 + 实体位置”规则抽取三元组,模拟 LLM 的结构化抽取能力。
  3. triples.append((h, r, t))这里同时将头尾互连,把有向三元组转成无向图的边。
  4. find_communities(graph)用 BFS 找连通分量,作为社区检测的最简近似;真实 GraphRAG 用 Leiden 算法。
  5. summarize_community(community, triples)只统计社区内部关系,生成描述性摘要,模拟 LLM 摘要生成。
  6. global_query(question, communities, summaries)Map 阶段让每个社区结合摘要独立回答,Reduce 阶段用字符串拼接模拟综合汇总。

常见误区

误区“论文都说 GraphRAG 效果碾压传统 RAG,那我的项目无脑上 GraphRAG 就对了,检索能答的它能答,检索不能答的它也能答。”

正解GraphRAG 强在全局性问题,弱在单点事实查询。“某产品保修期多久”这类问题,向量检索一次命中、毫秒返回,GraphRAG 还要先走 Map-Reduce 流程,既慢又贵。就像导览手册再详尽,问“这家店几点打烊”还是直接问店员更快。正确做法是混合架构:局部问题走朴素 RAG,全局问题走 GraphRAG。

为何常见:论文与宣传材料突出的是全局问题的对比数字,容易给人“全面超越”的印象,忽略了问题的适用范围。

误区“知识图谱那么复杂,实体关系都靠人工整理的话,GraphRAG 落地成本肯定高得离谱,小团队根本用不起。”

正解GraphRAG 的知识图谱完全由大模型自动抽取,工程师只需设计抽取提示词、定义实体类型和关系类型。人工角色从“画地图”降级为“审核地图”——检查少量错误三元组、调整提示词,大部分标注工作由 LLM 承担。这与传统知识图谱工程中大量人工本体编辑有本质区别。

为何常见:传统知识图谱项目确实重度依赖人工构建,人们容易把旧经验套到新技术上,忽略了 LLM 带来的自动化能力。

误区“GraphRAG 里的社区聚类是微软为它新发明的算法,肯定很神秘复杂,普通开发者难以理解。”

正解Leiden 算法是图计算领域 2020 年就已发表的成熟开源算法,GraphRAG 是它的应用者而非发明者。这就像导览手册作者不需要发明“街区”概念,只需借用城市规划中成熟的区域划分方法。理解这一点对开发者很重要:社区划分部分可以直接复用图计算库(如 networkx、igraph),不必重复造轮子。

为何常见:新技术里的组件听起来都像新发明,加上论文对算法部分着墨不多,容易让人误以为整套体系全是原创。

误区“索引建完就万事大吉,摘要缓存永远不用更新。文档库新增内容时,重新跑一遍向量库就够了。”

正解文档库发生新增或修改时,实体图谱可能产生新节点和新边,原有社区划分和社区摘要都要相应更新。否则导览手册会“过期”——手册里没写新开张的博物馆,游客问起来就答不上来。生产系统中需要设计增量重索引策略,定期对变更文档重新抽取并更新受影响社区。

为何常见:RAG 的“索引一次、长期使用”惯性思维深入人心,容易忽略知识图谱是有结构的状态,需要维护而不是一次性构建。

误区“GraphRAG 用知识图谱替代了向量检索,embedding 这项技术可以彻底丢掉了。”

正解GraphRAG 是“图加向量”的混合体而非替代关系。实体抽取需要判断文本语义、局部问题回答仍走向量 Top-K 检索、社区摘要也可能通过向量相关性召回。知识图谱负责提供全局结构,向量检索负责快速定位具体内容,两者各司其职。就像导览手册给的是宏观视角,但游客真要去某条街,还是得靠精确的门牌索引。

为何常见:人们容易把“新增了图谱层”误解为“抛弃了原有的检索层”,忽略了 GraphRAG 架构中向量检索依然承担基础召回职责。

面试题

Q1. GraphRAG 相比传统 RAG 最核心的增强是什么?

单选题 · easy

  • A.引入知识图谱结构信息← 正确答案
  • B.增加模型参数量
  • C.使用更大规模语料
  • D.加快单次推理速度

答案:A。GraphRAG 的核心创新是先构建知识图谱再基于社区回答,让模型看见实体之间的结构关系,本质是引入图谱信息。增加参数、扩语料、加速推理都不是 GraphRAG 的增强方向。

Q2. 以下哪些属于 GraphRAG 索引阶段的关键步骤?

多选题 · mid

  • A.实体与关系三元组抽取← 正确答案
  • B.社区发现与层级聚类← 正确答案
  • C.社区摘要预计算生成← 正确答案
  • D.向量数据库实时写入

答案:ABC。索引阶段包含三步:抽取三元组、社区聚类、生成摘要。向量写入虽然存在于系统中,但它在朴素 RAG 中也有,不是 GraphRAG 索引的关键新增步骤,更谈不上“实时”。

Q3. GraphRAG 社区检测环节使用的核心算法是?

单选题 · mid

  • A.Leiden 层次社区发现← 正确答案
  • B.K-Means 聚类算法
  • C.线性回归拟合算法
  • D.决策树分类算法

答案:A。GraphRAG 使用图领域的 Leiden 算法做社区检测,它通过优化模块度发现社区结构。K-Means 是向量聚类,线性回归与决策树是监督学习模型,都不是图社区检测算法。

Q4. 传统 RAG 在回答全局性问题时的主要缺陷是?

单选题 · easy

  • A.检索片段相互割裂不完整← 正确答案
  • B.检索过程运行速度过慢
  • C.向量化模型参数量小
  • D.文档库容量不够庞大

答案:A。传统 RAG 只检索 Top-K 相似片段,片段之间没有关联,拼不出全局图景。“全面性不足”是它的核心缺陷。速度慢、参数量小、库容量小都不是它的典型问题。

Q5. 关于 GraphRAG 的社区摘要,下列说法正确的有?

多选题 · mid

  • A.摘要由 LLM 离线预生成← 正确答案
  • B.社区摘要分层逐级聚合← 正确答案
  • C.摘要缓存后查询时复用← 正确答案
  • D.摘要必须在线实时计算

答案:ABC。社区摘要在索引阶段由 LLM 离线生成,叶子摘要逐层聚合成上层摘要,并缓存到存储中供查询直接取用。D 说反了——摘要恰恰是离线预计算而非在线实时计算的。

Q6. 模块度 Q 在社区检测中的作用是?

单选题 · extreme

  • A.衡量社区划分结果的优劣← 正确答案
  • B.控制实体抽取的总数量
  • C.决定向量检索的召回率
  • D.计算摘要生成的消耗量

答案:A。模块度计算社区内实际连边数与随机期望连边数的差值,是 Leiden 算法优化目标,用来评判划分质量。它与实体抽取数量、向量召回率、摘要消耗量都无关,是纯粹的图划分质量指标。

Q7. GraphRAG 回答全局性问题时采用的策略是?

单选题 · easy

  • A.MapReduce 分而治之聚合← 正确答案
  • B.所有文档直接拼接回答
  • C.单一文档完整读取作答
  • D.随机抽取片段拼接作答

答案:A。全局问题走 Map-Reduce:Map 阶段让每个社区基于摘要独立作答,Reduce 阶段汇总去重。B、C、D 分别是暴力拼接、单文档读取、随机抽取,都不是 GraphRAG 的结构化全局策略。

Q8. 关于 GraphRAG 的索引成本,下列说法正确的有?

多选题 · hard

  • A.构建阶段 Token 消耗明显偏高← 正确答案
  • B.离线成本可在查询端摊销← 正确答案
  • C.局部查询收益并不显著← 正确答案
  • D.全局查询完全不需要检索

答案:ABC。三元组抽取和社区摘要生成需要大量 LLM 调用,Token 消耗高,但属于离线一次性成本,能被大量查询摊销。局部查询走朴素 RAG 就够,GraphRAG 优势不在此。D 错误——全局查询也要基于社区摘要做检索。

Q9. 下列场景中最适合使用 GraphRAG 的是?

单选题 · mid

  • A.大规模文档集全局趋势分析← 正确答案
  • B.单篇短文的精确关键词查找
  • C.低延迟高并发的事实点查询
  • D.内存受限的嵌入式终端部署

答案:A。GraphRAG 的价值在于跨越大量文档做全局综合分析,所以大规模文档集的趋势分析最适合。单点查找用朴素 RAG 更快,低延迟场景索引开销大不划算,嵌入式终端跑不动 LLM 抽取。

Q10. 实体抽取后进行共指消解的主要目的是?

单选题 · mid

  • A.合并称呼不同实为同一对象← 正确答案
  • B.减少图内边的总数量
  • C.提升向量检索相似度
  • D.降低社区聚类的复杂度

答案:A。共指消解把“乔布斯”“史蒂夫·乔布斯”"Jobs"合并为同一实体节点,核心目的是解决同一对象多种称呼造成的图谱分裂问题。减少边数、提升向量相似度、降低聚类复杂度都是合并后的附带效应,而非主要目的。