GraphRAG 工作流

当前的 RAG 技术无法回答关于语料库的全局性问题,比如“这个数据集的主题是什么”。这一类问题不是可以通过检索增强技术解决的,因为答案一般不在某一段文本里面,正确答案需要理解整个语料库并给出抽象的总结,作者称这类问题为 query-focused summarization (QFS) 问题1。普通的 RAG 技术无法很好处理这个问题。

将整个语料库的文本放在上下文窗口里面又不大现实,就算能放得下,面对超长输入,LLM 经常会忘掉中间的信息

本文提出的 GraphRAG1 就是要解决这些问题

Tip

要理解 GraphRAG 最好要有知识图谱的背景,比如对社区、社区检测算法要有一定的了解。另外要对图这种数据结构有比较清楚的了解

flowchart LR
    Document --> TextChunk1 & TextChunk2 & ...

将文档划分为多个 Text Chunk(彼此之间可以有重叠)

flowchart LR
    TextChunk --> Entity & Relationship & Claim

每一个 Text Chunk 会被 LLM 用来提取知识图谱里的

  • 实体,包括 名字、类型、描述
  • 关系,包括 起点、描述、终点
  • 断言(Claim)
Tip

如果提取到重复的实体/关系(不考虑 描述)会将其合并,但每一个实体/关系的描述会被记录下来,即一个实体/关系可能会有多个描述

提取的原理就是写 Prompt 给大模型,这里的 Prompt 会用 Few-shot learning 和 In-Context learning 的技术作增强,可以想象,这里的 Prompt 很关键

除了精心编写 Prompt 以外,作者1还提出可以多次执行提取的步骤,这是为了鼓励 LLM 发现上一轮遗漏的实体。怎么鼓励呢?可以尝试告诉 LLM 说上一轮遗漏掉了非常多的实体

Tip

注意,Text Chunk 设置得越大,LLM 调用的次数就越少,速度越快。但作者1发现提取的实体量、关系的数量会变少,因此这里需要做好权衡,可以参考论文里的 Figure 2

flowchart LR
    Document(Document in the form of entity, relationships, and claims)
    subgraph community_tree
        root(Community1)
        root2(Community2)
        root3(Community3)
        root --> root2 & root3
        root --> dots(...)
        root2 --> dots1(...)
        root3 --> dots2(...)
        dots --> dots3(...)
    end
    Document --> community_tree

在上一个阶段后,其实我们已经为每个文档搭建了一个知识图谱,接下来就可以在这个知识图谱上用 Leiden 社区检测算法,该算法会返回知识图谱里面的社区(Graph Communities),注意这些社区是树状的(下称社区树),结构是有层次的

flowchart LR
    Community --> CommuniySummary

这一步需要用 LLM 得到每个社区的总结,针对不同类型的社区有不同的策略

  • 叶子社区
    • 将所有的关系按照 起点终点度数和降序排列,🤔 也许是度数和越高越重要
    • 按顺序处理,对于每条关系,依次添加如下的文本到上下文窗口里直到上下文窗口放不下
      • 起点 对应的 描述
      • 终点 对应的 描述
      • 任何跟这条关系有关联的断言
      • 这条关系的 描述
  • 非叶子社区,即下面都是子社区
    • 假装子社区不存在:按照上面的方法处理,但前提是不超过 LLM 的上下文窗口长度
    • 否则,对子社区进行排序,按照它包含的实体、关系的描述文本的总长度降序排列(🤔 文本越多信息越丰富越要保留)。然后依次将社区总结(短)替换为叶子社区那样拼接的描述(长),目的显然是要保留更细粒度的信息(社区 -> 社区包含的实体、关系)
Tip

所以,社区总结的处理方式是在社区树上用 Bottom-Up 的方式得到的

flowchart LR
    a(Random shuffled community summaries)
    a --> Chunk1 & Chunk2 & annoy(...)
    Chunk1 --> p(Intermediate Answer1)
    Chunk2 --> q(Intermediate Answer2)
    annoy --> r(...)
    window(Context Window)
    p & q & r --> window
    window --> ans(Global Answer)

现在,每一个社区都有自己的社区总结,可以用这些社区总结来回答用户的问题,具体步骤如下

  1. 所有的社区总结打乱,然后划分为一个个 Chunk。这种方式确保了相关的信息分布在不同的 Chunk
  2. Map 阶段:用每一个 Chunk 让 LLM 生成临时回复,并让 LLM 给出临时回复的得分(0~100),得分衡量了这个生成的临时回复对用户问题的帮助程度
  3. Reduce 阶段:将临时回复按照得分降序排列然后一一拼接起来,直到上下文窗口长度放不下,然后再让 LLM 生成最终回复

上面就是 GraphRAG 的全部内容,整体流程还算是蛮清晰的,官方也提供了实现但折腾了一会发现只支持 OpenAI 的 LLM。搜索的时候看到有人用 LangChain 实现的 langchain-graphrag,准备看看是否能用


  1. Edge, Darren, et al. “From local to global: A graph rag approach to query-focused summarization.” arXiv preprint arXiv:2404.16130 (2024). ↩︎ ↩︎ ↩︎ ↩︎