RAG 文本分块实战:chunk size、overlap 和切分方式怎么选
分块是 RAG 效果的第一道关口。用具体例子讲清楚块大小、重叠、按结构切分和元数据,以及怎么用数据验证你的选择。
做 RAG(检索增强生成)时,大家往往把精力花在选模型、选向量数据库上,却忽略了最前面的一步:怎么把文档切成块。块切得不好,后面检索再准也找不到正确答案。
为什么要分块
- 嵌入模型有输入上限,太长的文本放不进去;
- 一个向量只能代表一个“主题”。把整篇长文压成一个向量,细节会被平均掉,检索时很难命中具体问题;
- 上下文成本:检索回来的内容要放进 prompt,块越大,花的 token 越多。
分块的本质是在两件事之间取平衡:块要足够小,才能精确命中;块又要足够大,才能保留理解它所需的上下文。
块大小怎么选
没有通用的最佳值,但有一些经验起点:
| 文档类型 | 建议起点 | 原因 |
|---|---|---|
| FAQ、短问答 | 100–300 token | 每条本身就是独立单元 |
| 技术文档、帮助中心 | 300–800 token | 一个小节通常就是一个完整知识点 |
| 长文报告、合同 | 500–1000 token | 论述跨段落,太小会丢失逻辑 |
注意单位:很多框架默认按字符计数,而模型按 token 计费和限制。中英文的字符与 token 比例差别很大,同样“500 字符”的块,中文和英文的 token 数可能相差几倍。
Overlap(重叠)的作用
相邻两块共享一小段内容,就是 overlap。它解决的是关键信息恰好被切在边界上的问题:
……该接口的默认超时时间为 30 秒。‖ 如需修改,请在请求头中设置……
如果正好在“‖”处切开,后一块就不知道“修改”的是什么。加上一点重叠,后一块会带上前一句。
常见设置是块大小的 10%–20%。重叠太大会造成大量重复内容,浪费存储,也会让检索结果里出现几乎相同的块。
切分方式:从简单到讲究
1. 固定长度切分:每 N 个字符切一刀。实现最简单,但经常把句子拦腰截断,不推荐直接使用。
2. 递归切分:先尝试按段落(空行)切,块还太大就按行切,再按句子、按词切。这是 LangChain RecursiveCharacterTextSplitter 的思路,也是目前最常用的默认方案。处理中文时要把 。!?;, 这些中文标点也加进分隔符列表。
3. 按文档结构切分:Markdown 按标题、HTML 按 section、代码按函数切分。结构清晰的文档效果通常最好。
4. 语义切分:计算相邻句子的嵌入相似度,在话题明显变化的地方切开。效果可能更好,但成本更高,也更难调试。
建议从递归切分开始,有了评测数据之后再考虑更复杂的方案。
别忘了元数据
每个块除了文本,还应该带上元数据,比如:
{
"text": "该接口的默认超时时间为 30 秒……",
"source": "docs/api/timeout.md",
"title": "请求超时设置",
"heading_path": ["API 参考", "通用参数", "超时"],
"updated_at": "2026-09-01"
}
元数据可以用来过滤(只搜某个产品线的文档)、在回答里给出引用链接,也可以把标题路径拼到文本前面一起做嵌入,让短块也带上上下文。
用数据验证,而不是凭感觉
调分块参数最有效的方法是建一个小评测集:
- 准备 30–50 个真实用户问题,并标注每个问题对应的正确文档片段;
- 用不同的块大小和重叠参数分别建索引;
- 统计每种配置下,正确片段出现在前 3 / 前 5 条结果里的比例。
评测数据可以存成 JSONL,每行一个问题,用 JSONL 校验器 检查格式。
动手调参之前,可以先在 RAG 文本分块 里粘贴一篇你的真实文档,拖动块大小和重叠的滑块,直观看看每种设置下块是怎么被切开的。
小结
- 分块要在“精确命中”和“保留上下文”之间取平衡;
- 从递归切分、300–800 token、10%–20% 重叠开始;
- 注意字符和 token 的区别,尤其是中文;
- 给每个块加上元数据;
- 最终用评测数据而不是直觉来决定参数。