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
核心逻辑:- 先用
fixed_separator(默认\n\n)拆分文本为粗粒度段落; - 遍历每个段落,判断长度:
- 若段落长度 ≤
chunk_size:直接加入最终结果; - 若段落长度 >
chunk_size:调用self.recursive_split_text(chunk)细粒度拆分;
- 若段落长度 ≤
- 返回所有拆分后的片段。
- 先用
-
第二步:FixedRecursiveCharacterTextSplitter.recursive_split_text
这是拆分的核心逻辑,处理「超长段落」的细粒度拆分,内部调用关系:- 选分隔符:从
_separators中找第一个存在于文本中的分隔符(优先级:段落→换行→句号→空格→字符); - 按分隔符拆分:
- 若分隔符非空:按分隔符拆分文本(保留分隔符时,给拆分后的片段补充分隔符);
- 若分隔符为空(最后兜底):按单个字符拆分;
- 过滤无效片段:剔除空字符串、纯换行的片段;
- 合并/递归拆分:
- 遍历拆分后的小片段,若长度 <
chunk_size:暂存到_good_splits,累计长度; - 若累计长度接近
chunk_size:调用_merge_splits(父类方法)合并成符合chunk_size的片段(滑动窗口核心),加入最终结果; - 若单个小片段长度 >
chunk_size:递归调用self._split_text(s, new_separators)(父类RecursiveCharacterTextSplitter的_split_text方法),用更细粒度的分隔符拆分;
- 遍历拆分后的小片段,若长度 <
- 兜底字符拆分:若分隔符为空(字符级拆分),按
chunk_size和chunk_overlap实现「滑动窗口切割」(保留重叠部分)。
- 选分隔符:从
-
第三步:RecursiveCharacterTextSplitter._merge_splits
被recursive_split_text调用,是「滑动窗口」的核心实现:- 合并
_good_splits中的片段,累计长度; - 若合并后长度超过
chunk_size:弹出头部片段,保留chunk_overlap长度的重叠部分(保证上下文连续); - 返回合并后的符合
chunk_size的片段。
- 合并
-
第四步:RecursiveCharacterTextSplitter._split_text
被recursive_split_text递归调用,是父类的底层拆分方法:- 接收「子分隔符列表」(如原分隔符是
\n\n,子分隔符是\n、。、.等); - 重复
recursive_split_text的核心逻辑(选分隔符→拆分→合并); - 实现「递归降级拆分」(段落→行→句子→单词→字符)。
- 接收「子分隔符列表」(如原分隔符是
三、关键调用节点总结
| 调用方 | 被调用方 | 调用场景 |
|---|---|---|
| 业务代码 | 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数) |
四、核心设计逻辑
- 分层拆分:先按固定分隔符(段落)粗拆,再按层级分隔符细拆,保证语义完整;
- 滑动窗口:
_merge_splits中通过chunk_overlap保留重叠部分,避免拆分后上下文断裂; - 递归兜底:超长片段自动降级到更细粒度分隔符,直到字符级,避免粗暴截断;
- 灵活长度计算:支持字符数/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的片段。
浙公网安备 33010602011771号