图 1:本文封面,三种分块切法的边界对照。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。「RAG 的文本分块有哪些方法」是检索增强方向的高频题,表面是让你背方法清单,实际考两件事:能不能说清每种切法切的是什么边界,以及块大小这个旋钮往哪边拧、代价是什么。
一句话结论:主流切法分三类——定长切、递归切、语义切,分别切在长度、结构边界和主题断点上;块大小没有标准答案,常见起点 256~512 token、重叠 10%~20%。
先讲为什么要分块。嵌入模型有输入长度上限,几百页的文档整篇塞不进去;就算塞得进,整篇压成一个向量,具体主题的细节会被平均掉,检索时什么都像、什么都不像。分块的本质是把文档切成「可以独立被检索」的最小单元,切法直接决定召回质量的上限。
第一类,定长分块。 每 N 个 token(或字符)切一刀,相邻块保留 10%~20% 的重叠,防止恰好切在答案中间。优点是实现最简单、速度最快、块长均匀;缺点也明显——句子会被拦腰切断,语义残缺。适合日志这类结构规整、单条信息量小的文本。
第二类,递归分块。 给分隔符排优先级:先按段落(两个换行)切,切出来的块还超上限就降一级按换行切,再超按句号切,最后才落到单字符硬切。LangChain 官方文档把 RecursiveCharacterTextSplitter 列为通用文本的默认推荐,理由就是尽量保住完整语义单元。代价是块长不均、实现略复杂,但绝大多数文档场景应该从这里起步。
第三类,语义分块。 把相邻句子分别算嵌入向量,相似度跌破阈值就判定主题切换、在此断开。块内主题最内聚,但每篇文档要多跑一轮嵌入计算,慢,且断点质量依赖嵌入模型本身。适合主题混杂的长文。
用一段代码把前两种切法的差别摆出来(Node.js 22.15.0,node chunk.js,用字符数模拟 token 上限):
function fixedChunks(s, size, overlap) {
const out = [];
for (let i = 0; i < s.length; i += size - overlap) {
out.push(s.slice(i, i + size));
}
return out;
}
function recursiveSplit(s, seps, size) {
const sep = seps[0];
const pieces = sep === '' ? [...s] : s.split(sep);
const out = [];
for (const p of pieces) {
if (p.length <= size) { if (p.length) out.push(p); }
else out.push(...recursiveSplit(p, seps.slice(1), size));
}
return out;
}
// 分隔符优先级:段落 → 换行 → 句号 → 单字符
实测输出(输入为三段关于分块的短文,上限 40):
定长切分 块1: 第一段讲分块为什么重要:……整篇文档根本塞不进去。⏎第二段
定长切分 块2: 进去。⏎第二段讲检索精度:块太大,答案被噪声淹没……
递归切分 块1: 第一段讲分块为什么重要:嵌入模型有输入长度上限,整篇文档根本塞不进去。
递归切分 块2: 第二段讲检索精度:块太大,答案被噪声淹没;块太小,上下文又支离破碎。
定长切分把「塞不进去」从中间剁开,还因为重叠把「第二段」重复进两个块;递归切分让每个段落完整成块,语义单元保住了。
图 2:三种切法的规则、优缺点与适用场景对照。
块大小怎么选。 这是一对矛盾:块小,向量语义集中、检索准,但上下文碎,答案可能缺前因后果;块大,上下文全,但噪声多、向量被稀释。工程上的常见起点是 256~512 token、重叠 10%~20%,然后按召回效果回调:发现答案常被切断就加大重叠,发现召回块答非所问就缩小块或换语义切。另有一类结构感知切法可作补充——按 HTML 标签、Markdown 标题、代码函数边界切,文档结构本身就是现成的语义边界。
面试现场的答题结构。 先给三类切法加一句分界线(长度、结构、语义),再各带一组优缺点与适用场景,然后主动谈块大小的权衡与调参路径,最后补一句「切法选择要跟检索评测联动,不能拍脑袋」。答到「分块决定召回质量上限」这一层,这道题就从背书题答成了工程题。你的知识库用的哪种切法?块大小是拍出来的还是调出来的?欢迎聊聊。