Hi,大家好,我是Ivan。
终于到了 RAG 的第四节,我们之前做 RAG 的时候,查询这个部分一直是从向量数据库中找出几个相关的 Chunk,再交给大模型来生成答案。如果只是让它回答一些比较具体的问题,那没啥大问题。但是,如果要总结一批在公司里共同出现的风险报告,那答案可能会分散在好几十份文件中,你要是只取几个相似的 Chunk ,明显是不够的。

所以,到了 RAG 的第四节,我打算讲讲 GraphRAG。它会从文档中提取实体和关系,构建知识图谱,然后根据问题来使用 Local Search、Global Search 或 DRIFT Search。后面我们把它的索引和查询流程跑一遍,再用 Gephi 看一下生成的知识图谱。
一、GraphRAG 解决了什么问题
在正式开始前,我们不妨想想这俩个问题。
txt
问题 1:A 公司收购了哪家公司?
问题 2:这些报告反映出的主要竞争格局和共同风险是什么?
第一个问题中包含明确的公司实体。相关内容通常集中在少数文本块里,只用普通向量检索也可能找到答案。
第二个问题没有给出具体实体,问的是整个资料集。向量检索就只能寻找和竞争格局、风险语义接近的 Chunk,很难知道哪些内容在整批文档中反复出现,也不会主动把不同公司、产品和事件之间的关系串起来。
而 GraphRAG 在原始文本和最终回答之间多加了一层图结构:
txt
原始文档
↓
文本切分
↓
实体、关系和声明提取
↓
知识图谱
↓
社区划分和社区报告
↓
Local / Global / DRIFT 查询
这里的社区,你可以理解成知识图谱中联系比较紧密的一组实体。比方说,某个社区围绕一家公司和它是产品,另一个社区围绕供应链和合作伙伴。GraphRAG 会继续给社区生成摘要,查询时就不必每次从所有原始文本重新归纳主题。

二、索引阶段生成了哪些内容
GraphRAG 的查询方式看起来比较多,但它们用的都是索引阶段已经整理好的数据。我们先把这部分弄清楚,之后的 Local Search 和 Global Search 就容易理解了。
1. 文本切分
输入文档会先被切成 Text Unit。它和普通 RAG 中的 Chunk 接近,保存原文内容、Token 数量、来源文档,以及与它关联的实体和关系。
Text Unit 仍然很重要。知识图谱负责找到相关实体和关系,最后回答问题时还需要回到原始文本,不能只根据图中的名称和摘要生成答案。
2. 提取实体和关系
GraphRAG 会从每个文本块中提取实体,例如人物、组织、地点和事件,同时整理实体之间的关系。
假设原文是:
txt
星云科技在 2025 年收购了云帆数据,随后把云帆的数据治理产品并入企业服务部门。
索引阶段可能得到下面这些内容:
txt
实体:星云科技、云帆数据、数据治理产品、企业服务部门
关系:星云科技 → 收购 → 云帆数据
关系:数据治理产品 → 并入 → 企业服务部门
每条实体和关系还会保存对应的 Text Unit ID。后面查询到云帆数据时,可以继续找到这句话所在的原始文本。
3. 使用 Leiden 划分社区
实体和关系组成图以后,GraphRAG 会使用 Leiden 社区检测算法,把联系紧密的节点划到一起,并继续生成分层社区。
社区层级越高,划分得越细,包含的实体通常越少。当前 CLI 默认使用 community-level=2。如果把层级调得更细,查询时可以拿到更具体的社区报告,不过报告数量也会增加。

Level 0 的范围较大,层级数字增加以后,社区会继续向下细分。
4. 生成社区报告
只有社区编号还不够,GraphRAG 会根据社区中的实体和关系生成报告。报告里包含标题、摘要、完整内容、重要发现和评分等信息。
全局检索主要使用这些社区报告。本地检索也会把与命中实体相关的社区报告放进上下文。

Documents、Text Units、Entities、Relationships 和 Community Reports 通过 ID 互相连接。
三、准备一个 GraphRAG 项目
GraphRAG 当前要求 Python 3.10 到 3.12。先创建虚拟环境并安装:
bash
mkdir graphrag-demo
cd graphrag-demo
python -m venv .venv
Windows PowerShell:
powershell
.venv\Scripts\Activate.ps1
macOS 或 Linux:
bash
source .venv/bin/activate
安装 GraphRAG:
bash
python -m pip install graphrag
在当前目录初始化:
bash
graphrag init
初始化时会让你选择对话模型和 Embedding 模型。完成以后,目录中会出现下面这些内容:
txt
graphrag-demo/
├─ .env
├─ settings.yaml
└─ input/
.env 用来保存 API Key,settings.yaml 是索引和查询配置,待处理的文本文件放进 input 目录。
打开 .env,填入实际的 Key:
txt
GRAPHRAG_API_KEY=你的_API_Key
这里先使用初始化时生成的默认配置。GraphRAG 目前也能通过 LiteLLM 接入其他模型,不过图谱提取依赖结构化输出。如果模型经常返回不完整的 JSON,索引会在这一阶段出问题。
为了方便观察 Local 和 Global 的差别,建议准备几份主题相关、内容又不完全相同的文本。例如公司年报、产品资料和新闻稿。只有几段互不相关的短文本时,也能运行,不过很难看出社区和全局检索的作用。
四、建立知识图谱索引
把 .txt 文件放进 input 后运行:
bash
graphrag index
如果项目目录不在当前路径,也可以指定根目录:
bash
graphrag index --root E:\rag-project\graphrag-demo
索引过程中会调用模型提取实体和关系、生成描述、划分社区并生成社区报告。相比只做 Embedding 的普通 RAG,这一步明显会更慢,也会产生更多模型调用,所以第一次不要直接放入大量文档。
完成以后,output 目录中会出现一组 Parquet 文件。常用的几项如下:
| 文件 | 保存的内容 |
|---|---|
documents.parquet |
原始文档及其 ID |
text_units.parquet |
切分后的文本块和来源信息 |
entities.parquet |
实体名称、类型、描述和关联文本 |
relationships.parquet |
实体之间的关系、描述和权重 |
communities.parquet |
社区层级、父子关系和成员 |
community_reports.parquet |
社区标题、摘要、发现和评分 |
covariates.parquet |
可选的声明或协变量信息 |
covariates 不是每个项目都会使用。默认的声明提取关闭时,输出中可能没有这部分内容。
这些表通过 ID 互相连接。比如实体记录中保存 text_unit_ids,Text Unit 里也保存相关实体和关系的 ID。查询阶段找到实体以后,才能继续追溯到原文。
五、Local Search:查询具体实体
Local Search 适合回答对象比较明确的问题,例如:
txt
云帆数据被收购以后,产品和团队发生了哪些变化?
运行命令:
bash
graphrag query \
"云帆数据被收购以后,产品和团队发生了哪些变化?" \
--method local
Windows PowerShell 可以写成一行:
powershell
graphrag query "云帆数据被收购以后,产品和团队发生了哪些变化?" --method local

Local Search 大致会经过下面几步:
- 把问题和对话历史作为查询输入。
- 根据实体描述的 Embedding,找到与问题语义接近的实体。
- 沿着这些实体取得相邻实体、关系、社区报告和可选的声明信息。
- 根据实体和 Text Unit 的映射,找回对应的原始文本块。
- 对候选内容排序和过滤,在上下文窗口允许的范围内交给大模型生成答案。
这里不是只查知识图谱。结构化的实体、关系和社区报告负责扩展线索,原始 Text Unit 负责提供具体内容。两部分会一起进入最终上下文。
假设问题命中了云帆数据,图谱中还保存了它与"星云科技""数据治理产品""企业服务部门"的关系。Local Search 可以沿着这些关系继续查,再取回相关原文。普通向量检索如果只命中其中一段,其他关系可能就不会进入 Top K。
Local Search 也不等于范围一定很小。命中实体连接了很多节点时,它仍然会拿到较多候选内容,只是最终会按照 Token 预算进行筛选。
六、Global Search:回答整个资料集的问题
下面换一个问题:
txt
这些资料反映出的主要业务方向、竞争压力和共同风险是什么?
这个问题没有指定某个实体,需要综合多个主题。使用 Global Search:
bash
graphrag query \
"这些资料反映出的主要业务方向、竞争压力和共同风险是什么?" \
--method global

Global Search 不会先找某一个实体。它从指定层级读取社区报告,再通过 Map-Reduce 生成答案。
1. Map 阶段
社区报告会被打乱并分成若干批。每一批连同用户问题一起交给大模型,生成中间回答。
中间回答通常不只是几段文字,还会给各个要点附上重要性评分。某一批社区中没有与问题相关的内容,也可以返回低分结果。
例如三个批次可能分别得到:
txt
批次 1:供应链集中度较高,重要性 82
批次 2:企业服务收入增长,重要性 67
批次 3:没有发现与问题直接相关的信息,重要性 0
Map 阶段可以并行处理,所以文档很多时不需要把全部报告一次塞进同一个上下文窗口。
2. Reduce 阶段
GraphRAG 会过滤低分内容,把保留下来的中间回答按重要程度整理,再调用一次大模型生成最终答案。
因此,Global Search 的调用次数通常多于 Local Search。它能覆盖更多社区,不过耗时和 Token 成本也会更高。

Map 阶段并行生成带评分的中间回答,Reduce 阶段再过滤、排序和汇总。
社区层级会直接影响结果。CLI 可以这样指定:
bash
graphrag query \
"这些资料中反复出现了哪些风险?" \
--method global \
--community-level 2
层级数字越大,社区越小,报告通常也越细。细粒度报告可能带来更完整的内容,同时也会增加待处理的报告数量。这里没有固定的最佳值,要根据数据规模和问题类型测试。
七、DRIFT Search:先看方向,再继续向下查
Local Search 从实体出发,细节比较足,但开头找错实体以后,后面的范围也会受到影响。Global Search 能看到整个资料集,回答又容易停留在概括层面。
DRIFT 是 Dynamic Reasoning and Inference with Flexible Traversal。它把社区信息加入本地检索,先确定问题可能涉及哪些主题,再生成后续问题向更具体的实体和原文扩展。
运行方式如下:
bash
graphrag query \
"星云科技的收购策略怎样影响它在企业服务市场中的位置?" \
--method drift
它的查询过程可以分成三部分:
1. Primer :从语义最相关的若干社区报告中生成一个较宽的初步回答,同时提出后续问题。 2. Follow-Up :针对后续问题执行本地检索,继续取得实体、关系和原文细节。 3. Output:整理多轮查询形成的问答层级,按相关程度生成最终结果。
上面的示例既问到"星云科技"这个具体实体,又要求判断它在市场中的位置。只做 Local Search 可能局限在收购事件附近,只做 Global Search 又可能把公司本身的细节冲淡,所以这类问题更适合用 DRIFT 试一下。
不过,DRIFT 会展开后续查询,模型调用和等待时间通常也会增加。它不是 Local 和 Global 的固定替代品,只有问题确实需要在全局主题和局部细节之间来回查找时,才有必要使用。
八、三种查询方式怎么选
| 查询方式 | 从哪里开始查 | 适合的问题 | 主要代价 |
|---|---|---|---|
| Local Search | 相关实体及其邻居 | 某个人、公司、产品或事件的具体问题 | 覆盖范围受命中实体影响 |
| Global Search | 某一层级的社区报告 | 整批资料的主题、趋势、共同风险 | 模型调用多,耗时和成本较高 |
| DRIFT Search | 相关社区,再逐步进入局部 | 同时需要整体背景和实体细节的问题 | 查询过程更长,参数也更多 |

实际使用时,可以先看问题中有没有清楚的实体。对象明确时先用 Local;问"整体有哪些""主要趋势是什么"时用 Global;问题同时要求背景、关系和细节,再考虑 DRIFT。
GraphRAG 的 CLI 还提供了 basic 方法,它更接近普通的文本向量检索。想检查图检索是否真的带来了改善时,可以拿它做一组对照。
bash
graphrag query "云帆数据的主要产品是什么?" --method basic
不要只看某一次回答是否顺眼。最好准备一批已知答案的问题,分别运行 basic、local、global 和 drift,检查引用范围、遗漏情况、耗时和模型用量。
九、使用 Gephi 查看知识图谱
Parquet 文件适合程序读取,但不方便直接观察图谱有没有提取错。GraphRAG 可以输出 graph.graphml,再使用 Gephi 查看实体、关系和社区。

Gephi 可以按照社区着色,并通过节点和连线检查实体关系。
先在 settings.yaml 中启用 GraphML 快照:
yaml
snapshots:
graphml: true
重新运行索引后,在 output 目录找到:
txt
graph.graphml
安装并打开 Gephi,导入这个文件。刚打开时通常只会看到一团节点和连线,需要再做几步处理。
1. 安装 Leiden 插件
打开 Tools → Plugins,搜索 Leiden Algorithm,安装后重启 Gephi。
进入右侧 Statistics,分别运行 Average Degree 和 Leiden Algorithm。Leiden 的 Quality Function 可以选择 Modularity,Resolution 先使用 1。
2. 按社区着色
在左侧 Appearance 中选择:
txt
Nodes → Partition → Cluster
生成调色板并点击 Apply。同一种颜色代表被 Leiden 划进同一社区的一组节点。这样可以比较直观地看到资料里形成了哪些主题团块。
3. 按连接数量调整节点大小
再进入:
txt
Nodes → Ranking → Degree
设置最小和最大尺寸后应用。连接数量较多的实体会显示得更大,可以用来检查哪些节点处于图谱中心。
节点大不等于它一定最重要,只表示它在当前图中连接得比较多。公司名称、通用地点或者频繁出现的词都可能成为大节点,还要结合实体描述和原文判断。
4. 调整布局
最后可以使用 ForceAtlas2 等布局,让联系紧密的节点靠近,联系较弱的节点拉开。节点太多时先关闭标签,布局稳定以后再显示主要实体名称。
Gephi 的作用主要是检查和分析。例如某个实体被拆成了两个名称,某个社区混入了明显无关的节点,或者整个图都被一个过于宽泛的实体连在一起,都说明实体提取、关系提取或提示词还需要调整。它不会直接提高查询质量,也不是 GraphRAG 运行所必需的组件。
十、GraphRAG 的成本和限制
GraphRAG 最费资源的地方通常不是查询,而是第一次建立索引。每个文本块都可能涉及实体和关系提取,后面还要生成实体描述和社区报告。文档数量和文本长度增加以后,时间和 API 费用都会跟着增加。
它对频繁更新的数据也不算轻便。GraphRAG 现在提供 update 命令和更新索引方式:
bash
graphrag update
但新增文档可能带来新的实体、关系和社区结构,更新不只是把一个向量写进数据库这么简单。资料每天大量变化时,需要单独评估更新频率、索引成本和一致性。

GraphRAG 建立索引时需要多次调用模型,文档更新以后,实体、关系和社区也可能发生变化。
另外,知识图谱是模型从文本中提取出来的,并不天然等于事实。实体合并错误、关系方向错误、社区划分不合理,都会继续影响查询。正式使用以前,至少要抽查 entities、relationships 和 community_reports,再用一批固定问题做回归测试。
如果知识库规模不大,问题大多能在一个或两个 Chunk 中找到答案,普通向量 RAG 会更省事。GraphRAG 更适合答案分散在多份资料中,需要分析实体关系,或者经常要回答全局性问题的场景。
十一、总结
GraphRAG 建好索引以后,同一份资料可以按照问题选择不同的查询方式:具体实体用 Local Search,整批资料的主题和趋势用 Global Search,需要同时兼顾全局背景和局部细节时再用 DRIFT。Gephi 可以把实体、关系和社区直接展开,方便检查图谱是否真的按照资料内容构建出来。