GraphRAG 解决了什么问题
当你问:'这个数据集的主题是什么?'这类高级别、概括性的问题时,传统的 RAG 可能就会束手无策。这是因为这本质上是一个聚焦于查询的总结性任务 (Query-Focused Summarization, QFS),而不是一个明确的检索任务。
解决方案在相关论文中已有详细描述:
In contrast with related work that exploits the structured retrieval and traversal affordances of graph indexes (subsection 4.2),we focus on a previously unexplored quality of graphs in this context: their inherent modularity (Newman,2006) and the ability of community detection algorithms to partition graphs into modular communities of closely-related nodes (e.g.,Louvain,Blondel et al.,2008
简单来说,就是利用社区检测算法(如 Leiden 算法)将整个知识图谱划分模块化的社区 (包含相关性较高的节点),然后大模型自下而上对社区进行摘要,最终再采取 map-reduce 方式实现 QFS: 每个社区先并行执行 Query,最终汇总成全局性的完整答案。
与其他 RAG 系统类似,GraphRAG 整个 Pipeline 也可划分为索引 (Indexing) 与查询 (Query) 两个阶段。索引过程利用 LLM 提取出节点(如实体)、边(如关系)和协变量(如 claim),然后利用社区检测技术对整个知识图谱进行划分,再利用 LLM 进一步总结。
鉴于篇幅原因,本文主要聚焦于 Indexing,Query 的工作原理将在后续介绍。
Pipeline
当你运行 poetry run poe index 命令时,它会执行 graphrag.index.cli 目录下的 index_cli 入口函数。在 GraphRAG 中,构建知识图谱被视为一个流水线(pipeline)过程,这个流水线包含多个工作流(workflow),例如文本分块、使用 LLM 来识别实体等。Pipeline 涵盖的 Workflow 是通过 settings.yml 配置文件进行指定的。index_cli 的主要任务是创建 pipeline_config 对象,并利用 run_pipeline_with_config 函数来运行流水线。

整个过程体现了自上而下的编程思想——每个结果依赖于更底层函数的执行,从顶部开始调用,然后逐步深入到底层函数。这样的结构使得整体流程清晰明了。
Workflow
讨论 Workflow 之前,先简单了解下项目使用的另一个框架:DataShaper 是微软开源的一款用于执行工作流处理的库,内置了很多组件 (专业名词叫做 Verb)。通过定义一个数据处理的工作流,你可以对输入的数据(比如 Pandas 的 DataFrame)定义一系列数据操作的动作(DataShaper 中称作Verb)、参数与步骤,执行这个工作流即可完成数据处理过程。在 DataShaper 中提供了很多开箱即用的 Verb,你也可以自定义 Verb。多个子工作流也可以组合定义成一个更大的工作流。
当你通过命令行执行完 indexing 之后,你会看到如下的输出内容:

从这个可以看出 GraphRAG 的 indexing 共经历了 14 个 workflow:
- create_base_documents
- create_final_documents
- create_base_text_units
- join_text_units_to_entity_ids
- join_text_units_to_relationship_ids
- create_final_text_units
- create_base_extracted_entities
- create_summarized_entities
- create_base_entity_graph
- create_final_entities
- create_final_relationships
- create_final_nodes
- create_final_communities
- create_final_community_reports
基本的处理过程如下:首先,它会将输入文本进行拆分,然后提取实体与关系,生成摘要信息,并根据这些信息构建内存中的图(Graph)结构。接下来,它会从这个图中识别出各个社区,为每个社区创建报告,并在图中创建文本块节点和文档节点。
当然,以上只是些核心步骤,但在实际的处理过程中还涉及到许多细节处理,比如生成嵌入(embedding),持久化到存储,以及应用不同的算法策略等等。
这里可以多说一点,这 14 个 workflow 其实又可以进一步细分为四大类:
- 关于文档的 document_workflows
- create_base_document
- create_final_documents
- 关于文档单元的 text_unit_workflows
- create_base_text_units
- join_text_units_to_entity_ids
- join_text_units_to_relationship_ids
- create_final_text_units
- 构建图谱的 graph_workflows
- create_base_extracted_entities
- create_summarized_entities
- create_base_entity_graph
- create_final_entities
- create_final_relationships
- create_final_nodes
- 社区聚类的 community_workflows
- create_final_communities
- create_final_community_reports
此外,各个工作流之间存在一定的依赖关系,形成了一个工作流流程图。输入数据为存放在 input 目录下的 txt 或 csv 文件 (目前只支持这两种),经过这些工作流组成的流程图处理后,输出的结果就是最终构建的知识图谱。
接下来,我将以一个包含'海贼王'的 txt 文件为例,逐步解析它经历的各个工作流,以及每个工作流的输入和输出是什么。
1. create_base_text_units
整个 pipeline 的入口输入在源码中是个叫 dataset 的变量,其存储的值 Pandas DataFrame。Pandas DataFrame 可以简单看做是一张 table,这个 table 的每一行代表一个 txt 文件,text 列是 txt 文件的内容:

create_base_text_units 是整个 pipeline 的第一个 workflow,它的作用是对 txt 的文件内容按照特定的策略进行切分 (chunking) 操作,目前只支持两种策略:按照 token 和按照 sentence,默认是按照 token,chunk 操作的输入是 text:


对于一个 text 经过 chunking 操作后会得到多个 chunks:

Microsoft GraphRAG 在索引构建的过程中其中间数据主要使用 Pandas DataFrame 这种结构化类型进行交换,可以简单理解为 Mysql 中的 table,对 DataFrame 的一些操作比如 select、join 等可以类比 mysql 的 select, join 等 sql 语句来理解。
一旦我们得到了相应的 chunk,GraphRAG 就会采用特定的策略从每个 chunk 来提取需要的实体 entity。

目前,GraphRAG 支持两种抽取策略:
- graph_intelligence:这是默认的策略。
- nltk:另一种可选策略。
在源码中的 ExtractEntityStrategyType 里,尽管定义了一个名为'graph_intelligence_json'的枚举值,但是目前还未对它进行支持。
当处理多个数据块时,GraphRAG 会并行调用 LLM 来抽取实体,而且默认情况下,它会选择使用多线程。不过,如果你想的话,也可以通过配置修改成 asyncio 模式。
在此流程中,GraphRAG 会调用 run_extract_entities 进行实体抽取,该函数会利用目录下的 entity_extraction.txt 中的 prompt 来调用 LLM 完成实体提取。默认的 entity_extraction prompt 抽取的实体类型是 ['organization', 'person', 'geo', 'event'],你可以根据你的文件内容来修改 settings.yml 中 entity_extraction。
entity_extraction:
我截取了其中一个 chunk 得到的 LLM 调用结果的部分内容:
("entity"<|>欧罗·杰克逊号<|>ORGANIZATION<|>欧罗·杰克逊号是罗杰海贼团的船只) ## ("entity"<|>白胡子<|>PERSON<|>白胡子是'顶上战争'之前的四皇之一,悬赏金为 50 亿 4600 万) ## ("entity"<|>百兽<|>PERSON<|>百兽是'顶上战争'之前和之后的四皇之一,悬赏金为 46 亿 1110 万) ## ("entity"<|>BIG MOM<|>PERSON<|>BIG MOM 是'顶上战争'之前和之后的四皇之一,悬赏金为 43 亿 8800 万) ## ("entity"<|>红发<|>PERSON<|>红发是'顶上战争'之前和之后的四皇之一,悬赏金为 40 亿 4890 万) ## ("entity"<|>黑胡子海贼团<|>ORGANIZATION<|>黑胡子海贼团是黑胡子的势力) ## ("relationship"<|>弗兰奇<|>卡雷拉公司<|>弗兰奇设计的海贼船由卡雷拉公司协助制作<|>8) ## ("relationship"<|>草帽大船团<|>俊美海贼团<|>俊美海贼团是草帽大船团旗下的一个海贼团<|>7) ## ("relationship"<|>卡文迪许<|>俊美海贼团<|>卡文迪许是俊美海贼团的船长<|>9) ## ("relationship"<|>斯莱曼俊美海贼团斯莱曼是俊美海贼团的船员)
GraphRAG 会对 LLM 的输出结果进行后处理 post_processing,最终形成 Graph 对象的。我们先看一下实体(entities),每一个实体都有四个主要的属性:name、description、source_id 和 type。
- Name:这是实体的名称。
- Description:对实体的描述。
- Source_id:在此情况下,source_id 是指那些生成这个特定实体的数据块 (chunk) 的识别号。
- Type:实体的类型。

每个 chunk 都生成对应的实体后,会把这些实体添加到一个列表 entities 中,并把每段 chunk 对应的表达图形结构的Graphml也放到一个列表 entity_graph 中:

Microsoft GraphRAG 在索引构建的过程中对于 Graph 数据的交换使用Graphml(一种 xml 表示的 graph)
这里有个情况需要考虑,不同的数据块(chunks)可能会抽取出相同的实体。比如说,第一个和第二个数据块都可能包含'草帽路飞'这个实体。这时候,GraphRAG 会采用一种名为 merge_graphs 的操作,把多个子图合并成一个新的大图。如果遇到相同的节点,那么 GraphRAG 就会执行 concat 操作,也就是将对应的属性和关系进行合并。
比如对于一个实体:'哥尔·D·罗杰',经过 merge 之后会包含多个 description 的列表:['哥尔·D·罗杰是罗杰海贼团的船长', '哥尔·D·罗杰是被称为'海贼王'的男人,他在被行刑受死之前说了一句话,开启了'大海贼时代'']
通过 merge_graphs 操作,GraphRAG 能够有效地处理重复的实体,并把多个 chunk 对应的 Graph 整合成一个新的 Graph,形成一个更加完善和详细的数据图:

3. create_summarized_entities
通过 merge_graphs 的操作,将多个子图合并到一个全新的大图之后,GraphRAG 会进一步这个大图的节点 (node) 和关系 (relationship) 的描述 (descriptions) 进行总结。
这样做的目的是为了方便查询,因为查询时需要根据问题匹配知识库中的实体信息和关系信息时,只需要根据总结后的实体描述和关系描述就可以进行匹配了。不然得遍历 description list 进行匹配。
GraphRAG 目前支持的 summarize 的策略只有一种:graph_intelligence。
summarize 使用的 prompt 中文翻译如下:
你是一位负责生成以下提供数据的综合摘要的有用助手。 根据一个或两个实体,以及一系列描述,这些描述都与同一个实体或一组实体有关。 请将所有这些描述合并成一个单一的、全面的描述。确保包括所有描述中收集到的信息。 如果提供的描述存在矛盾,请解决这些矛盾,并提供一个单一的、连贯的摘要。 确保用第三人称写作,并包括实体名称,以便我们拥有完整的上下文。
执行 summarize_descriptions 操作后,原来图形中的多个 description就被整合为了一个全新的、详尽的描述。可以说,summarize_descriptions 是把前一步得到的 Graph 进行整理的过程,使得 Graph 更加清晰、准确。
经过 summarize 之后,上一个 workflow create_base_extracted_entities 得到的 Graph 被更进一步完善了:

生成这种摘要的好处是:可以借助嵌入 embedding 向量更有效与准确的对这些实体与关系进行检索。
4. create_base_entity_graph
这一步是做社群检查的:将实体进行分类,拿三国举例,比如周瑜和孙策属于吴国,曹操和司马懿属于魏国,刘备和关羽属于蜀国,而吴国、魏国、蜀国都属于东汉,其中东汉是一个大社群,魏蜀吴是三个小社群,当执行查询时,可以指定社区的级别,如果指定的是低级别社群,那么查找的结果就比较微观,比如问三国时期有哪些著名人物,如果指定的社群为吴国,那么匹配的就只有周瑜和孙策,如果指定的社群为东汉,那么就能找到更多的著名人物。
create_base_entity_graph 这个 workflow 会对 Graph 应用应用层次聚类算法 (对应源码中的 cluster_graph 方法),在 Graph 中识别出层次结构和社区结构:一个 Level 对应多个 community。
GraphRAG 在源码中借助了Graspologic 库实现的 Leiden 算法:Leiden 算法通常比许多其他的社区检测算法更稳定,能更可靠地复现结果,但是 Leiden 算法在某些情况下可能会比其他方法慢一些。
在这个 workflow 中先会进行 run_layout 布局分析,应用 Leiden 算法对 nodes 分社区,完成这些步骤后,每一个社区的节点都会被赋予以下属性:
- Level:表示节点所在的层次。
- Cluster:表示节点所在社区的编号。
- Human_readable_id:这可以被看做是实体(entity)在同一个社区内的编码,从 0 开始

经过 create_base_entity_graph 之后,Graph 按照层级被划分出多个子图,每个子图对应一个 level:

5. create_final_entities
create_final_entities 这个 workflow 的功能是对节点做 embedding,方便进行之后的 query。
在做 embedding 之前,为了更好地表示每个节点,我们将节点的'name'和'description'字段拼接起来,形成一个新的'name_description'字段。这样,每个节点都将有一个通俗易懂,并且信息丰富的标签。
然后,我们把这个新生成的'name_description'字段通过嵌入过程转换成一个向量表示。这种方法能够捕获和表示文本数据的复杂模式,也使得我们可以针对这些节点进行高效的计算和分析。
经过上面的一些图的修整之后,我们还需要对 entity 做进一步的 embedding 操作。在这之前,会经过 embedding 操作,embedding 会对 node 的 name 和 description 拼接的 name: description 组成 name_description 字段,对这个字段做 embedding 操作。

经过 embedding 之后,新增了一列 description_embedding 字段:

6. create_final_nodes
Network Visualization 阶段,由于生成的图谱一般不是一个平面图(可以通过在平面上绘制其顶点和边而不出现边的交叉),通过使用降维技术操作将非平面图映射到平面上,可以更直观地观察和理解数据的结构和模式。
在图论和网络分析中,图的布局算法(layout algorithm)用于将图中的节点和边在二维或三维空间中进行合理的排列和可视化。其主要目标是使图的结构和关系尽可能清晰地展示出来,以便于人类理解和分析。create_final_nodes 会对 Graph 应用 layout 算法,GraphRAG 目前支持两种算法:
workflow 的输入是 create_base_entity_graph 的输出:

每个 entity 的所有属性现在长这样:

7. create_final_communities
这个 workflow 用于创建 community table,步骤如下:
我们从第 4 步生成的 create_base_entity_graph 中抽取节点数据,形成一个名为 graph_nodes 的表:

然后,我们同样从 create_base_entity_graph 中提取边信息,生成另一个名为 graph_edges 的表:

然后,我们将 graph_nodes 和 graph_edges 进行 left_join 操作,这个新生成的表命名为 combined_clusters:

紧接着,我们对 combined_clusters 进行进一步的聚合操作,同样是按照 cluster 和 level 进行分组。在这个过程中,我们会把 edge 的 id_2 去重后组合成一个数组,命名为 relationship_ids;同时,也会把 node 的 source_id_1 去重后组合成另一个数组,命名为 text_unit_ids:

以 cluster = 1 为例,通过 text_unit_ids,我们能够知道这个社区来源于哪些 chunk;通过 relationship_ids,我们则可以确定这个社区包含了哪些边。
最后,我们还会对上述数据进行一次处理,主要是生成每个社区的名称:

8. join_text_units_to_entity_ids
join_text_units_to_entity_ids 的作用是建立 text_unit 到 entity 的映射关系。
首先,GraphRAG 会提取出每个实体的"id",以及表示实体来源的字段"text_unit_ids"。接着,我们对"text_unit_ids"进行'打平'操作,也就是将嵌套的数据结构转化为一维的形式。
然后,我们进行聚合操作。具体来说,我们会按照"text_unit_id"对数据进行分类,并把相同类别的实体 id 聚合成一个数组,命名为"entity_ids"。
这样,在最终的结果中,每一条记录都会包含一个"text_unit_id",以及一个与之关联的"entity_ids"数组:

9. create_final_relationships
create_final_relationships 用于创建 relationship table,步骤如下:
首先,我们从 create_base_entity_graph 中得到的 Graph 提取出所有的边关系:

接下来,我们会对 edge 和 nodes 使用 left_join 操作,在这个步骤中,我们将新增两列:source_degree 和 target_degree。这两列分别表示源实体和目标实体的度数,也就是每个实体连接的边的数量。
最后,我们会创建一个新的列"rank"。这个列的值是通过将 source_degree 和 target_degree 相加得到的。这样,我们就可以根据 rank 的值,了解每条边连接的两个实体的总度数:
| source | target | weight | description | text_unit_ids | id | human_readable_id | source_degree | target_degree | rank |
|---|
| 蒙奇·D·路飞 | 草帽一伙 | 1.0 | 蒙奇·D·路飞是草帽一伙的船长和创立者 | ['2808e991f29115cba505836944beb514'] | 392be891f8b649fabdc20e7bf549f669 | 0 | 11 | 19 | 30 |
| 蒙奇·D·路飞 | 香克斯 | 1.0 | 蒙奇·D·路飞为了实现与香克斯的约定而出海 | ['2808e991f29115cba505836944beb514'] | 0111777c4e9e4260ab2e5ddea7cbcf58 | 1 | 11 | 2 | 13 |
| 蒙奇·D·路飞 | ONE PIECE | 1.0 | 蒙奇·D·路飞为了寻找传说中的大秘宝 ONE PIECE 而扬帆起航 | ['2808e991f29115cba505836944beb514'] | 785f32471c439e89601ab81c828d1d | 2 | 11 | 1 | 12 |
10. join_text_units_to_relationship_ids
这个 workflow 和我们之前讨论过的 join_text_units_to_entity_ids 非常相似,主要区别在于,现在我们是将 text_unit_id 映射到它所包含的 relationship_id,而不再是 entity_ids。
简单来说,我们的目标是理解每个 text_unit_id(对应"chunk")都包含了哪些关系(relationship)。为了实现这个目标,我们会创建一种映射关系,把每个 text_unit_id 连接到它所涉及的所有 relationship_id。结果将以类似于字典的形式呈现,其中键是 text_unit_id,值是一个列表,包含了所有相关的 relationship_id:

这个 workflow 用于生成社区摘要:借助 LLM 生成每个社区的摘要信息,用来了解数据集的全局主题结构和语义。这也是 Microsoft GraphRAG 的核心价值所在,也是回答 QFS 问题的关键。具体步骤如下:
首先借助 create_final_nodes 的输出,并添加了一个 node_details 列以存储更多关于节点的信息:

然后对这些 nodes 使用 community_hierarchy 来构建社区的层次结构,通过对 (community, level) 的分组,将同一组内的节点 title 聚合成数组:


从上图我们可以看到每个 community 包含了哪些 entity。
接下来,GraphRAG 开始分析父子社区的构造情况,如果上一级的社区包含了全部下一级社区的成员,那么它们之间就构成了父子社区的关系,我们发现社区 1 是个大社区,包含了 12、13、14 三个子社区,但是它们都属于同一个 level:

紧接着 GraphRAG 会基于三个 table: node_df、edge_df、claim_df 做聚合操作,生成每个社区的 context_string:包含社区的所有节点和 relationships 信息:

为了方便你看到 context_string 的内容,我摘取了某个社区的 context_string 内容:
'-----Entities-----\n'
'human_readable_id,title,description,degree\n'
'2,蒙奇·D·路飞,蒙奇·D·路飞是'草帽一伙'的船长,外号'草帽小子',梦想成为'海贼王',悬赏金 30 亿贝里,11\n'
'20,ONE PIECE,,1\n'
'17,五老星,五老星认为蒙奇·D·路飞食用的橡胶果实实际上是人人果实·幻兽种·尼卡形态,1\n'
'10,和之国事件,和之国事件是蒙奇·D·路飞击败原'四皇'之一的'百兽'凯多的事件,1\n'
'19,尼卡,尼卡是五老星认为蒙奇·D·路飞食用的人人果实·幻兽种的形态,1\n'
'9,恶魔果实,恶魔果实是一种神秘的果实,食用后可以获得超人能力,但会失去游泳的能力,1\n'
'11,百兽凯多,百兽凯多是原'四皇'之一,被蒙奇·D·路飞在和之国事件中击败,1\n'
'\n'
'\n'
'\n'
'-----Relationships-----\n'
'human_readable_id,source,target,description,rank\n'
'0,蒙奇·D·路飞,草帽一伙,蒙奇·D·路飞是草帽一伙的船长和创立者,30\n'
'7,蒙奇·D·路飞,东海,蒙奇·D·路飞的出身地是东海,16\n'
'1,蒙奇·D·路飞,香克斯,蒙奇·D·路飞为了实现与香克斯的约定而出海,13\n'
'6,蒙奇·D·路飞,香波地群岛,蒙奇·D·路飞是'极恶的世代'中登陆香波地群岛的 11 位超新星之一,13\n'
'9,蒙奇·D·路飞,极恶的世代,蒙奇·D·路飞是中登陆香波地群岛的 位超新星之一,\n,蒙奇·D·路飞,ONE PIECE,蒙奇·D·路飞为了寻找传说中的大秘宝 ONE PIECE 而扬帆起航,\n,蒙奇·D·路飞,五老星,五老星认为蒙奇·D·路飞食用的橡胶果实实际上是人人果实·幻兽种·尼卡形态,\n,蒙奇·D·路飞,和之国事件,蒙奇·D·路飞在和之国事件中击败了百兽凯多,\n,蒙奇·D·路飞,尼卡,五老星认为蒙奇·D·路飞食用的橡胶果实实际上是人人果实·幻兽种·尼卡形态,\n,蒙奇·D·路飞,恶魔果实,蒙奇·D·路飞因误食恶魔果实而成为了橡皮人,\n,蒙奇·D·路飞,百兽凯多,蒙奇·D·路飞在和之国事件中击败了百兽凯多,
接着 LLM 会使用 community_report.txt 中的 prompt 并把 context_string 作为输入,对社区按照 level 进行自下而上的总结,使用的默认 prompt 中文翻译如下:
你是一个人工智能助手,帮助人类分析员进行一般的信息发现。信息发现是识别和评估与某些实体(例如,组织和个人)相关的相关信息的过程。
在给定属于社区的实体列表及其关系和可选的相关声明的情况下,编写社区的全面报告。报告将用于通知决策者有关社区及其潜在影响的信息。报告内容包括社区关键实体的概述、他们的法律合规性、技术能力、声誉和值得注意的声明。
报告应包括以下部分:
- 标题:代表其关键实体的社区名称——标题应简短但具体。尽可能在标题中包括具有代表性的命名实体。
- 摘要:对社区整体结构、其实体之间的关系以及与其实体相关的重大信息的执行摘要。
- 影响严重性评分:一个介于 0-10 之间的浮动评分,表示社区内实体所构成的影响的严重程度。影响是社区的重要性评分。
- 评分解释:用一句话解释影响严重性评分。
- 详细发现:关于社区的 5-10 个关键见解的列表。每个见解应有一个简短的摘要,后跟根据以下基础规则进行的多段解释性文本。要全面。
返回输出为格式良好的 JSON 格式的字符串,格式如下:
```json
{
"title": <report_title>,
"summary": <executive_summary>,
"rating": <impact_severity_rating>,
"rating_explanation": <rating_explanation>,
"findings": [
{
"summary":<insight_1_summary>,
"explanation": <insight_1_explanation>
},
{
"summary":<insight_2_summary>,
"explanation": <insight_2_explanation>
}
]
}
基础规则
支持数据的点应列出其数据引用,如下所示:
'这是一个由多个数据引用支持的示例句子 [数据: (记录 ID); (记录 ID)]。'
在单个引用中不要列出超过 5 个记录 ID。相反,列出最相关的前 5 个记录 ID,并加上'+更多'以表示还有更多。
例如:
'Person X 是 Company Y 的所有者,并且受到许多不当行为指控 [数据:报告 (1), 实体 (5, 7); 关系 (23); 声明 (7, 2, 34, 64, 46, +更多)]。'
其中 1, 5, 7, 23, 2, 34, 46 和 64 代表相关数据记录的 ID(而不是索引)。
不要包括没有提供支持证据的信息。
示例输入
文本:
实体
id,entity,description
5,VERDANT OASIS PLAZA,绿洲广场是团结游行的地点
6,HARMONY ASSEMBLY,和谐集会是一个在绿洲广场举行游行的组织
关系
id,source,target,description
37,VERDANT OASIS PLAZA,UNITY MARCH,绿洲广场是团结游行的地点
38,VERDANT OASIS PLAZA,HARMONY ASSEMBLY,和谐集会在绿洲广场举行游行
39,VERDANT OASIS PLAZA,UNITY MARCH,团结游行正在绿洲广场进行
40,VERDANT OASIS PLAZA,TRIBUNE SPOTLIGHT,论坛焦点正在报道绿洲广场上的团结游行
41,VERDANT OASIS PLAZA,BAILEY ASADI,Bailey Asadi 在绿洲广场上就游行发表演讲
43,HARMONY ASSEMBLY,UNITY MARCH,和谐集会正在组织团结游行
输出:
{
"title": "绿洲广场和团结游行",
"summary": "社区围绕绿洲广场展开,该广场是团结游行的地点。广场与和谐集会、团结游行和论坛焦点都有关系,这些都与游行事件有关。",
"rating": 5.0,
"rating_explanation": "由于团结游行期间可能发生的骚乱或冲突,影响严重性评分为中等。",
"findings": [
{
"summary": "绿洲广场作为中心地点",
"explanation": "绿洲广场是该社区的中心实体,作为团结游行的地点。该广场是所有其他实体的共同联系点,表明其在社区中的重要性。广场与游行的关联可能会导致如公共秩序问题或冲突等问题,具体取决于游行的性质和它引起的反应。[数据:实体 (5), 关系 (37, 38, 39, 40, 41,+更多)]"
},
{
"summary": "和谐集会在社区中的角色",
"explanation": "和谐集会是社区中的另一个关键实体,他们在绿洲广场组织游行。和谐集会的性质和他们的游行可能是潜在的威胁来源,这取决于他们的目标和引起的反应。和谐集会和广场之间的关系对于理解该社区的动态至关重要。[数据:实体 (6), 关系 (38, 43)]"
},
{
"summary": "团结游行作为重要事件",
"explanation": "团结游行是一个在绿洲广场上发生的重要事件。该事件是社区动态的关键因素,具体取决于游行的性质和它引起的反应,可能是潜在的威胁来源。游行和广场之间的关系对于理解社区的动态至关重要。[数据:关系 (39)]"
}
真实数据
使用以下文本作为答案的依据。不要在答案中编造任何内容。
文本:{input_text}
报告应包括以下部分:
- 标题:代表其关键实体的社区名称——标题应简短但具体。尽可能在标题中包括具有代表性的命名实体。
- 摘要:对社区整体结构、其实体之间的关系以及与其实体相关的重大信息的执行摘要。
- 影响严重性评分:一个介于 0-10 之间的浮动评分,表示社区内实体所构成的影响的严重程度。影响是社区的重要性评分。
- 评分解释:用一句话解释影响严重性评分。
- 详细发现:关于社区的 5-10 个关键见解的列表。每个见解应有一个简短的摘要,后跟根据以下基础规则进行的多段解释性文本。要全面。
返回输出为格式良好的 JSON 格式的字符串,格式如下:
{
"title": <report_title>,
"summary": <executive_summary>,
"rating": <impact_severity_rating>,
"rating_explanation": <rating_explanation>,
"findings": [
{
"summary":<insight_1_summary>,
"explanation": <insight_1_explanation>
},
{
"summary":<insight_2_summary>,
"explanation": <insight_2_explanation>
}
]
}
基础规则
支持数据的点应列出其数据引用,如下所示:
'这是一个由多个数据引用支持的示例句子 [数据: (记录 ID); (记录 ID)]。'
在单个引用中不要列出超过 5 个记录 ID。相反,列出最相关的前 5 个记录 ID,并加上'+更多'以表示还有更多。
例如:
'Person X 是 Company Y 的所有者,并且受到许多不当行为指控 [数据:报告 (1), 实体 (5, 7); 关系 (23); 声明 (7, 2, 34, 64, 46, +更多)]。'
其中 1, 5, 7, 23, 2, 34, 46 和 64 代表相关数据记录的 ID(而不是索引)。
不要包括没有提供支持证据的信息。
输出:
我们看下某个社区的报告内容:
{'findings': [{'explanation': '蒙奇·D·路飞是草帽一伙的船长和创立者,他的梦想是成为海贼王。他的出身地是东海,并且为了实现与香克斯的约定而出海。他还因误食恶魔果实而成为了橡皮人,这使他获得了超人能力但失去了游泳的能力 '[Data: Entities (2, 9); Relationships (0, 7, 1, '3)].', 'summary': '蒙奇·D·路飞的核心地位'}, {'explanation': '和之国事件是蒙奇·D·路飞击败原'四皇'之一的百兽凯多的事件。这一事件标志着他在海贼世界中的地位进一步提升,并对世界格局产生了深远影响 '[Data: Entities (10, 11); Relationships (4, '5)].', 'summary': '和之国事件的重要性'}, {'explanation': '五老星认为蒙奇·D·路飞食用的橡胶果实实际上是人人果实·幻兽种·尼卡形态。这一观点揭示了蒙奇·D·路飞的能力可能比之前认为的更为强大和神秘 '[Data: Entities (17, 19); Relationships (8, '10)].', 'summary': '五老星的观点'}, {'explanation': '恶魔果实是一种神秘的果实,食用后可以获得超人能力,但会失去游泳的能力。蒙奇·D·路飞因误食恶魔果实而成为了橡皮人,这使他在战斗中具有独特的优势 '[Data: Entities (9); Relationships (3)].', 'summary': '恶魔果实的影响'}, {'explanation': '草帽一伙是由蒙奇·D·路飞创立的海贼团体,他们在海贼世界中扮演着重要角色。蒙奇·D·路飞作为船长,带领着这支团队在寻找传说中的大秘宝 ONE PIECE 的过程中经历了许多冒险 [Data: Entities (2, 20); 'Relationships (0, 2)].', 'summary': '草帽一伙的角色'}, {'explanation': '蒙奇·D·路飞是'极恶的世代'中登陆香波地群岛的 11 位超新星之一。这一身份使他在海贼世界中备受关注,并进一步提升了他的影响力 '[Data: Relationships (6, 9)].', 'summary': '极恶的世代'}], 'rating': 8.5, 'rating_explanation': '该社区的影响力很高,因为蒙奇·D·路飞在和之国事件中的胜利对整个世界格局产生了重大影响。', 'summary': '该社区围绕着蒙奇·D·路飞展开,他是草帽一伙的船长,梦想成为海贼王。蒙奇·D·路飞与多个实体有着紧密的联系,包括和之国事件、五老星、百兽凯多等。和之国事件是他击败原'四皇'之一的百兽凯多的重要事件。五老星认为他食用的橡胶果实实际上是人人果实·幻兽种·尼卡形态。', 'title': '蒙奇·D·路飞与和之国事件'}
这份报告包含了社区的总体 title、summary 和发现等等,这个过程也是最耗费 token 的。
### 12. create_final_text_units
这个 workflow 很简单,就是把对应的 chunk 和这个 chunk 有的 document_ids, entity_ids, relationship_ids 做关联,成一张表
- **id**: 表示每条记录的唯一标识符。
- **text**: 包含文本内容的列。
- **n_tokens**: 表示文本内容中包含的标记(token)的数量。
- **document_ids**: 包含一个或多个文档标识符的列,表示该记录与哪些文档相关联。
- **entity_ids**: 包含一个或多个实体标识符的列,表示该记录中提到的实体。
- **relationship_ids**: 包含一个或多个关系标识符的列,表示该记录中涉及到的关系。

### 13. create_base_documents
这个流程也很简单,主要是建立 document 和 text_unit 的对应关系表

### 14. create_final_documents
这个流程完成的工作基本和 create_base_documents 一致,只是把 text_units 列名换成了 text_unit_ids 而已

## 总结
当 GraphRAG 完成索引过程后,它默认会将构建知识图谱所需的所有数据持久化。这些数据被存储在输出目录中,并采用 Parquet 文件格式。Parquet 是一种列式压缩存储格式,专为高效的数据存储和分析而设计。你可以将其视为 DataFrame 的一种持久化方式。
在查询阶段,这些 Parquet 文件会被加载到内存和向量数据库中。这样做的好处在于,我们可以直接从内存和数据库中检索信息,而无需再次从原始数据源抽取和处理数据。这大大提高了查询的效率和速度。
由于 parquet 是一种底层文件格式,我们无法用来直观的了解与观察上面构建的知识图谱索引的细节,有什么办法可以做更直观的可视化、分析与检索呢?
由于 parquet 文件可以很简单的通过 pandas 库读取成 DataFrame 表,所以在了解其结构后,就可以通过 Cypher 语句导入成 Neo4j 图数据库中的节点与关系。在 Github 上已经有人完成这样的工作:https://github.com/tomasonjo/blogs/blob/master/msft_graphrag/ms_graphrag_import.ipynb。你如果嫌麻烦,也可以把 parquet 转成 csv 格式进行查看,代码也非常简单,不到 20 行左右。
下图是抽取的 Entity 的 Neo4j 展示:

基于 GraphRAG 生成的数据导入到 Neo4j 之后,我们完全可以不再依赖于 GraphRAG 项目自带的 Query 功能,可以结合自己的项目需求在自己的 Neo4j 图数据库上定义自己的 RAG 应用检索与生成器,从而带来极大的灵活性。
![]()