假设公司积累了一万份会议纪要、项目周报和供应商邮件。现在问:
项目 A 为什么延期?涉及哪些供应商?其他项目有没有相同风险?
答案可能不在同一份文档里。周报只写了项目 A 等待电池,邮件说电池厂依赖某家芯片公司,另一份纪要又提到项目 B 使用同款电池。普通 RAG 能找到与“项目 A 延期”相似的几段文字,却不一定顺着供应链把这些分散的事实串起来;如果问“所有项目有哪些共同风险”,问题本身甚至没有足够明确的关键词,向量检索很难知道应该取哪几段。
本文分析的对象,是 Microsoft 开源的 GraphRAG 仓库及其发布到 PyPI 的 graphrag Python 包。下文所说的 GraphRAG,均指这套具体实现,而不是泛指所有“知识图谱 + RAG”方案。代码行为以仓库中的 3.1.2 版本为准。
Microsoft GraphRAG 想解决的,就是这种跨文档关联和全局归纳问题。 它先用 LLM 从原始文本中抽取实体与关系,组成知识图谱;再用图算法发现关系紧密的社区,为每个社区预先生成摘要。查询时,它可以从某个实体向外找证据,也可以在社区摘要之上回答整个资料库的问题。
这不是一种新模型,也不需要训练模型参数。它是一个 Python 数据处理与查询工具包:模型负责抽取和总结,图算法负责分组,检索代码负责选择上下文。
Microsoft 在 GraphRAG 仓库的 README 中把它定位为研究项目,并说明项目已经基本进入维护状态,主要处理缺陷、依赖更新和安全问题。它适合直接使用和二次集成,但不是带用户、权限、上传页面和聊天记录的完整知识库产品。
普通 RAG 为什么不总够用
传统 RAG 的主干很短:
文档切块 → 生成向量 → 用问题检索相似文本块 → LLM 根据文本块回答
它擅长定位具体事实,例如“退款期限是多少”“某个接口如何配置”。困难通常出现在两类问题上:
- 关系问题:某个人、公司、项目和其他对象有什么联系?影响是怎样传递的?
- 全局问题:整个资料库有哪些主题、共同风险或主要观点?
向量检索衡量的是语义相似度,不等于事实链完整度。查询“项目 A 为什么延期”时,一段只写“芯片厂减产”的文字可能并不像问题,却恰好是原因链的重要一环。查询“资料里最重要的五个主题”时,也不存在一段天然与这个问题最相似的原文。
GraphRAG 的办法是把这些关系提前整理出来,并把全库内容预先压缩成不同层级的社区报告。
建索引:先把文档整理成图和摘要
Microsoft GraphRAG 3.1.2 的标准索引流程,可以简化成下面这张图:
flowchart TD
docs[原始文档] --> chunks[按 token 切成文本块]
chunks --> extract[LLM 逐块抽取实体与关系]
extract --> merge[合并重复实体与关系]
merge --> graph[形成知识图谱]
graph --> cluster[Leiden 分层社区发现]
cluster --> reports[LLM 生成社区报告]
chunks --> embed[生成文本块向量]
merge --> embed
reports --> embed
embed --> store[(向量存储)]
chunks --> tables[(结构化表)]
merge --> tables
cluster --> tables
reports --> tables
第一步:文档切块
长文档不能整份交给模型抽取。GraphRAG 3.1.2 默认按 token 切块,配置是:
chunking:
type: tokens
size: 1200
overlap: 100
size: 1200 表示每块最多 1200 token;overlap: 100 表示相邻块重复 100 token,减少一句话刚好被截断造成的信息损失。切块结果成为 text_units。
块越小,单次抽取越聚焦,但模型调用数量会增加,也更容易把跨段关系切开;块越大,上下文更完整,噪声和单次调用成本也会增加。修改配置后需要重新建立索引,旧文本块不会自动变化。
第二步:逐块抽取实体和关系
假设原文是:
星河公司的项目 A 使用远航公司供应的电池。远航公司的生产依赖海岳公司的芯片。
LLM 可能抽取出三个组织或项目实体,以及两条关系:
项目 A → 使用电池 → 远航公司
远航公司 → 依赖芯片 → 海岳公司
不同文本块可以并发处理。抽取完成后,系统按名称和类型合并重复实体,按关系两端合并重复关系,同时保留来源文本块 ID。这样一条图关系仍能追溯到原文。
这里有一个无法绕开的质量边界:图谱是 LLM 从文本中生成的,不是天然正确的数据。 别名可能没有合并,同名对象可能被误合并,隐含关系也可能漏掉。提示词、实体类型、切块策略和原始数据质量都会影响结果。
第三步:发现社区并提前总结
有了实体和关系,系统会把图当成无向加权网络,使用分层 Leiden 算法寻找连接紧密的实体群。例如项目、供应商、零件和负责人频繁互相关联,就可能被归入一个社区;大的社区还可以继续拆成小社区。
随后,LLM 为每个社区生成标题、摘要、关键发现、完整报告和重要度评分。可以把它理解成:
系统先为整个资料库整理出多级目录,再为每个章节写好内容提要。
这一步使 Global Search 不必在提问时从随机找到的几段原文推测全局,而可以基于覆盖全库不同区域的报告进行归纳。代价是建索引时会产生较多模型调用,而且摘要一定会压缩和丢失细节。
索引数据由谁生成,存在哪里
这些表和向量不是用户手工创建的。运行 graphrag index 后,Microsoft GraphRAG 的索引流水线会自动切块、抽取、汇总并写入存储;用户只需要准备输入文档、配置模型和存储位置。GraphRAG 默认不要求 Neo4j,图结构可以直接表示成实体表和关系表:实体是一行节点,关系是一行边。
| 表 | 可以把它理解成 | 查询时的作用 |
|---|---|---|
documents |
原始文档档案 | 保存整份内容和来源 |
text_units |
带编号的原文摘录 | 给答案提供原始证据 |
entities |
人物、公司、项目等对象名单 | 作为图节点和 Local Search 的入口 |
relationships |
对象之间的关系清单 | 描述谁依赖谁、谁影响谁,是图的边 |
communities |
联系紧密的对象分组 | 建立多层主题结构 |
community_reports |
每个分组的预生成报告 | 为 Global 和 DRIFT 提供全局背景 |
covariates |
可选的声明或主张 | 保存主体、客体、状态和来源等扩展信息 |
例如,entities 中可能有“项目 A”和“远航公司”;relationships 中则有一行 项目 A → 远航公司,描述项目使用其电池,并记录这条关系来自哪些 text_units。这些 ID 关联的意义,是让系统既能沿图找相关内容,也能返回原始证据。
在 graphrag init 生成的默认配置中,结构化结果写入项目根目录的 output/,例如 output/entities.parquet、output/relationships.parquet 和 output/community_reports.parquet;向量数据库 URI 默认是 output/lancedb。这些目录和文件由 GraphRAG 自动创建,不需要用户先建表。
GraphRAG 3.1.2 默认会生成三类向量:
| 向量索引 | 向量化的内容 | 主要使用者 |
|---|---|---|
text_unit_text |
原文文本块 | Basic Search |
entity_description |
实体名称与描述 | Local Search 的实体定位 |
community_full_content |
社区报告全文 | Global / DRIFT 的社区检索 |
关系没有单独的关系向量索引。 Local Search 先用实体向量找到入口,再从关系表中按图连接、关系数量、权重或其他排名字段筛选关系;它不是把每条关系单独做 Embedding 后再相似度检索。
默认情况下,结构化表写成 Parquet 文件,向量写入 LanceDB。它们都是本地持久化数据,不需要另起数据库服务。GraphRAG 3.1.2 内置的存储包括:
| 数据类型 | 内置选择 |
|---|---|
| 结构化表 | Parquet、CSV、Cosmos DB |
| 向量索引 | LanceDB、Azure AI Search、Cosmos DB |
SQLite、MySQL 和 PostgreSQL 没有现成适配器,但并非不能接。项目提供注册自定义 TableProvider 和 VectorStore 的工厂接口。只想把结果用于自己的业务系统,也可以先生成 Parquet,再导入关系数据库;数组和对象字段需要映射为 JSON 或拆成关联表。
如果只用 Basic,可以把 embed_text.names 改成只包含 text_unit_text,避免为实体和社区报告生成不必要的向量;但索引工作流也要相应裁剪,否则仍可能生成不用于 Basic 的结构化结果。用户账号、文档权限、租户隔离和聊天记录不属于 GraphRAG 索引,需要应用层另外保存。
查询时有四条路,不只有 GraphRAG
graphrag 包同时提供 Basic、Local、Global 和 DRIFT。它们不是默认一起执行的四路召回,而是四种可选择的查询模式。
| 模式 | 使用的数据 | 适合的问题 |
|---|---|---|
| Basic | 文本块向量 | 某个具体事实或原文内容 |
| Local | 实体、关系、原文、社区报告 | 某个对象及其关联关系 |
| Global | 某一层级的社区报告 | 全库主题、趋势和共同风险 |
| DRIFT | 社区报告 + 多轮 Local Search | 需要从概览逐步深入的复杂问题 |
Basic:GraphRAG 包里确实有普通 RAG
Basic Search 就是通用的向量 RAG:把用户问题转成向量,从文本块向量库取 Top K,将能放进 token 预算的原文拼入提示词,再让 LLM 回答。
默认 k 为 10,上下文预算为 12000 token。它只有一条文本向量召回路径。GraphRAG 3.1.2 的 Basic Search 没有默认加入:
- BM25 或全文关键词召回;
- LLM 查询改写、纠错和同义词扩展;
- 把问题生成多个问法后分别检索;
- 专用 Cross-Encoder / Reranker 二次排序;
- 将关键词、向量和图谱结果统一融合。
因此,Basic 是可用的传统 RAG 基线,不是一套已经做满查询优化的企业级混合检索管线。 在 3.1.2 的 BasicSearchContext 实现中,向量结果先被转换成 ID 集合,再按原始文本块顺序取回;当上下文预算不足时,不应假设最后保留的片段严格维持相似度排名。
Local:从相关实体向外收集证据
Local Search 先用问题向量匹配实体描述,把最相关的实体作为图入口,再围绕这些实体收集多种上下文:
问题 → 匹配相关实体
├─ 相邻实体与关系
├─ 包含这些实体的原文片段
├─ 所属社区的报告
└─ 可选声明数据
↓
排序、裁剪、回答
它确实会汇集多种资料,也会根据匹配实体数量、关系属性、社区排名等规则排序。不过,这更准确地说是围绕实体的多来源上下文构建,不是多个独立检索器召回后做统一融合。
Local 对“项目 A 为什么延期”“远航公司影响了哪些项目”更合适。它还可以把有限轮次的历史用户问题拼进当前检索文本,帮助多轮对话,但这仍不是一次独立的 LLM 查询改写。
Global:把社区报告 Map-Reduce
Global Search 面向“整个资料库在讲什么”这类问题。它选择指定层级的社区报告,分批交给 LLM:每批生成带重要度的中间要点,再过滤、汇总成最终回答。
社区报告分批 → 各批生成并评价要点 → 筛选重要要点 → 最终回答
社区层级会直接影响结果。更细的层级包含更多细节,也意味着更多报告、更高延迟和更多模型消耗。这里的评分与筛选发生在中间答案层,不等于对原始文本块使用专门的 Reranker。
DRIFT:先看全局,再自动追问局部
DRIFT 是 Dynamic Reasoning and Inference with Flexible Traversal。它先匹配相关社区报告,生成一个初步回答和若干后续问题,然后通过 Local Search 逐步探索,例如:
“项目延期由什么造成?”
↓
初步判断 + 后续问题
├─ 项目依赖哪些供应商?
├─ 供应商遇到了什么问题?
└─ 哪些原文能够证明影响?
↓
执行局部检索并继续展开
↓
整合最终答案
四种模式中,DRIFT 最接近“查询扩展 + 多次检索”。它通常比单次 Local 更全面,也会带来更多模型调用和等待时间。
失败重试、缓存和增量更新
模型请求默认支持重试。初始化生成的配置使用指数退避策略,可以分别给聊天模型和 Embedding 模型指定次数、基础等待时间、最大等待时间和随机抖动:
completion_models:
default_completion_model:
retry:
type: exponential_backoff
max_retries: 5
base_delay: 2.0
max_delay: 30.0
jitter: true
这里重试的是失败的单次模型请求,不是整个索引任务自动从断点继续。认证错误、参数错误、上下文超长等异常会直接失败;3.1.2 的跳过列表还包含一部分连接和服务异常,所以不能把它理解成“任何网络故障都必然重试”。
GraphRAG 还会缓存成功的模型结果。任务失败后重新运行,如果请求内容和配置一致、缓存仍然存在,之前成功的调用可能被复用,从而减少重复费用。它也提供增量索引流程,用于将新增或变化的数据与旧索引合并;这仍需要应用层决定何时触发,并处理数据版本和发布策略。
实际怎么用
Microsoft GraphRAG 最直接的交付物,是可安装的 graphrag Python 包和同名 CLI。最快的方式是先使用 CLI 初始化配置、建立索引和验证查询:
python -m pip install graphrag
mkdir graphrag-demo
cd graphrag-demo
graphrag init
初始化会生成 settings.yaml、.env、input 和提示词目录。配置模型与密钥,把文本资料放进 input 后执行:
graphrag index
graphrag query "资料中的主要风险是什么?" --method global
graphrag query "项目 A 为什么延期?" --method local
graphrag query "项目 A 为什么延期?" --method basic
集成到自己的后端时,可以调用 graphrag.api 暴露的 build_index、basic_search、local_search、global_search 和 drift_search。查询 API 接收配置及对应的索引表,返回回答与本次使用的上下文数据。
应用与 GraphRAG 的职责边界大致是:
| GraphRAG 负责 | 业务应用负责 |
|---|---|
| 文档切块与索引流水线 | 文件上传、格式清洗和任务调度 |
| 实体关系抽取、社区发现与报告 | 用户、租户和文档权限 |
| 向量、局部、全局及 DRIFT 查询 | 聊天界面、会话保存和审计 |
| 索引缓存和增量更新能力 | 数据版本、发布、监控和成本控制 |
Microsoft GraphRAG 仓库中的 unified-search-app 是 Streamlit 演示程序,可以加载多个已经生成的 GraphRAG 索引、对比不同查询结果并查看社区报告。它适合观察效果,不是 GraphRAG 的核心存储或生产服务层。
什么时候值得用,什么时候普通 RAG 就够了
GraphRAG 的价值取决于问题,而不只取决于文档数量。
| 场景 | 更合适的起点 |
|---|---|
| 找一条明确事实、一个配置项 | Basic 或其他普通 RAG |
| 查询人物、公司、项目之间的联系 | Local |
| 概括全库主题、趋势、共同风险 | Global |
| 复杂问题需要先概览再逐步查证 | DRIFT |
| 资料很少、关系简单、预算有限 | 先用普通 RAG |
它的主要成本发生在索引阶段:逐块抽取、实体描述汇总、社区报告和向量都会消耗模型资源。更新频繁的数据还需要持续维护索引。图谱抽取与摘要也可能出错,因此重要结论仍应回到原文引用核验。
所以,Microsoft GraphRAG 最准确的定位是:一个以图谱关联和全局归纳为核心、同时附带普通向量 RAG 的知识库索引与查询工具包。 graphrag 包已经实现了这套方法最主要的算法流程,可以直接拿来做原型或嵌入后端;但关键词混合检索、专用重排序、细粒度权限、业务数据库和产品界面,仍然属于接入方要补上的工程层。
没有 GraphRAG,知识库问答照样能做。区别在于,当问题开始要求跨文档串联关系、总结整个资料库时,普通 RAG 往往需要自己继续补查询改写、多路召回、图遍历和分层总结;GraphRAG 把其中“图与社区”这一条路线,做成了一套现成流程。
视频介绍
如果想先通过视频快速了解 GraphRAG,也可以参考这个 B 站介绍:
