Dify文本分块方法

一、核心类与函数调用关系总览

FixedRecursiveCharacterTextSplitter 是最终被业务代码调用的拆分器类,其继承链和函数调用关系如下:

业务代码 → FixedRecursiveCharacterTextSplitter.split_text() 
          ↓ (内部调用)
FixedRecursiveCharacterTextSplitter.recursive_split_text()
          ↓ (递归/兜底调用)
EnhanceRecursiveCharacterTextSplitter._split_text() (父类RecursiveCharacterTextSplitter的方法)
          ↓ (工具方法调用)
RecursiveCharacterTextSplitter._merge_splits()

同时,初始化时的依赖关系:

FixedRecursiveCharacterTextSplitter.__init__() → EnhanceRecursiveCharacterTextSplitter.__init__() → RecursiveCharacterTextSplitter.__init__()
EnhanceRecursiveCharacterTextSplitter.from_encoder() → 初始化自身(绑定length_function)

二、逐层级拆解调用逻辑

1. 初始化流程(init 调用链)

FixedRecursiveCharacterTextSplitter 是最下层子类,初始化时会逐层调用父类构造方法:

  • 第一步:业务代码创建拆分器实例时,调用 FixedRecursiveCharacterTextSplitter.__init__
    核心逻辑:

    • 解码 fixed_separator(处理转义符,如 \n\n 转成实际换行);
    • 初始化分隔符列表 _separators(默认 ["\n\n", "\n", "。", ". ", " ", ""],优先按段落、换行、中文句号、英文句号、空格拆分);
    • 调用父类 EnhanceRecursiveCharacterTextSplitter.__init__(实际是 RecursiveCharacterTextSplitter.__init__)。
  • 第二步EnhanceRecursiveCharacterTextSplitter.__init__
    无自定义逻辑,直接继承父类 RecursiveCharacterTextSplitter 的初始化,完成 chunk_size(拆分长度)、chunk_overlap(滑动窗口重叠长度)、keep_separator(是否保留分隔符)等核心参数的初始化。

  • 辅助初始化:from_encoder 类方法
    业务代码可通过 EnhanceRecursiveCharacterTextSplitter.from_encoder() 创建实例(替代直接 __init__):

    • 绑定 length_function(字符数/Token数计算逻辑):
      • 若传入 embedding_model_instance,用模型的 get_text_embedding_num_tokens 计算Token数;
      • 否则用 GPT2Tokenizer 计算Token数,或用字符长度(_character_encoder);
    • 返回 EnhanceRecursiveCharacterTextSplitter(或子类 FixedRecursiveCharacterTextSplitter)实例。

2. 核心拆分流程(split_text 调用链)

业务代码调用 split_text(text) 触发拆分,是所有逻辑的入口:

# 业务代码示例
splitter = FixedRecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(long_text)
  • 第一步:FixedRecursiveCharacterTextSplitter.split_text
    核心逻辑:

    1. 先用 fixed_separator(默认 \n\n)拆分文本为粗粒度段落;
    2. 遍历每个段落,判断长度:
      • 若段落长度 ≤ chunk_size:直接加入最终结果;
      • 若段落长度 > chunk_size:调用 self.recursive_split_text(chunk) 细粒度拆分;
    3. 返回所有拆分后的片段。
  • 第二步:FixedRecursiveCharacterTextSplitter.recursive_split_text
    这是拆分的核心逻辑,处理「超长段落」的细粒度拆分,内部调用关系:

    1. 选分隔符:从 _separators 中找第一个存在于文本中的分隔符(优先级:段落→换行→句号→空格→字符);
    2. 按分隔符拆分
      • 若分隔符非空:按分隔符拆分文本(保留分隔符时,给拆分后的片段补充分隔符);
      • 若分隔符为空(最后兜底):按单个字符拆分;
    3. 过滤无效片段:剔除空字符串、纯换行的片段;
    4. 合并/递归拆分
      • 遍历拆分后的小片段,若长度 < chunk_size:暂存到 _good_splits,累计长度;
      • 若累计长度接近 chunk_size:调用 _merge_splits(父类方法)合并成符合 chunk_size 的片段(滑动窗口核心),加入最终结果;
      • 若单个小片段长度 > chunk_size:递归调用 self._split_text(s, new_separators)(父类 RecursiveCharacterTextSplitter_split_text 方法),用更细粒度的分隔符拆分;
    5. 兜底字符拆分:若分隔符为空(字符级拆分),按 chunk_sizechunk_overlap 实现「滑动窗口切割」(保留重叠部分)。
  • 第三步:RecursiveCharacterTextSplitter._merge_splits
    recursive_split_text 调用,是「滑动窗口」的核心实现:

    1. 合并 _good_splits 中的片段,累计长度;
    2. 若合并后长度超过 chunk_size:弹出头部片段,保留 chunk_overlap 长度的重叠部分(保证上下文连续);
    3. 返回合并后的符合 chunk_size 的片段。
  • 第四步:RecursiveCharacterTextSplitter._split_text
    recursive_split_text 递归调用,是父类的底层拆分方法:

    1. 接收「子分隔符列表」(如原分隔符是 \n\n,子分隔符是 \n、。、. 等);
    2. 重复 recursive_split_text 的核心逻辑(选分隔符→拆分→合并);
    3. 实现「递归降级拆分」(段落→行→句子→单词→字符)。

三、关键调用节点总结

调用方 被调用方 调用场景
业务代码 FixedRecursiveCharacterTextSplitter.split_text 触发文本拆分(入口)
split_text recursive_split_text 处理超长段落的细粒度拆分
recursive_split_text _merge_splits(父类) 合并小片段,实现滑动窗口重叠
recursive_split_text _split_text(父类) 递归降级拆分超长小片段(如句子拆单词)
EnhanceRecursiveCharacterTextSplitter.from_encoder init 初始化实例并绑定长度计算函数(Token/字符)
recursive_split_text length_function 计算片段长度(字符数/Token数)

四、核心设计逻辑

  1. 分层拆分:先按固定分隔符(段落)粗拆,再按层级分隔符细拆,保证语义完整;
  2. 滑动窗口_merge_splits 中通过 chunk_overlap 保留重叠部分,避免拆分后上下文断裂;
  3. 递归兜底:超长片段自动降级到更细粒度分隔符,直到字符级,避免粗暴截断;
  4. 灵活长度计算:支持字符数/Token数两种长度计算(通过 from_encoder 绑定)。

五、典型调用链路示例

假设拆分「超长中文段落」(长度 1000,chunk_size=200,chunk_overlap=20):

split_text → 用\n\n拆分出超长段落 → 调用recursive_split_text
recursive_split_text → 选分隔符「。」→ 拆分出多个句子 → 遍历句子:
  - 句子1(150字)+ 句子2(80字)→ 累计230字 > 200 → 调用_merge_splits:
    - 合并句子1 + 句子2前20字(重叠)→ 生成200字片段;
    - 保留句子2后60字作为重叠部分,用于下一个窗口;
  - 句子2剩余60字 + 句子3(100字)→ 累计160字 < 200 → 继续合并;
  - 若某句子长度>200 → 调用_split_text,用「,」拆分(降级);
最终返回所有符合chunk_size的片段。
posted @ 2026-01-30 09:35  hateXWSK  阅读(20)  评论(0)    收藏  举报