LlamaIndex之HierarchicalNodeParser层次节点解析器

6 阅读4分钟

hierarchical : [haɪəˈrɑːrkɪkl] 分层的

# 进行层次节点解析器 chunk_sizes=每层目标Token数(从粗到细)
node_parser = HierarchicalNodeParser.from_defaults(
    chunk_sizes=[512, 300],
    chunk_overlap=70
)

这段代码使用了 LlamaIndex(一个流行的用于构建大语言模型应用的框架)中的 HierarchicalNodeParser 组件。它的主要作用是将长文档按照层级结构进行分割,以便在后续的检索(如 RAG 系统)中既能保留上下文,又能精确定位。

以下是对这段代码的详细解释:

1. 代码含义与参数解析

  • HierarchicalNodeParser:层级节点解析器。与普通的文本切分器(只生成单一层级的文本块)不同,它会将文档切分成具有父子关系的树状结构(例如:大块包含中块,中块包含小块)。

  • from_defaults() :使用默认配置创建解析器实例的工厂方法。

  • chunk_sizes=[512, 300] :定义了层级的切分大小。这里有两个值,表示生成两层节点结构:

    • 第一层(父节点) :最大尺寸为 512 个 Token(或字符,取决于底层分词器,LlamaIndex 默认通常为 Token)。
    • 第二层(子节点) :最大尺寸为 300 个 Token。
    • 原理:解析器会先把文档尽量按 512 切分成大块,然后再把每个大块尽量按 300 切分成小块。这就形成了一个树状结构:文档 -> 512大块 -> 300小块。
  • chunk_overlap=70:切分时的重叠区域大小为 70。在切分文本时,相邻的两个文本块会有 70 个 Token 的重叠部分。

2. 实现原理

  1. 层级切分:当输入一篇长文档时,解析器首先尝试将其分割成不超过 512 的文本块。随后,针对每一个 512 的文本块,再次进行分割,提取出不超过 300 的文本块。300 的子节点在逻辑上属于 512 的父节点。
  2. 滑动窗口与重叠:为了保证切分后的文本语义连贯,避免关键信息被生硬截断,解析器在切分时使用了滑动窗口。前后两个相邻的块会共享 70 个 Token 的内容,确保跨块的上下文得以保留。

3. 主要用途

这种层级解析在 RAG(检索增强生成)  系统中非常有用,主要是为了解决“上下文碎片化”问题:

  • 小块用于精准匹配:在用户提问时,使用较小的文本块(如 300)进行向量检索,可以提高检索的准确性(语义更集中,噪音更少)。
  • 大块用于上下文补充:当检索到某个 300 的小块后,系统可以自动向上追溯,将其所属的 512 的父节点(甚至更上层)送入大模型。这样既保证了检索精准,又为大模型提供了充足的上下文,避免回答断章取义。
  • LlamaIndex 的 AutoMergingRetriever 就是专门配合此解析器设计的,当检索到的多个小块属于同一个父节点时,它会自动将它们合并为父节点再喂给 LLM。

4. 注意事项

  1. 参数递减逻辑chunk_sizes 列表中的值应该是严格递减的。如果写成 [300, 512] 会导致逻辑错误,因为父节点不可能比子节点还小。
  2. 重叠参数的共享:代码中的 chunk_overlap=70 是一个全局设置,这意味着所有层级(512切分和300切分时)的重叠大小都是 70。如果你希望不同层级有不同的重叠度,需要在 from_defaults 中传入自定义的 chunk_parser(即底层的 SentenceSplitter)。
  3. 单位问题:LlamaIndex 默认的切分单位是 Token(通常通过 tiktoken 计算),而不是单纯的字符。中文字符和英文单词在 Token 化时的长度计算是不同的,调试时需注意。
  4. 层级深度chunk_sizes 可以包含更多值,例如 [1024, 512, 256],这会生成三层树结构。但层级越深,解析和存储的复杂度越高,通常两层或三层足以满足大部分 RAG 需求。
  5. 配合存储:使用层级解析器后,生成的节点包含父子关系,在存入 Vector Store(向量数据库)时,需要确保这些关系元数据被正确保存,否则后续的父节点检索将无法工作。