作者:陈梓康
众所周知,GraphRAG将文档内容抽取为知识图谱三元组后,实际上仅保留了关联性知识信息,因此不可避免地会丢失原文的一些内容细节。在对数据完整度要求严格的业务场景,如金融、医疗、保险等行业,这是不希望看到的结果。为了解决此类业务诉求,我们将文档结构信息引入GraphRAG链路,以解决知识抽取后原文信息损失的问题。同时,我们也从端到端优化了GraphRAG链路,大幅提升了知识图谱的构建和检索性能。
1. 摘要
GraphRAG 是一个创新的知识检索与问答增强框架,它巧妙地结合了图数据库技术与检索增强生成(RAG)方法。GraphRAG 往往在处理复杂数据关系任务上取得比传统 RAG 更好地效果,是当下 LLM 领域热门的工程方向之一。
作为 DB-GPT 万星开源项目的重要组件之一,蚂蚁自研的 GraphRAG 近期获得了显著的性能提升------改进的 GraphRAG 在原有的社区摘要增强和混合检索支持的基础上,新增了文档结构(Document Structure)索引,进一步提升了知识图谱的丰富度和知识召回的完备性。同时,继续兼容基于 AntV G6 引擎和 TuGraph 引擎的知识图谱的酷炫渲染,GraphRAG 让文档中的复杂数据关系一目了然。
本文将详细介绍 DB-GPT GraphRAG 系统的最新技术改进,重点包括文档结构索引的创新实现、知识图谱构建的效率优化,以及多维检索机制带来的效果提升。通过与业界标杆的对比测试,验证了优化方案在降低资源消耗的同时,保持了知识表示的完整性和准确性。文章同时展示了实际应用案例,为读者提供了直观的技术参考。
2. 知识图谱构建
2.1 文档结构图谱
2.1.1 文档解析
为提升文档解析的精确度,我们对 Markdown 格式文件实现了增强支持。系统通过识别标准格式文档中的标题层级符号(如"#"、"##"等),将文档智能切分为多个独立的文本块(content block, 简称 chunk)。这种结构化解析方法不仅保留了原始内容的完整性,更重要的是准确捕获了内容之间的层级关系。
基于这些层级关系,我们构建了一个有向图结构:
- 每个节点代表一个文本块(chunk)。
- 边的类型分为两种(有向边):
- next:连接同一层级的相邻文本块,表示内容的顺序关系。
- include:连接不同层级的文本块,表示上下级包含关系。
为了直观展示文档结构解析的过程,让我们通过一个具体的示例来理解这个机制,考虑以下 Markdown 文档片段:
markdown
# 机器学习基础
## 监督学习
### 分类算法
### 回归算法
## 无监督学习
### 聚类算法
这个文档会被切分成以下文本块(用[...]表示具体内容):
- chunk1: "# 机器学习基础 [...]"
- chunk2: "## 监督学习 [...]"
- chunk3: "### 分类算法 [...]"
- chunk4: "### 回归算法 [...]"
- chunk5: "## 无监督学习 [...]"
- chunk6: "### 聚类算法 [...]"
同时,这些文本块(chunk)之间存在着两种有向关系(包含 include、连接 next):
- include 边:chunk1 包含 chunk2 和 chunk5;chunk2 包含 chunk3 和 chunk4;chunk5 包含 chunk6。
- next 边:chunk2 连接 chunk5;chunk3 连接 chunk4。
2.1.2 文档结构图谱构建
自然地,得益于文本块(chunk)之间的有向关系,可以将文件结构组织为有向图。顺便,将其写入到知识图谱(基于 TuGraph 底座)。如图所示,在图的上半部分,其中的节点可以是文件的一个内容分片/文本块(chunk,紫色节点),边则代表了文本块(chunk)之间在原文档中的结构关系。
通俗地说,Document 1 就像一本教程的主目录,它通过包含(include)关系连接到不同的章节(Chunk 1、2、3)。每个章节又可以包含(include)更具体的知识点(Entity)。同时,通过 next 关系,我们能清晰地看到学习的推荐顺序。
2.2 三元组图谱
完成文档结构图构建后,下一个关键步骤是通过语义理解提取文档中的知识实体与关系,构建结构化知识图谱。我们采用了创新的"上下文增强"方法,通过多维度信息融合提升知识抽取的准确性。
2.2.1 相似文本块检索
- 对于每个待处理的文本块(chunk),我们首先通过向量相似度检索找到语义相近的其他 top k 个文本块(chunk)。
- 这些相似的文本块将作为补充上下文,帮助 LLM 更好地理解当前内容。
2.2.2 三元组知识抽取
- 将当前文本块和相关上下文一起作为一个请求,并发送到 LLM 服务端。
- 基于更丰富的上下文,LLM 能够更准确地识别和抽取出知识三元组。
- 抽取的结果遵循 【entity】--(relation)->【entity】的标准格式。
2.2.3 并发抽取优化
- 文本块的三元组抽取是相对独立的进程,因此,我们采用并发的形式来批次执行进程。
- 考虑到三元组知识抽取任务的特点:
- 各文本块之间的处理相互独立。
- 计算过程无共享状态。
- 任务粒度适中。
- 我们实现了基于文本块的并发处理机制:
- 通过批处理方式,平衡并发数量和 LLM 调用资源。
- 从而,实现了处理速度和资源利用的最优平衡。
- 该提出的并发优化方案取得了显著的性能提升:任务处理时间降低至原有耗时的 20%。这种五倍的性能提升充分证明了并发策略在大规模知识抽取场景下的效果。在实际运行中,我们也观察到了两个主要的性能瓶颈:
- LLM 并发请求限制
- 大语言模型的 API 通常对并发请求数有严格限制。
- 即使系统能够并行处理更多任务,也会受限于 API 的调用配额。
- Token 生成速率限制
- LLM 服务对每分钟生成的 token 总量有明确上限。
- 这种限制直接影响了系统的最大处理吞吐量。
- LLM 并发请求限制
这些限制是由 LLM 服务的基础架构决定的,超出了我们的 GraphRAG 框架的优化范围。不过,这也为我们指明了未来的优化方向:
- 探索模型部署的弹性伸缩方案。
- 研究请求批处理的智能调度策略。
- 优化 token 使用效率(比如,精简系统提示词等等)。
"上下文增强"方法的优势在于:
- LLM 能够获得更全局的知识视角,不会被单个文本块的局部信息所限制。
- 通过上下文的补充,能够建立起更准确的实体关系。
- 最终形成的知识图谱质量更高,关系更加准确和完整。
例如,当处理一个关于"深度学习"的文本块(chunk)时,通过检索相似内容,LLM 可以(通过系统支持的相似度算法)同时看到"神经网络结构"、"反向传播算法"等在意义上相似的文本块(chunk),从而帮助 LLM 更准确地抽取出三元组/实体之间的关系,如: 【深度学习】--(使用)->【反向传播算法】; 【神经网络】--(是)->【深度学习模型】。
至此,加上前文提到的文档结构,我们拓展了 GraphRAG 对于 Graph 的定义范畴:
其中,
- 三元组有向图(Triplets Graph):捕获实体间语义关系。
- 文档结构图(Document Structure Graph):保持知识层级结构。
这种图结构不仅仅绘制了一张"知识地图",还为 LLM 顺藤摸瓜式地依据 entity 回溯到原始文本块(chunk)提供了可能性。在未来,我们或许构建一个更加复杂、覆盖更加全面的信息的图,支持更加复杂的检索算法。
2.3 社区摘要总结
为了更好地宏观地、全局地理解和组织图谱中的知识,我们引入了社区层面的知识总结和归纳机制。这个过程分为三个关键步骤:
2.3.1 社区发现
GraphRAG 默认采用 Leiden 算法进行社区检测。它以其高效和准确的特点著称,该算法能够:
- 自动发现知识实体间的紧密关联群组。
- 在保持社区内部联系紧密的同时,确保社区间边界清晰。
- 适应性地确定合适的社区规模。
2.3.2 社区文本化
对于每个识别出的社区,我们进行了系统的信息提取(即,图的展开过程):
- 收集社区内所有实体的属性信息、提取实体间的关系描述。
- 将图结构信息转换为结构化文本表示。
2.3.3 社区总结
基于文本化的社区信息,我们通过以下步骤生成社区摘要:
- 调用 LLM 分析社区的核心主题和关键概念,生成凝练的社区主题描述(使用并发处理社区摘要总结功能)。
- 将摘要信息持久化存储,便于后续检索和分析。
因此,得益于文档结构+三元组结构,以及社区摘要总结,新版的 GraphRAG 不仅提供了知识图谱的局部视角,帮助人们发现知识间的紧密联系,还为知识图谱的应用提供了更高层次的语义理解基础。
2.4 图数据建模
在设计 GraphRAG 数据持久化的阶段,我们选择了 TuGraph 作为底层存储引擎。同时,在原来的图模型(Graph Schema)的基础上,新设计了一套完整的图模型,使得图谱表达、存储和索引文档结构的部分。因此,新的图模型具备如下特点:
- 完整保留文档的层级结构。
- 清晰表达文本块之间的关系。
- 支持三元组知识实体的关联表示。
2.4.1 点类型
GraphRAG 定义了三种基本的节点类型:
document
:表示文档对象- 核心属性:id、name。
- 可选属性:community_id(用于社区划分)。
chunk
:表示文档的文本块- 核心属性:id、name。
- 可选属性:community_id(用于社区划分)、content(块的具体内容)。
entity
:表示知识实体- 核心属性:id、name
- 可选属性:community_id(用于社区划分)、description(实体描述)。
2.4.2 边类型
为了准确表达节点之间的关系,GraphRAG 定义了五种特定的边类型:
- 包含关系边:
document_include_chunk
:文档包含文本块。chunk_include_chunk
:文本块间的层级包含。chunk_include_entity
:文本块包含知识实体。
- 顺序关系边:
chunk_next_chunk
:文本块之间的顺序关系。
- 语义关系边:
relation
:三元组实体之间的语义关系。
每种边类型都包含基本属性(id、name)和可选的描述信息(description)。对于实体间的关系边,GraphRAG 还额外记录了 chunk_id,用于追踪关系的来源上下文。
基于这种建模方式,我们不仅实现了文档的结构化存储,还为后续的知识图谱构建和语义分析打下了基础。
3. 知识图谱检索
知识图谱的构建为智能检索奠定了基础,但如何高效地检索和利用这些结构化知识仍面临诸多挑战。我们设计了一套多层次的检索框架,通过融合不同维度的知识表示,实现精准且全面的信息获取。
3.1 关键词抽取
首先,GraphRAG 需要准确理解用户的查询意图。通过调用 LLM 实现查询解析:用户输入 Query -> 关键词。比如,提取出关键的搜索词:"机器学习"、"入门"、"基础"、"教程"等。
3.2 局部检索:三元组图谱检索
基于提取的关键词,我们首先通过简单的图查询语句,定位到相关的知识实体(entity)和文本块(chunk)。次后,GraphRAG 凭借图的多跳遍历,探索实体之间的关联关系,获取更多的关联信息。
为了更好地理解多跳遍历,我们举一个例子。在知识图谱中,多跳遍历这就像是:
- 首先发现一个 AI 相关的知识点,发现它与"深度学习"有关(一跳)。
- 顺着这条线索,又找到了"深度学习"与"神经网络"的关联(二跳)。
- 继续探索,发现"神经网络"又与"反向传播"有密切关系(多跳)。
- 通过这种方式,GraphRAG 不仅可以找到最直接相关的答案,还能借助多跳探索,得到一张子图,从而提供更加全面和丰富的信息。
3.3 全局检索:社区摘要检索
通过社区检测算法 Leiden,GraphRAG 能够获取与查询相关的知识社区概览,提供了更宏观的知识视角。
3.4 原文检索:文档结构检索
对于检索到的文本块,系统通过文档结构中的"包含"关系(include)"向上"回溯,直到回溯至文档节点(document)。这种溯源机制保证了知识的可追溯性,还作为原始语境,以帮助用户理解上下文信息。笔者暂且称之为"知识溯源"。
- 以 TuGraph 查询为例,解释知识溯源的过程:
假设用户询问:"TuGraph 在金融风控方面的应用效果如何?"
第一步:定位相关文本块 系统首先找到了这个关键信息(chunk):
在金融风控方面,TuGraph已帮助金融机构提升反欺诈和反洗钱的效率,例如,某银行的反洗钱风险事件分析效率提升了790%,并在信贷平台中提高了713%的风控覆盖区分率。
第二步:向上溯源 系统通过 include 关系,逐层回溯:
1. 发现这个文本块属于"应用案例"部分。
2. "应用案例"进一步属于 TuGraph 的整体介绍文档。
3. 最终定位到完整的文档结构:
plain
TuGraph简介
├── 主要特性
│ ├── 高效性
│ ├── 分布式架构
│ └── 多样功能
└── 应用案例
└── 金融风控应用 <- 当前内容位置
从"当前内容位置"到 TuGraph 简介的过程,笔者称之为"向上溯源"。
3.5 生成回答
最后 GraphRAG 将来自三个链路的检索结果整合起来,形成一个完整的知识集合:
- 局部检索提供具体的知识点和关联关系。
- 全局检索提供领域概览和知识聚类。
- 原文检索提供可溯源的文档依据。
这些多维度的知识被输入到 LLM 中,模型会结合用户的查询意图,生成一个全面、准确且连贯的回答,最后返回给用户。GraphRAG 所做的一切,均是为了确保了答案的准确性,为用户提供深入的知识洞察。
4. 效果演示
4.1 可视化
目前,用户可以快速地在 DB-GPT v0.6.2 版本的前端应用中体验到该功能,并且该前端提供了清晰直观的知识图谱可视化图。生成的知识图谱由 AntV G6 引擎驱动渲染。
4.2 问答示例
用户询问的是 "TuGraph" 的基本信息。系统通过关键词提取,准确定位到了相关的知识节点。这验证了笔者前面讨论的查询理解机制的有效性。
特别值得注意的是答案底部的引用部分(蓝色引用框),这正是之前讨论的"知识溯源"机制的体现:
- 借助文档结构的实现,GraphRAG 不仅可以回答用户问题,还可以标明信息的具体来源。
- 通过引用原文的方式,GraphRAG 确保了信息的可信度,方便用户进一步探索。
5. 性能测试
5.1 测试结果
为了方便对比,我们选用 graphrag-test.md 测试文本,来分别测试我们版本的 GraphRAG 和微软版本的 GraphRAG 的性能。通过实测发现,我们的方案在保持相近的文档输入规模(42,631 tokens)的情况下,取得了一下成果(相比微软版本的 GraphRAG方案,9 月份版本):
- 总 Token 消耗是微软 GraphRAG 的 42.9%(**417,565 **vs 972,220)。
- 特别是,生成 Tokens 量是微软 GraphRAG 的 18.4%(41,797 vs 227,230)。
- 构建知识图谱的时间是微软 GraphRAG 的 80.1% (170s vs 210s)。
- 我们支持文档结构图谱, 微软 GraphRAG 不支持。
- 同时,我们的图谱结构保持了相当的复杂度(734节点/1164 边 vs 779节点/967边),确保了知识表示的完整性和覆盖率大约保持同一水平。
GraphRAG (DB-GPT) | GraphRAG (Microsoft) | |
---|---|---|
Doc Tokens | 42631 | 42631 |
Triplets Graph | 734 nodes, 1064 edges | 779 nodes, 967 edges |
Doc Structure Graph | 76 nodes, 1090 edges | N/A |
Prompt Tokens | 375768 | 744990 |
Completion Tokens | 41797 | 227230 |
Total Tokens | 417565 | 972220 |
Indexing Time | 170s | 210s |
- 全局检索:性能有明显提升。
GraphRAG (DB-GPT) | GraphRAG (Microsoft) | |
---|---|---|
Time | 8s | 40s |
Tokens | 7432 | 63317 |
- 局部搜索:性能基本相当。
GraphRAG (DB-GPT) | GraphRAG (Microsoft) | |
---|---|---|
Time | 15s | 15s |
Tokens | 9230 | 11619 |
5.2 结果分析
微软 GraphRAG 方案在 Map 步骤中,将社区报告分割成预定义大小的文本块。每个文本块用于生成包含点列表的中间回答,每个点都有相应的数值评分,表示该点的有用度(评分标准:有用度 0-100,LLM 作为"评委")。然后,在 Reduce 步骤中,从中间响应中筛选出最有用的一些点,并将它们聚合起来,作为生成最终回答的上下文。为了获得 map-reduce 中每个点的"有用度"评分,需要多次调用 LLM,因此微软 GraphRAG 消耗了更多的 tokens 和时间。实际上,这种中间回答缺少了全局视野/全局信息,导致 LLM 在评估中间回答的有用度时,存在偏差。
- 为了更好理解偏差产生的原因,以三国演义为例。用户问题: "曹操为什么会失去荆州的统治权?"
- 社区摘要块和中间回答:
- 片段1: "荆州原本由刘表治理。建安十三年(208年),刘表病逝,其子刘琮投降曹操。曹操派兵南下,占领荆州。"
- 中间回答:荆州最初是通过刘琮投降而落入曹操之手。
- LLM 评分:85(高分,因为直接回答了曹操如何获得荆州)。
- 片段2: "周瑜和诸葛亮在赤壁之战中,以火攻大败曹操军队。曹操被迫退往北方。"
- 中间回答:曹操在赤壁之战中战败,不得不撤离。
- LLM 评分:70(中分,因为描述了曹操退往北方,而荆州在南方,说明曹操基本不可能获得统治权)。
- 片段3: "刘备派关羽镇守荆州。关羽善用水军,又因当地百姓拥护,使得荆州基业稳固。"
- 中间回答:关羽接管了荆州的防务。
- LLM 评分:30(低分,因为看似只是一个人事安排,和曹操无关)。
- 片段1: "荆州原本由刘表治理。建安十三年(208年),刘表病逝,其子刘琮投降曹操。曹操派兵南下,占领荆州。"
- 偏差分析:
- LLM 对片段3的评分较低,因为单独看这段内容,似乎只是一个简单的人事调动。但实际上,关羽镇守荆州是极其关键的信息,因为:不仅展示了刘备势力的实际控制,表明了荆州易主的完整过程,还反映了民心向背的重要性。片段 3 将会被微软 GraphRAG 按照算法筛选掉。如此,偏差产生。
进一步地,当图的规模增大时,即节点数量和边数量增多,建议选择直接通过相关的社区检测(community detection)算法来计算社区的聚集程度,以此定义某些被发现的子图对于用户问题的重要程度,而并不使用有用度评分机制。笔者认为,这才是在全局索引维度下,GraphRAG 的职责之一------提供"全局信息"。在大规模图的情况下,微软 GraphRAG 的 map-reduce 方法的真实效果如何,索引性能是否依旧优秀?后续或许可以通过进行更多的实验来检验这些观点是否正确。
微软 GraphRAG 采用并行处理策略,对社区摘要块进行批量处理以生成中间答案,这种方法虽提高了处理速度,但带来了额外的 tokens 和时间开销。在生成中间态回答的同时,一定程度上引入了信息损耗和偏差:
(1)信息完整性损失:缺乏跨块信息关联。
(2)语义理解偏差:局部处理影响全局判断"。
无论如何,我们的版本考虑到了这些隐患和偏差。当然,微软 GraphRAG 在开源社区取得了巨大的成功,也是笔者学习和借鉴的对象。
总得来说,我们取得了不错的效果:在构建同样规模的知识图谱 的情况下,我们在构建图谱这个任务上,花费了更少的时间(约80%),**消耗了更少的 tokens **(约40%)。同时,在回答需要全局检索的用户问题时,根据测试结果,我们版本的 GraphRAG 在时间和 tokens 的消耗上更具优势。此外,我们的 GraphRAG 得益于文档结构的支持,我们可以搜索原文,并将原文作为参考文本的一个部分返回给用户,让用户可以获得更可靠的原文信息。