微信扫码
添加专属顾问
我要投稿
深入理解GraphRAG技术,从图的构建到查询的完整指南。核心内容:1. GraphRAG技术概述及其在知识图谱检索增强生成中的应用2. 图创建和查询的关键步骤,包括实体提取和社区划分3. 实际案例分析:使用GraphRAG对《Penitencia》书籍进行索引和查询的过程
也许你已经看到过论文From Local to Global: A GraphRAG Approach to Query-Focused Summarization,这是微软研究院在使用知识图谱进行检索增强生成(RAG)方面的观点。也许你感觉论文中某些部分有些模糊。也许你希望文档更详细地解释信息是如何从图中检索的。如果你有这样的感觉,就继续阅读吧!
在这篇文章中,我们将详细描述GraphRAG过程的每一步。你甚至会学到论文中完全没有提到的一种搜索方法(局部搜索)。
一句话来说,GraphRAG是对检索增强生成的一种增强,它利用图结构。
存在多种实现方式,这里我们将重点介绍微软的方法。它可以分为两个主要步骤:图创建(即索引)和查询(其中包含三种可能性:本地搜索、全局搜索和漂移搜索)。
我将使用一个现实世界的例子来带您逐步了解图创建、本地搜索和全局搜索。那么,无需多言,让我们使用 GraphRAG 对 Pablo Rivero 的书 Penitencia 进行索引和查询。
关键的 GraphRAG 步骤:图创建和图查询
GraphRAG 文档 会引导您完成项目设置。一旦您初始化了工作区,您将在 ragtest 目录中找到一个配置文件(settings.yaml)。
我已将书《Penitencia》添加到输入文件夹中。对于本文,我保留了配置文件不变,以使用默认的设置和索引方法(IndexingMethod.Standard)。
要创建图,请运行:
graphrag index --root ./ragtest
这会触发两个关键操作,从源文档中提取实体和将图划分为社区,如GraphRAG项目中工作流目录中的模块所定义。
实现实体提取和将图划分为社区的模块。数字(黄色)显示执行顺序。
在 create_base_text_units 模块中,文档(在我们的情况下,是书 Penitencia)被分割成较小的 N 个标记块。
《Penitencia》书的前五个块。每个块长度为 1200 个标记,并具有唯一的 ID
2. 在 create_final_documents 中,创建了一个查找表,将文档映射到其关联的文本单元。每一行代表一个文档,由于我们只处理一个文档,因此只有一行。
表格显示所有文档及其ID。对于每个文档,列出所有相关的片段(即文本单元),并按其ID进行显示。
3. 在 extract_graph 中,每个片段都使用来自OpenAI的LLM进行分析,以提取实体和关系,该过程遵循此 提示。
在此过程中,可能会出现重复的实体和关系。例如,主要人物 Jon 在82个不同的文本片段中被提及,因此他被提取了82次——每个片段一次。
实体表的快照。实体按实体标题和类型进行分组。实体 Jon 被提取了82次,这可以从频率列中观察到。text_unit_ids 和 description 列分别包含82个ID和描述,分别显示 Jon 在哪些片段中被识别和描述。默认情况下,有四种实体类型(Geo、Person、Event 和 Organization)。
关系表快照。关系按源实体和目标实体进行分组。对于乔恩和西莉亚,描述和text_unit_ids列中各包含14个条目,表明这两位角色在14个不同的文本片段中被识别出关系。权重列显示的是LLM分配的关系强度总和(权重不是源节点和目标节点之间的连接数!)。
通过将实体按标题和类型分组,将关系按源节点和目标节点分组,尝试进行去重。然后,LLM被提示通过分析所有出现的较短描述来为每个唯一实体和唯一关系编写详细描述(见 prompt)。
实体表快照,包含最终的实体描述(由所有提取的短描述组成)。
关系表快照,包含最终的关系描述(由所有提取的简短描述组成)。
如您所见,去重有时并不完美。此外,GraphRAG不处理实体消歧(例如,Jon 和 Jon Márquez 将作为独立的节点,尽管它们指的是同一个人)。
4. 在 finalize_graph 中,使用 NetworkX 库来表示实体和关系,作为图的节点和边,包括结构信息,如节点度数。
最终实体表快照,其中每个实体代表图中的一个节点。一个节点的度数是它所拥有的边的数量(即它连接到的其他节点的数量)。
最终关系表快照,其中每个关系代表图中的一个边。一条边的 combined_degree 表示源节点和目标节点度数的总和。一条具有高 combined_degree 的边很重要,因为它连接了高度连接的节点。
我发现自己通过实际看到图表来理解更有帮助,因此我使用Neo4j进行了可视化(notebook):
《Penitencia》这本书使用Neo4j进行可视化
实体“Jon”及其关系使用Neo4j进行可视化
Laura和Mario之间的关系以Neo4j图边的形式进行可视化
5. 在_create_communities_中,使用Leiden算法将图划分为社区,这是一种层次聚类算法。
一个社区是一组节点,这些节点彼此之间比与其他图中的节点关系更紧密。Leiden算法的层次特性允许检测不同特异性水平的社区,这体现在其层级上。层级越高,社区越具体(例如,层级3相当具体,而层级0是一个根社区,非常通用)。
社区表快照。社区 0 是一个级别 0 的社区,因此它是一个根社区(没有父社区)。它有两个子社区,如子节点列所示。所有关系、文本单元和实体都被社区所包含,分别列在相应的列中。大小列显示该社区由 131 个实体组成。
如果我们把每个社区视为一个节点,包括属于该社区的实体,就可以看出聚类情况。
过滤掉 IN_COMMUNITY 关系后,Penitencia 图显示了 15 个根级别社区(红色圆圈)。
社区的价值在于它们能够将来自广泛来源的信息(如实体和关系)结合起来,从而提供整体视角。对于书籍而言,社区可以揭示文本中的总体主题或话题,如步骤 8 中所见。
Neo4j 中三个层级连接的社区可视化:社区 2(Celia Gómez 和 Tetuán 事件)—— [父级]→ 社区 23(Celia 的绝望与家庭暴力)—— [父级]→ 社区 42(Celia 与 Laura 的斗争)。排名是 LLM 分配给社区的重要性,从 1(重要性最低)到 10(重要性最高)。
6. 在 create_final_text_units 中,步骤 1 中的文本单元表将实体 ID、关系 ID 和协变量 ID(如有)映射到每个文本单元 ID,以便更方便地查找。
最终文本单元表快照
协变量本质上是声明。例如,“Celia 杀死了自己的丈夫和孩子(疑似)。” LLM 根据此 提示 从文本单元中推断出这些协变量。默认情况下,协变量不会被提取。
7. 在 create_community_reports 中,LLM 为每个社区撰写报告,详细说明其主要事件或主题,并提供报告摘要。LLM 由此 提示 引导,并接收所有来自社区的实体、关系和声明作为上下文。
一张表格的快照,显示报告生成前的一个中间步骤。对于每个社区,所有实体和关系都被收集,然后结构化为字符串,作为上下文传递给 LLM。列 context_exceed_limit 在 context_string 需要缩短时提醒算法。
对于大型社区,上下文字符串(包括实体、关系,可能还有协变量)可能会超过配置文件中指定的 max_input_length。如果出现这种情况,算法有一种方法可以减少上下文中的文本量,涉及 分层替换 和,如果必要的话,修剪。
在分层替换中,来自实体、关系和声明的原始文本会被子社区的社区报告所替换。
例如,假设社区 C(层级 0)有子社区 S1 和 S2(均为层级 1)。社区 S1 的规模更大(包含更多实体)。在这种情况下,所有在 C 中也存在于 S1 的实体、关系和声明都会被 S1 的社区报告所替换。这优先考虑了 token 数量的最大减少。如果在进行此更改后,上下文长度仍然超过 max_input_length,则使用 S2 来替换 C 中相关的实体和关系。
如果经过层次化替换后,上下文仍然过长(或者社区一开始就没有子社区),那么上下文字符串需要截断 — 不太相关的信息会被简单地排除。实体和关系会根据节点度和组合度进行排序,度值最低的会被移除。
最终,LLM会使用提供的上下文字符串来生成关于该社区的发现(一组5到10个关键洞察)和摘要。这些内容会被组合成社区报告。
社区表的快照,包括LLM生成的报告(full_content列)和报告摘要(summary列)。报告文本是摘要(红色)和发现(蓝色)的组合。rank和rating_explanation列包含LLM分配的重要程度值(1到10之间)以及选择该值的说明。
8. 最后,在 generate_embeddings 中,使用配置中指定的 Open AI 嵌入模型为所有文本单元、实体描述和 full_content 文本(社区标题 + 社区摘要 + 社区报告 + 排名 + 评分解释)创建嵌入。向量嵌入允许基于用户查询在图中进行高效的语义搜索,这在本地搜索和全局搜索中将是必要的。
一旦图构建完成,就可以开始对其进行查询。搜索功能的实现可以找到 GraphRAG 项目的 structure_search 目录中。
如果您有一个具体的问题,请使用 GraphRAG 提供的 本地搜索 功能(在 notebook 中有额外的使用示例)。
graphrag query \
--root ./ragtest \
--method local \
--query "What kind of retribution is Laura seeking, and why?"
本地搜索的关键步骤
社区报告、文本单元、实体、关系以及协变量(如有)从 ragtest/output/ 中的 parquet 文件加载,这些文件是在图创建后自动保存的。
然后,用户查询被嵌入,并计算其与每个实体描述嵌入的语义相似度。
实体快照及其与用户查询的余弦距离
检索出最语义相似的前 N 个实体。N 的值由配置文件中的超参数 top_k_mapped_entities 定义。
奇怪的是,GraphRAG 会以 2 倍的倍数进行过采样,实际上会检索 2 * top_k_mapped_entities 个实体。这样做是为了确保能够提取到足够的实体,因为有时检索到的实体可能具有无效的 ID。
实体快照:与用户查询语义最相似的实体。在这个例子中,_top_k_mapped_entities=10,因此应检索20个实体,但只有17个实体具有有效ID,因此实际检索到了17个实体。排名列显示实体节点的度数。
摘要图:局部搜索中提取实体的检索
2. 所有提取的实体都成为候选实体。提取实体的社区、关系和文本单元成为候选社区、候选关系和候选文本单元。
具体而言:
候选社区是包含至少一个提取实体的所有社区。
候选关系是图中边,其中提取实体是源节点或目标节点。
候选文本单元是包含至少一个提取实体的书本片段。
摘要图:本地搜索中候选社区、实体、关系和文本单元的选择
3. 候选项被排序,最相关的内容排在各自列表的顶部。这确保了最重要的信息优先用于回答查询。
优先级是必要的,因为LLM的上下文长度不是无限的。能够传递给模型的信息量是有上限的。配置中设置的超参数决定了分配给实体、关系、文本单元和社区的上下文窗口令牌数量。默认情况下,text_unit_prop = 0.5,community_prop = 0.1,这意味着配置中指定的_max_tokens_的50%将被文本单元占用,10%用于社区报告,剩下的40%用于实体和关系的描述。max_tokens 默认值为12 000。
社区按其匹配数量排序,即提取实体在多少个不同的文本单元中出现。在出现平局时,按其排名(由LLM分配的重要性)排序。给定 max_tokens=12000 和 community_prop=0.1,则社区报告最多可占用1200个token。仅允许完整社区报告,意味着没有截断——要么整个社区报告都被包含,要么完全不包含。
候选社区按匹配和排名排序的快照。匹配是提取实体出现在多少个不同的文本单元中。排名是社区的重要性分数,由LLM决定。
候选实体不进行排序,保持实体按其与用户查询的语义相似性顺序排列。尽可能多地将候选实体添加到上下文中。如果40%的 max_tokens 用于实体和关系,意味着最多有4800个token可用。
候选实体快照
候选关系根据其是_内网_还是_外网_关系而优先级不同。内网关系是指两个提取实体之间的关系。外网关系是指一个提取实体与其他不在提取实体集中的实体之间的关系。内网关系按其_综合度_(源节点和目标节点度数之和)排序。外网关系则首先按外实体到内实体的_链接_数量排序,若出现平局,则按_综合度_排序。
显示内网关系的表格。排名列显示综合度。
显示外网关系的表格快照。排名列显示综合度,属性列显示外实体到内实体的链接数量(Crímenes, Papás de Laura, 和 Laura)。
发现内部网络和外部网络的关系是一个迭代过程,一旦可用的token空间被填满(在我们的例子中,available_tokens = 4800 — entity_descriptions),该过程就会停止。首先将内部网络的关系添加到上下文中,因为它们被认为更重要。然后在空间允许的情况下,再添加外部网络的关系。
优先级候选关系的快照。请注意,前两行是内部网络的关系。权重默认不使用,且内部网络关系的链接可能过时或错误。
候选文本单元按照提取实体的顺序排序,随后按照与文本单元关联的提取实体关系的数量排序。实体顺序确保了提及与用户查询语义最相似的实体的文本单元优先被排序。例如,如果“Crímenes”是与用户查询语义最相似的实体,而文本单元CB6F…是一个从中提取出“Crímenes”的片段,那么CB6F…将在列表的最前面,即使它关联的提取实体关系数量较少。
表格快照,显示优先级排序的文本单元
每个图边(关系)都有一个属性,该属性说明该关系是从哪些文本单元中提取的。这个属性使得可以追溯提取出的实体与检测到该实体的文本单元之间的关系。
给定 max_tokens=12000 和 text_unit_prop=0.5,那么社区报告最多可以占用 6000 个 token。就像社区报告的情况一样,文本单元会被附加到上下文中,直到达到 token 限制,不会进行截断。
摘要图:本地搜索中候选社区、实体、关系和文本单元的排序
4. 最后,优先级最高的社区报告、实体、关系和文本单元的描述(按此顺序)会被连接起来,并作为上下文提供给 LLM,从而生成对用户查询的详细回答。
摘要图:本地搜索中对用户查询的响应生成
如果您有一个一般性问题,请使用 全局搜索 功能(更多使用示例请参见 notebook)。
graphrag query \
--root ./ragtest \
--method global \
--query "书中探讨了哪些主题?"
社区报告和实体是从它们被保存的parquet文件中加载的。
对于每个社区,计算一个_occurence_weight_。occurence_weight 表示与该社区相关的实体出现在不同文本单元中的归一化计数。该值反映了该社区在整个文档中的普遍程度。
社区表快照
2. 所有社区被随机打乱,然后分批处理。打乱可以减少偏差,确保不是所有最相关的社区都被收集到同一批中。
每个批次中的社区按其_community_weight_ 进行排序。本质上,那些实体出现在多个文本片段中的社区会被优先处理。
总结图:全局搜索中的社区分批处理
3. 对于每个批次,LLM 使用社区报告作为上下文生成多个对用户查询的响应,并为每个响应分配一个分数,以反映其在回答用户问题方面的帮助程度(提示)。通常每个批次生成5个响应。
总结图:Global Search 中每个批次的响应生成
所有响应按分数排序,任何分数为零的响应都会被丢弃。
表格显示所有对用户问题的响应,按分数排序。分析师列代表批次ID。
4. 排序后的响应文本被连接成一个输入,该输入被传递给LLM作为上下文,以生成对用户问题的最终回答(提示)。
总结图:Global Search 中的最终响应生成
本文逐步引导您通过使用真实数据和代码层面的见解,了解 Microsoft GraphRAG 实现的图创建、局部搜索和全局搜索。虽然官方 文档 自我开始在2024年初使用该项目以来有了显著改进,但这次深入探讨填补了知识空白,揭示了底层实现的细节。截至目前,这是我遇到的最详细且最新的 GraphRAG 资源,希望您觉得它有所帮助。
您可以尝试调整参数、微调实体提取提示,或使用不同的索引方法。进行实验,利用 GraphRAG 的强大功能来为您的项目服务!
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-10-27
2024-09-04
2024-05-05
2024-07-18
2024-06-20
2024-06-13
2024-07-09
2024-07-09
2024-05-19
2024-07-07
2025-05-08
2025-05-05
2025-04-30
2025-04-29
2025-04-29
2025-04-26
2025-04-25
2025-04-22