RecursiveCharacterTextSplitter 的分割原理,核心就是“按语义层级从大到小,依次尝试切分,直到块足够小”。
一、优先保留大语义单元
它预设了一套分隔符的优先级列表,默认是["\n\n", "\n", " ", ""]。
\n\n(段落):最优先尝试,因为段落是语义最完整的单元。\n(换行):段落切不开时,退一步按行切。" "(空格):行也切不开时,按单词切。""(空字符):最后兜底,直接按字符硬切,保证一定能切开。
二、具体分割流程(递归过程)
整个分割是一个递归的过程:
尝试第一级分隔符:用列表里第一个分隔符(如
\n\n)把文本切成若干段。检查每段大小:看切出来的每一段是否小于设定的
chunk_size。递归处理超长段:如果某一段还是太大,就针对这一段,使用剩下的分隔符列表(去掉
\n\n),重复第 1 步,用\n去切它,以此类推。合并成块:把最终切出来的、足够小的片段,按顺序重新合并,直到接近
chunk_size,形成一个最终的块。
三、举例
假设chunk_size=10,文本是"Learning new skills is essential",分隔符是["\n\n", "\n", " ", ""]。
找不到
\n\n和\n,于是用空格" "切分。切成:
["Learning", "new", "skills", "is", "essential"]。开始合并:
Learning(8字符) → 先放进去。看下一个
new(3字符),8+3=11 > 10,放不下,所以第一个块就是"Learning"。开始新块,放入
new(3字符)。看下一个
skills(6字符),3+6=9 ≤ 10,可以放下,于是合并成"new skills"。继续看
is(2字符),9+2=11 > 10,放不下,所以第二个块就是"new skills"。以此类推。
最终结果就是:['Learning', 'new skills', 'is', 'essential']。
为什么要这么设计?
保持语义完整:尽可能让一个完整的段落或句子留在一个块里,避免“半句话”被切到另一个块,影响 RAG 的检索质量。
保证一定能切:万一文本里没有任何标点,最后还有
""兜底,强制按字符切开,确保不会出错。
提醒:
默认分隔符是为英文设计的,直接用于中文可能会把词语切碎。建议在separators列表中加入中文标点,例如:["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]。