GraphRAG学习小结(3)

6.深入GraphRAG的代码

参考:https://medium.com/percena/inside-graphrag-analyzing-microsofts-innovative-framework-for-knowledge-graph-processing1-6f84deec5499

6.1 index索引部分

6.1.1 构建workflow

在测试中我们执行了这样的代码

python -m graphrag.index --init --root ./ragtest
python -m graphrag.index --root ./ragtest

前者是初始化index参数,后者是执行index,也就是构建知识图谱。

index文件夹下的代码的执行过程如图所示。

这个图谱是一个叫code2flow的库的执行结果,可以直接pip install该库,在阅读大规模的代码时非常方便。

调用过程中出现问题,可以参考这个链接。

python - "RuntimeError: Make sure the Graphviz executables are on your system's path" after installing Graphviz 2.38 - Stack Overflow

我个人的情况是MacOS,补充运行了brew安装graphviz就可以正常使用了。

brew install graphviz

这个图看起来可能有点复杂,但是执行的命令其实很简单。index_cli() 函数首先根据用户输入参数, 如 --init, 确定是否需要初始化当前文件夹, 检查目录下的一些配置、prompt、.env等文件是否存在,没有则创建,具体文件内容,就是settings.yaml, prompts等;

之后_create_default_config()首先检查根目录以及配置文件 settings.yaml, 然后执行_read_config_parameters()读取系统配置, 如 llm, chunks, cache, storage等。

后续会根据当前参数创建一个pipeline的配置, 具体代码位于create_pipeline_config()

 create_pipeline_config函数:这是模块的主要函数,它接受一个GraphRagConfig对象,根据这个对象的设置生成一个PipelineConfig对象。这个函数首先确定要跳过的工作流(workflows),获取嵌入字段,然后根据设置构建输入、报告、存储和缓存配置。

def create_pipeline_config(settings: GraphRagConfig, verbose=False) -> PipelineConfig:
    """Get the default config for the pipeline."""
    # relative to the root_dir
    if verbose:
        _log_llm_settings(settings)

    skip_workflows = _determine_skip_workflows(settings)
    embedded_fields = _get_embedded_fields(settings)
    covariates_enabled = (
        settings.claim_extraction.enabled
        and create_final_covariates not in skip_workflows
    )

    result = PipelineConfig(
        root_dir=settings.root_dir,
        input=_get_pipeline_input_config(settings),
        reporting=_get_reporting_config(settings),
        storage=_get_storage_config(settings),
        cache=_get_cache_config(settings),
        workflows=[
            *_document_workflows(settings, embedded_fields),
            *_text_unit_workflows(settings, covariates_enabled, embedded_fields),
            *_graph_workflows(settings, embedded_fields),
            *_community_workflows(settings, covariates_enabled, embedded_fields),
            *(_covariate_workflows(settings) if covariates_enabled else []),
        ],
    )

    # Remove any workflows that were specified to be skipped
    log.info("skipping workflows %s", ",".join(skip_workflows))
    result.workflows = [w for w in result.workflows if w.name not in skip_workflows]
    return result

之后 的Pipeline执行逻辑是run_pipeline_with_config()首先根据load_pipeline_config()加载现有pipeline的配置(由上一步中_create_default_config()生成), 然后创建目标文件中的其他子目录,如cache, storage, input, output等,然后再利用run_pipeline()依次执行每一个工作流, 并返回相应结果。

具体看一下run这部分代码。

 run_pipeline_with_config:根据提供的配置或配置文件路径来运行流程。它接受多种参数,包括工作流、数据集、存储、缓存、回调、进度报告器等。

 run_pipeline:执行实际的数据处理流程。它接受工作流列表、数据集、存储、缓存等参数,并返回异步可迭代的结果。

load_pipeline_config:加载流程配置。

load_storageload_cacheload_pipeline_reporter:分别用于加载存储、缓存和报告器。

create_workflow:创建工作流实例。

_create_callback_chain:创建回调管理器,可以注册多个回调。

NullProgressReporter:如果没有提供进度报告器,则使用空实现。

ProgressWorkflowCallbacks:用于进度报告的回调。

MemoryProfile:用于分析内存使用情况。

_validate_dataset:验证数据集是否符合要求。

_run_post_process_steps:运行输入数据的后处理步骤。

_create_run_context:创建流程执行的上下文,包括存储、缓存和统计信息。

PipelineRunStats:用于收集流程运行的统计数据。

emit_workflow_output:将工作流的输出结果存储到指定的存储系统中。

write_workflow_stats_save_profiler_stats:分别用于存储工作流统计信息和内存分析结果。

on_error:在发生错误时调用回调函数。

使用AsyncIterable来返回异步可迭代的结果。

在适当的时候调用gc.collect()进行垃圾回收。

_apply_substitutions:对配置中的字符串进行模板替换。

_create_run_context:创建流程运行的上下文。

主要逻辑流程

1.加载配置和依赖项。2.设置存储、缓存、回调和进度报告器。3.加载和验证数据集。

4.运行工作流,处理数据,并收集统计信息。5.存储结果和统计信息,处理异常。

根据以上信息, 我们可以大致梳理出索引环节的完整工作流

初始化: 生成必要的配置文件, 缓存, input/output目录等

索引: 根据配置文件, 利用workflow模板创建一系列的pipeline, 并依据依赖关系, 调整实际执行顺序, 再依次执行

6.1.2 workflow的执行

这里就用到了微软自己的库datashaper来进行workflow的执行

Indexing Architecture

DataShaper中的核心资源类型之一是workflow。工作流程以步骤序列表示,称之为verb。每个步骤都有一个verb名称和一个配置对象。在DataShaper中,这些动词模拟关系概念,如SELECT、DROP、JOIN等。每个动词转换一个输入数据表,该表沿管道传递。

整个pipeline的dataflow如下图所示

再结合终端中打印的pipeline一起来看

⠹ GraphRAG Indexer 
├── Loading Input (InputFileType.text) - 1 files loaded (0 filtered) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00 0:00:00
├── create_base_text_units
├── create_base_extracted_entities
├── create_summarized_entities
├── create_base_entity_graph
├── create_final_entities
├── create_final_nodes
├── create_final_communities
├── join_text_units_to_entity_ids
├── create_final_relationships
├── join_text_units_to_relationship_ids
├── create_final_community_reports
├── create_final_text_units
├── create_base_documents
└── create_final_documents
🚀 All workflows completed successfully.

这部分的代码在如下路径

graphrag/index/workflows/v1

这是默认配置下的workflow执行。

这里选一个具体看看create_base_extracted_entities.py

build_steps:根据提供的配置,返回一个工作流步骤列表。

config:一个 PipelineWorkflowConfig 类型的参数,包含工作流的配置信息。

工作流步骤

函数内部定义了四个主要的工作流步骤,每个步骤都是一个字典,包含以下关键信息:

1."verb":指定了步骤的操作类型,例如 "entity_extract""snapshot""merge_graphs" 和 "snapshot_rows"

2."args":包含了操作需要的参数。

3."input":指定了步骤的输入来源,这里通常是其他工作流的输出。

4."enabled":指定了步骤是否启用,可以根据配置来决定。

步骤详解

1.实体提取:使用 "entity_extract" 动词,配置了实体提取的相关参数,如列名称、异步模式等,并将结果输出到 "entities" 和 "entity_graph"

2.快照:使用 "snapshot" 动词,当 raw_entity_snapshot_enabled 为真时启用,用于保存原始提取的实体。

3.合并图:使用 "merge_graphs" 动词,合并多个图,并提供了图的合并操作配置。

4.graph的快照:使用 "snapshot_rows" 动词,当 graphml_snapshot_enabled 为真时启用,用于保存合并后的图(以 .graphml 格式)。

这个就是我们之前可视化部分调整的参数输出的graphml文件。

配置依赖

此工作流依赖于 "workflow:create_base_text_units" 工作流的输出。

返回值

函数返回一个包含上述步骤的列表,这些步骤将被用于构建数据管道。

6.1.3 workflow内部的具体实现

如上图所示一共是6个阶段,其中阶段5和阶段6都是为可视化服务的,与index构建关系不大,这里就只简单看一下。

6.1.3.1 构建TextUnits

默认配置工作流程的第一阶段是将输入文档转换为TextUnitsTextUnit是用于我们图形提取技术的文本块。

chunk块大小(以token计算)是用户可配置的。默认情况下设置为300token,这里可以参考论文部分,有具体实验结果。

分组配置也是用户可配置的。默认情况下,将块对齐到文档边界,这意味着文档和文本单元之间存在严格的一对多关系。在极少数情况下,这可以变成多对多的关系。当文档很短时,这很有用,我们需要其中几个来组成一个有意义的分析单元(例如推文或聊天记录)。

具体代码的路径如下

graphrag/index/text_splitting

具体的函数定义

Tokenizer:一个分词器的数据类,包含分词重叠、每块的最大分词数、解码和编码函数。

TextSplitter:一个抽象基类,定义了文本分割的接口。

NoopTextSplitter:一个不执行任何操作的文本分割器,直接返回输入文本。

TokenTextSplitter:一个基于分词器的文本分割器,能够在给定的分词器基础上将文本分割成小块。

TextListSplitter:用于分割文本列表的文本分割器,可以处理JSON格式或由特定分隔符分隔的字符串。

TextListSplitterType:一个枚举,定义了TextListSplitter的类型。

split_text_on_tokens:一个函数,使用分词器将文本分割成小块。

6.1.3.2 Graph提取

这部分的具体代码路径如下graphrag/index/graph/extractors

实体和关系提取

在图形提取的第一步中,处理每个文本单元,以便使用LLM从原始文本中提取实体和关系。此步骤的输出是每个文本单元的子图,其中包含具有名称、类型和描述的实体列表,以及与源、目标和描述的关系列表。

任何具有相同名称和类型的实体都通过创建其描述数组来合并。同样,与相同来源和目标的任何关系都通过创建其描述数组来合并。

实际上这一过程主要依赖LLM完成(在下一节的prompts中有比较详细的描述),所以代码的主要内容是关于如何将LLM输出的内容整理成可供后续使用的图数据。

代码具体内容如下。

GraphExtractionResult:一个数据类,用于存储图提取的结果,包含一个网络图(NetworkX Graph)和源文档的映射。

GraphExtractor:一个类,用于从文本中提取实体和关系,并构建一个图来表示这些实体和关系。

 __init__构造函数接收多个参数,用于配置GraphExtractor的行为,例如LLM的实例、分隔符、提示文本、编码模型、最大提取次数和错误处理函数。

__call__接收一组文本和提示变量,调用LLM进行实体和关系的提取,并将结果整合到图中。

 _process_document对单个文档进行处理,调用LLM提取信息,并根据需要进行多次迭代以最大化实体数量。

_process_results将提取的结果字符串解析成未定向的单一图(Unipartite Graph)。

工作流程

1.使用 __call__ 对一组文本进行处理,提取实体和关系。

2._process_document对每个文档单独调用LLM,并可能进行多次迭代以提取更多信息。

3._process_results将所有文档的处理结果整合到一个图中。

4.使用 _unpack_descriptions 和 _unpack_source_ids 辅助函数来处理图节点和边的数据。

实体和关系总结

有了一个实体和关系的图表,每个都有一个描述列表,可以将这些列表汇总到每个实体和关系的单个描述中。这是通过向LLM询问一个简短的摘要来完成的,该摘要从每个描述中捕获所有不同的信息。

这部分代码就不细看了,直接看下图就可以明白同样是调用LLM然后将输出补充在上一步的生成的数据后面。

实体解析(默认情况下未启用,所以代码就不看了)

图提取的最后一步是解析任何代表相同现实世界实体但名称不同的实体。由于这是通过LLM完成的,并且我们不想丢失信息,我们希望对此采取保守、非破坏性的方法。

然而,我们目前对实体决议的实施是破坏性的。它将向LLM提供一系列实体,并要求其确定哪些实体应该合并。然后,这些实体被合并成一个单一的实体,并更新它们的关系。

我们目前正在探索其他实体解决技术。在不久的将来,实体解析将通过在实体变体之间创建一个边缘来执行,表明实体已被索引引擎解析。这将允许最终用户撤销索引端分辨率,并使用类似的过程添加自己的非破坏性分辨率。

这部分的描述很有趣,是一个值得探索的实体混淆的改进办法。英文原文如下。

We are currently exploring other entity resolution techniques. In the near future, entity resolution will be executed by creating an edge between entity variants indicating that the entities have been resolved by the indexing engine. This will allow for end-users to undo indexing-side resolutions, and add their own non-destructive resolutions using a similar process.

声明的提取

最后,作为一个独立的工作流程,我们从源TextUnits中提取声明。这些声明代表了积极的事实陈述,具有评估状态和时间限制,也就是论文中的协变量

这部分与实体和关系提取很像,就不详细展开了。

工作流程如下

1.使用__call__对一组文本和相关参数进行处理,提取声明。

2._process_document对每个文档单独调用LLM,并可能进行多次迭代以最大化声明数量。

3._parse_claim_tuples将提取的字符串声明解析为结构化的字典列表。

4.使用_clean_claim清理和标准化声明数据。

6.1.3.3 Graph增强

社群检测

在这一步中,使用分层莱顿算法生成实体社区的层次结构。这种方法将把递归的社群应用到图表中,直到达到社区规模的阈值。这将使我们能够了解图表的社群结构,并提供一种以不同粒度水平导航和总结图表的方法。

graph嵌入

在这一步中,使用Node2Vec算法生成图的向量表示。这将使我们能够理解图的隐式结构,并提供一个额外的向量空间,以便在查询阶段搜索相关概念。

graph输出

一旦graph增强步骤完成,最终的实体关系表将在其文本字段嵌入文本后发出。

这部分的核心内容主要在

graphrag/index/verbs/graph/clustering

graphrag/index/verbs/graph/embed

verbs文件夹内的各种文件就是datashaper使用的workflow中的每一个具体步骤。

具体看一下代码里的函数

函数 cluster_graph

这是一个装饰器@verb标注的函数,用于将图数据应用层次聚类算法,并输出新的列,包含聚类后的图和图的层级。

接收参数包括输入数据、回调函数、策略配置、要操作的列名、输出列名等。

使用networkx库对图进行操作,并将结果填充到Pandas DataFrame中。

函数 apply_clustering

接收一个图的字符串表示(GraphML格式)或图对象、社区信息和层级,然后对图应用聚类。

为图的节点和边添加额外的属性,例如聚类ID、层级、随机生成的UUID等。

枚举类 GraphCommunityStrategyType

定义了可用的图社区策略类型,目前只定义了leiden

函数 run_layout

据提供的策略和图数据,运行布局算法。

加载图数据,并根据策略生成社区(Cluster)信息。

社群信息

Communities:社区信息的类型注解,表示一个包含层级、社区ID和节点列表的元组列表。

随机UUID生成

使用gen_uuid函数生成随机UUID,为图的节点和边提供唯一标识。

莱顿算法这里就不详细展开了,或者说也没法展开,因为strategy文件中的代码是这样的逻辑。

run 函数根据提供的参数配置Leiden算法并调用 _compute_leiden_communities 函数。

_compute_leiden_communities 函数使用 hierarchical_leiden 方法计算社区,并返回每个层级的社区映射。

run 函数根据需要的层级整理社区结果,并将每个社区的节点ID以列表形式返回。

而莱顿算法本体是这样调用的。

from graspologic.partition import hierarchical_leiden

源自另一个图算法库 graspologic-org/graspologic: Python package for graph statistics (github.com)

6.1.3.4 社群总结

此时,我们有一个实体和关系的功能图,实体的社群层次结构,以及node2vec嵌入。

现在,我们希望以社群数据为基础,为每个社群生成报告。这可以让我们对graph有了高水平的理解。例如,如果A是顶级社群,我们将收到一份关于整个图表的报告。如果社群是较低级别的,我们将收到一份关于本地集群的报告。

生成社群报告

在此步骤中,我们使用LLM生成每个社群的摘要。这将使我们能够理解每个社群中包含的不同信息,并从高层次或低层次的角度对graph进行范围化理解。这些报告包含概述,并参考了社群子结构中的关键实体、关系和声明。

总结社群报告

在此步骤中,每个社群报告然后通过LLM进行汇总,以供速记使用。

社群嵌入

在这一步中,我们通过生成社区报告、社区报告摘要和社区报告标题的文本嵌入来生成我们社区的矢量表示。

社群及社群报告数据表的输出

此时,进行记录工作,输出社群社群报告

这部分的核心程序路径如下。

graphrag/index/graph/extractors/community_reports

这部分代码的主要逻辑和论文一致,具体可以参考3.3中的内容,看流程图就可以清晰地发现如何将上下文依次输送到LLM的prompt中。

6.1.3.5 文档处理(Document Processing)

用列增强(仅限CSV)

如果工作流在CSV数据上运行,您可以配置工作流,以向文档输出添加其他字段。这些字段应该存在于传入的CSV表上。

链接到TextUnits

在此步骤中,我们将每个文档链接到第一阶段创建的文本单元。这使我们能够了解哪些文件与哪些文本单元有关,反之亦然。

文档嵌入

在这一步中,我们使用文档切片的平均嵌入生成文档的矢量表示。我们重新分块文档,不重叠块,然后为每个块生成一个嵌入。我们创建按令牌计数加权的这些块的平均值,并将其用作文档嵌入。这将使我们能够理解文档之间的隐含关系,并帮助我们生成文档的网络表示。

6.1.3.6 网络可视化(Network Visualization)

在工作流程的这一阶段,我们执行一些步骤来支持现有图形中高维向量空间的网络可视化。在这一点上,有两个逻辑图在起作用:实体关系图和文档图。

对于每个逻辑图,我们执行UMAP维度缩小,以生成图形的二维表示。这将使我们能够在二维空间中可视化图形,并了解图形中节点之间的关系。然后,UMAP嵌入作为节点表发出。此表的行包括一个判别器,指示节点是文档还是实体,以及UMAP坐标。

6.2 query查询部分

6.2.1 概述

本地搜索

本地搜索方法通过将人工智能提取的知识图中的相关数据与原始文档的文本块相结合来生成答案。这种方法适用于需要了解文件中提到的特定实体的问题(例如洋甘菊的治疗特性是什么?)

# Local search
python -m graphrag.query \
--root ./ragtest \
--method local \
"Who is Scrooge, and what are his main relationships?"

全局搜索

全球搜索方法通过以地图减少的方式搜索所有人工智能生成的社区报告来生成答案。这是一种资源密集型方法,但通常可以很好地回答需要了解整个数据集的问题(例如本文本中提到的草药最重要的价值是什么?)

# Global search
python -m graphrag.query \
--root ./ragtest \
--method global \
"What are the top themes in this story?"

问题生成

此功能获取用户查询列表,并生成下一个候选问题。这对于在对话中生成后续问题或生成问题列表非常有用,以便调查员深入研究数据集。

这部分不是重点,所以就不详细看代码结构了。

6.2.2 全局(global)query

给定用户query和(可选的)对话历史记录,全局搜索方法使用来自graph社群层次结构的指定级别的LLM生成的社区报告集合作为上下文数据,以map-reduce方式生成响应。在map步骤中,社区报告被分割成预定义大小的文本块。然后,每个文本块用于生成包含点列表的中间响应,每个点都附有表示该点重要性的数字评级。在reduce步骤中,从中间响应中过滤的一组最重要的点被聚合并用作生成最终响应的上下文。

全球搜索响应的质量可能会受到为获取社群报告而选择的社群层次结构水平的严重影响。较低的层次结构及其详细报告往往会产生更彻底的响应,但由于报告量大,也可能增加生成最终响应所需的时间和LLM资源。

这部分和论文内容一致,具体可以看3.4小节来更深入原理。概括来讲,Global Search 使用特定层次的 Community Report 的集合。由于单个上下文可能无法容纳这些 Community Reports,需要进行 Map-Reduce 操作:

1.把 Community Reports 切分为多个部分,然后每个部分基于用户 query 使用 LLM 并发进行推理,每个部分总结几个主要的总结点。在推理总结时,会让 LLM 生成权重,方便最后进行 reduce 操作。

2.把每个部分推理的结果进行合并,将所有总结点进行 reduce 操作——再次使用 LLM 进行摘要总结。

接下来看看代码是如何执行的。

create_final_community_reports.parquet文件中获取选中的community_report数据

community_reports = _compute_community_weights(
    community_reports=community_reports,
    entities=entities,
    weight_attribute=community_weight_name,
    normalize=normalize_community_weight,
)

selected_reports = [
    report
    for report in community_reports
    if report.rank and report.rank >= min_community_rank
]

基于选中的community_reports列表批量请求大模型(这里由于community_reports内容较多,超过默认值12000token),让大模型基于每个community_report回答用户的问题,具体的prompt模板如下

MAP_SYSTEM_PROMPT = """
---Role---

You are a helpful assistant responding to questions about data in the tables provided.

---Goal---

Generate a response consisting of a list of key points that responds to the user's question, summarizing all relevant information in the input data tables.

You should use the data provided in the data tables below as the primary context for generating the response.
If you don't know the answer or if the input data tables do not contain sufficient information to provide an answer, just say so. Do not make anything up.

Each key point in the response should have the following element:
- Description: A comprehensive description of the point.
- Importance Score: An integer score between 0-100 that indicates how important the point is in answering the user's question. An 'I don't know' type of response should have a score of 0.

The response should be JSON formatted as follows:
{{
    "points": [
        {{"description": "Description of point 1 [Data: Reports (report ids)]", "score": score_value}},
        {{"description": "Description of point 2 [Data: Reports (report ids)]", "score": score_value}}
    ]
}}

The response shall preserve the original meaning and use of modal verbs such as "shall", "may" or "will".

Points supported by data should list the relevant reports as references as follows:
"This is an example sentence supported by data references [Data: Reports (report ids)]"

**Do not list more than 5 record ids in a single reference**. Instead, list the top 5 most relevant record ids and add "+more" to indicate that there are more.

For example:
"Person X is the owner of Company Y and subject to many allegations of wrongdoing [Data: Reports (2, 7, 64, 46, 34, +more)]. He is also CEO of company X [Data: Reports (1, 3)]"

where 1, 2, 3, 7, 34, 46, and 64 represent the id (not the index) of the relevant data report in the provided tables.

Do not include information where the supporting evidence for it is not provided.
"""

中文翻译

MAP_SYSTEM_PROMPT = """ ---角色---

你是一个很好的助手,负责回答有关所提供表格中数据的问题。

---目标---

生成一个由关键点组成的列表来回应用户的问题,总结输入数据表中的所有相关信息。

你应该使用下面提供的数据表中的数据作为生成回答的主要上下文。 如果你不知道答案,或者输入数据表中没有足够的信息来提供答案,就直接说出来。不要编造任何东西。

回答中的每个关键点应该包含以下元素:

  • 描述:对这个点的全面描述。
  • 重要性得分:一个0到100之间的整数值,表示这个点在回答用户问题中的重要性。一个“我不知道”类型的回答应该得到0分。

回答应该按照以下JSON格式:

{{
    "points": [
        {{"description": "Description of point 1 [Data: Reports (report ids)]", "score": score_value}},
        {{"description": "Description of point 2 [Data: Reports (report ids)]", "score": score_value}}
    ]
}}

回答应该保留原意并使用情态动词,如"shall"、"may"或"will"。

由数据支持的点应该列出相关的报告作为参考,如下所示: "这是一个由数据支持的例子句子 [Data: Reports (report ids)]"

不要在单个引用中列出超过5个记录ID。相反,列出最相关的前5个记录ID,并添加"+more"来表示还有更多。

例如: "X人是Y公司的拥有者,并且受到许多不当行为的指控 [数据:报告(2, 7, 64, 46, 34, +more)]。他也是X公司的CEO [数据:报告(1, 3)]"

其中1, 2, 3, 7, 34, 46和64代表所提供表格中相关数据报告的ID(而不是索引)。

不要包含没有提供支持证据的信息。

把第二步获取到的大模型返回的结果列表整合成一个完整的答案,当然,这个整合过程也是通过大模型完成的,具体的prompt如下

REDUCE_SYSTEM_PROMPT = """
---Role---

You are a helpful assistant responding to questions about a dataset by synthesizing perspectives from multiple analysts.

---Goal---

Generate a response of the target length and format that responds to the user's question, summarize all the reports from multiple analysts who focused on different parts of the dataset.

Note that the analysts' reports provided below are ranked in the **descending order of importance**.

If you don't know the answer or if the provided reports do not contain sufficient information to provide an answer, just say so. Do not make anything up.

The final response should remove all irrelevant information from the analysts' reports and merge the cleaned information into a comprehensive answer that provides explanations of all the key points and implications appropriate for the response length and format.

Add sections and commentary to the response as appropriate for the length and format. Style the response in markdown.

The response shall preserve the original meaning and use of modal verbs such as "shall", "may" or "will".

The response should also preserve all the data references previously included in the analysts' reports, but do not mention the roles of multiple analysts in the analysis process.

**Do not list more than 5 record ids in a single reference**. Instead, list the top 5 most relevant record ids and add "+more" to indicate that there are more.

For example:

"Person X is the owner of Company Y and subject to many allegations of wrongdoing [Data: Reports (2, 7, 34, 46, 64, +more)]. He is also CEO of company X [Data: Reports (1, 3)]"

where 1, 2, 3, 7, 34, 46, and 64 represent the id (not the index) of the relevant data record.

Do not include information where the supporting evidence for it is not provided.

---Target response length and format---

{response_type}

---Analyst Reports---

{report_data}

---Target response length and format---

{response_type}

Add sections and commentary to the response as appropriate for the length and format. Style the response in markdown.
"""

NO_DATA_ANSWER = (
    "I am sorry but I am unable to answer this question given the provided data."
)

GENERAL_KNOWLEDGE_INSTRUCTION = """
The response may also include relevant real-world knowledge outside the dataset, but it must be explicitly annotated with a verification tag [LLM: verify]. For example:
"This is an example sentence supported by real-world knowledge [LLM: verify]."
"""

中文翻译

REDUCE_SYSTEM_PROMPT = """ ---角色---

你是一个乐于助人的助手,通过综合多位分析师从多个角度对数据集的分析来回答有关数据集的问题。

---目标---

生成一个符合目标长度和格式的回答,回应用户的问题,总结关注数据集不同部分的多位分析师的所有报告。

请注意,下面提供的分析师报告按照重要性降序排列

如果你不知道答案,或者提供的报告中没有足够的信息来提供答案,就直接说出来。不要编造任何东西。

最终的回答应该从分析师的报告中移除所有不相关信息,并将清理后的信息合并成一个全面的回答,提供适合回答长度和格式的所有关键点和含义的解释。

根据长度和格式的需要,在回答中添加部分和评论。以Markdown格式设置回答的样式。

回答应保留原始意义并使用情态动词,如"shall"、"may"或"will"。

回答还应保留分析师报告中包含的所有数据引用,但不要提及多位分析师在分析过程中的角色。

不要在单个引用中列出超过5个记录ID。相反,列出最相关的前5个记录ID,并添加"+more"来表示还有更多。

例如:

"X人是Y公司的拥有者,并且受到许多不当行为的指控 [数据:报告(2, 7, 34, 46, 64, +more)]。他也是X公司的CEO [数据:报告(1, 3)]"

其中1, 2, 3, 7, 34, 46和64代表相关数据记录的ID(而不是索引)。

不要包含没有提供支持证据的信息。

---目标回答长度和格式---

{response_type}

根据长度和格式的需要,在回答中适当添加部分和评论。以Markdown格式设置回答的样式。 """

没有数据的回答是:     "很抱歉,根据提供的数据,我无法回答这个问题。"

一般知识指导如下: """ 回答也可以包括相关的现实世界知识,但必须明确地用一个验证标签进行注释 [LLM: verify]。例如: "这是由现实世界知识支持的一个例子句子 [LLM: verify]。" """

将整合后的结果直接返回给用户。

6.2.3 局部(local)query

给定用户query和可选的对话历史记录,本地搜索方法从知识图中识别一组与用户输入语义相关的实体。这些实体作为知识图的接入点,可以提取进一步的相关细节,如连接实体、关系、实体协变量和社群报告。此外,它还从与已识别实体相关联的原始输入文档中提取相关文本块。然后对这些候选数据源进行优先排序和过滤,以适应预定义大小的单个上下文窗口,该窗口用于生成对用户query的响应。

从上面的 dataflow 可以看出,上下文中的内容种类非常多。首先,使用用户 query 从知识图谱中通过 embedding 方式获取相关的实体,然后将实体相关的多种信息进行排序,选取部分放入上下文中,具体包括:

1.关联的原始文本(Text Units)

2.实体关联的 Community Reports

3.相关联的实体(Entities)

4.实体相关的关系信息

5.实体的属性(Covariates,例如,实体是苹果,颜色可以理解为一个属性)

再加上会话历史,一次 query 使用的 token 比通常的 RAG 方式要多不少。

local查询模式下,与global模式相比,多了几个数据来源create_final_text_units.parquetcreate_final_relationships.parquet

先将query转化为embedding向量,然后跟图谱中的实体描述进行相似度检索,也就是根据query找实体。

接下来是判断一下有没有历史对话记录,如果有也记录到上下文里面。

_build_community_context()然后利用前面query检索到的实体,通过实体反向找到这些实体所在的社群报告,然后取出社群报告的标题和摘要放到上下文里面。

_build_local_context()用实体去关联关系和声明,因为实体关系和声明都是直接跟实体关联的,所以可以直接根据实体id从表里面查询出来。

最后一步_build_text_unit_context()是根据实体找实体的原始文本块。

有了检索的上下文之后,就可以做生成。

生成的prompt如下与global的map部分很像,只是不需要重要性打分,这里就不再赘述了。

6.3 与RAG对比

参考https://www.cnblogs.com/fanzhidongyzby/p/18347640/graphrag-summary

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值