RAG切片策略:固定长度、递归字符、语义切分、结构感知四种方式对比
为什么Chunking这么重要
假如你正在准备一场考试,有一本500页的教材。你的复习策略是:把书撕成很多小卡片,每张小卡片写一个知识点,考试前根据题目去抽卡片
如果你的卡片切的太细---每张只有一句话,那每张卡片的上下文不完整
如果你的卡片切的太粗---每张抄一整章---那么信息虽然完整,但是你要看的内容太多,真正需要的那句话藏在一大堆无关文字里面
RAG里面的Chunking,面对的就是这个核心矛盾:chunk太小,单个片段语义不完整;chunk太大,检索出来的内容信噪比太低
四种主流切分策略
策略一:固定长度切分
最简单粗暴的方式,按照字符或者token数固定切分,每个chunk固定512个token,切完拉倒
优点是实现极简,不需要理解文档结构。缺点同样明显:它完全不管语义边界,可能把一句话从中间切断,把一个完整的论述拆成两半
实际工程中,这种方式通常只用于原型阶段的快速验证,不适合生产环境
策略二:递归字符切分
这是目前工程实践中最常用的基础策略,Langchain的RecursiveCharacterTextSplitter就是这个思路
核心思路是:按照优先级尝试不同的分隔符切分。先尝试用段落分隔符切分,如果切出来的chunk还是太大,再用换行符切分,还是太大就用句话切分,以此递归,直到每个chunk都在目标大小以内
相比固定长度切分,这种方式更倾向于在自然语言的语义边界处断开,同时还能控制chunk的上限大小,在大多数普通文本场景下,这是一个不错的默认选择
策略三:语义切分
更进一步的方式:不依赖固定分隔符,而是真正理解文本语义,在"话题转变"的地方切分
具体做法是:把文档按照句子分割,然后计算相邻句子之间的语义相似度,当相似度出现明显跌落时,就认为这里发生了话题转移,在此处切开
优点是切分结果的语义完整性最好,每个chunk内部讨论的主题相对聚焦。缺点是需要对全完做Embedding计算,处理速度慢,成本更高,而且切出来的chunk大小很不规则
结构感知切分
当文档有明确的层级结构时(比如md文档有标题层级、法律文件有条款号、代码有函数边界),按照结构切分往往是最优选择
比如一篇技术文档,每个二级标题下的内容作为一个chunk。这样每个chunk天然对应一个有意义的主题,检索精准度往往最高
对于代码文件,按照函数或者类的边界切分,对于PDF报告,识别出页码和章节标题再切分---这些都属于结构感知切分
Overlap:被低估的关键参数
讲Chunking绕不开一个关键参数:overlap(重叠)
设想一段文字,中间某句话恰好落在两个chunk的边界:前半句在chunk1,后半句在chunk2,当用户提问这句话相关的内容时,单独检索到chunk1或者chunk2,拿到的都是一半,信息不完整,生成的答案就会出错
Overlap的解决思路是:相邻chunk之间保留一段重叠的内容。比如chunk大小设为512token,overlap设为64token,那么chunk2的前64个token和chunk1的后64个token是完全相同的
这样,边界附近的信息在两个chunk里面都有备份,不管检索命中哪个,都能拿到完整的上下文
overlap的代价:索引体积增大,检索结果可能出现重复
一般推荐overlap大小为chunk的大小的10%-20%
Chunk过大、过小各有什么问题
Chunk过小:
单个chunk的语义不完整,孤立一句话往往没有足够的上下文让模型理解。比如检索结果里出现:是的,这种做法符合规范
符合什么规范?完全不知道。同时,chunk太小意味着要切出大量chunk,索引规模膨胀,检索速度也会变慢
Chunk过大:
每个chunk里面包含的话题太多,和用户问题的相关性被稀释。假设chunk是2000token的长段落,用户只关心其中50个token的内容,但是这2000个token都会被塞进Context,占用宝贵的上下文窗口,同时让模型的注意力难以聚焦。另外,chunk越大,它的向量表示就越平均化,语义越模糊,检索精确度越低
关于chunk大小的经验值:
没有通用的最优大小,但实践中一些常用的参考范围是:对话型问答场景通常在256-2000token,这些都是估计,需要根据实际业务效果调整
实际项目里面怎么选策略?
决策框架
文档有清晰的结构(章节、标题、条款)?优先考虑结构感知切分,按照自然的结构边界切分,语义完整性最好
文档是普通散文、没有明确结构?用递归字符切分,配合合适的chunk size和overlap
文档质量要求极高,话题切换频繁?考虑语义切分,代价是需要更多计算资源和时间
快速验证原型?固定长度切分够用,记得上线前换掉