文档切片策略选择
假设你点了一个超大的披萨,18寸的那种。
直接抱着吃?不现实。得切成小块。
但怎么切有讲究:
- 切太大,嘴巴塞不下
- 切太小,馅料都掉了,吃起来没味道
- 切得位置不对,可能把一块完整的培根切成两半
文档分片也是同样的道理。
大模型的上下文窗口是有限的,一篇几万字的文档塞不进去。而且我们检索的时候,想找到的是最相关的那一小段,不是整篇文档。
所以要切块。但切多大、怎么切,直接影响后面的检索效果。
切大了切小了,都是问题
切太大会怎样
假设我把一篇5000字的文档切成5块,每块1000字。
问题一:检索不精准
用户问"打印机怎么换墨盒",检索返回的那一块1000字里,可能只有100字是真正讲换墨盒的,其他900字讲的是打印机怎么联网、怎么设置纸张大小。
这900字就是噪音,会干扰大模型的判断。
问题二:超出上下文限制
检索返回5块,每块1000字,加上用户问题和系统提示词,总共可能有6000+字。
如果大模型的上下文窗口只有4096 tokens,直接就超了。
切太小会怎样
假设我切成50块,每块100字。
问题一:语义不完整
一句话被切成两半,前半句在A块,后半句在B块。
A块:如果打印机出现卡纸现象,请先
B块:关闭电源,然后打开后盖取出纸张。
单独检索到A块或B块,都是不完整的信息。
问题二:上下文丢失
有些内容需要结合上下文才能理解。比如:
上一段:XX-3000型号打印机支持双面打印。
这一段:该功能需要在驱动程序中开启。
如果"这一段"被单独切出来,"该功能"指的是什么就不清楚了。
所以到底切多大
没有标准答案,得看你的具体场景。
但有一些经验值可以参考:
| 场景 | 建议大小 | 原因 |
|---|---|---|
| 技术文档 | 500-1000字 | 技术内容逻辑性强,需要保持段落完整 |
| 新闻资讯 | 300-500字 | 内容相对独立,可以切小一些 |
| 法律合同 | 按条款切 | 每个条款是独立的语义单元 |
| 代码文档 | 按函数/类切 | 代码有明确的结构边界 |
通用起点:chunk_size = 500~1000字,overlap = chunk_size的10%~20%。宁可先设大一点,发现检索不精准再缩小。不同场景差异较大,最终要通过实际测试集来验证效果。
五种常见的切块方式
不同的切块方式,适合不同的场景。下面一个个讲。
方式一:固定大小切块
最简单粗暴的方式——数字符,够数了就切一刀。
原文:ABCDEFGHIJKLMNOPQRSTUVWXYZ(26个字母)
设置:每10个字符切一块
结果:
块1: ABCDEFGHIJ
块2: KLMNOPQRST
块3: UVWXYZ
参数说明:
chunk_size:每块的字符数chunk_overlap:相邻块之间重叠的字符数
Overlap是什么意思?就是让相邻的两块有一部分内容是重复的:
设置:chunk_size=10, overlap=3
结果:
块1: ABCDEFGHIJ
块2: HIJKLMNOPQ (HIJ和块1重叠)
块3: OPQRSTUVWX (OPQ和块2重叠)
为什么要重叠?因为重要信息可能正好被切断,重叠一部分可以降低信息丢失的风险。
优点:实现简单,可预测
缺点:完全不管语义,可能把一句话切成两半
适用场景:快速验证、对效果要求不高的场景
方式二:递归分块(推荐)
递归分块是目前用得最多的方式。
核心思路:按照一组分隔符的优先级来切割文本。先尝试用最高优先级的分隔符(如双换行符),如果切出来的块还是太大,再用下一级分隔符继续切。
递归分块(Recursive Character Text Splitter)是工业界最广泛使用的分块方式,LangChain、Spring AI Alibaba、Dify等主流框架都将其作为默认或推荐方案。它能在尊重文档自然结构的前提下,保证每块不超过指定大小,是大多数场景的首选。
常见的分隔符优先级:
\n\n—— 双换行,通常表示段落分隔\n—— 单换行。!?—— 句子结束标点;,—— 句内停顿—— 空格
示例:
原文:
第一段内容。第一段继续。
第二段内容。第二段很长很长很长很长很长很长很长...
第三段内容。
设置:chunk_size=50
处理过程:
1. 先按"\n\n"切,得到三段
2. 第二段超过50字,按"。"继续切
3. 切出来的小块不超过50字,完成
为什么叫"递归":
private void splitText(String text, int separatorIndex, List<String> chunks) {
// 如果文本已经足够小,直接加入结果
if (text.length() <= chunkSize) {
chunks.add(text);
return;
}
// 用当前分隔符切分
String separator = separators[separatorIndex];
String[] splits = text.split(separator);
for (String split : splits) {
if (split.length() > chunkSize) {
// 还是太大,递归用下一个分隔符继续切
splitText(split, separatorIndex + 1, chunks);
} else {
chunks.add(split);
}
}
}
就是这个递归调用,让它能够灵活处理各种长度的文本。
优点:尊重文档结构,切出来的块语义相对完整
缺点:对于没有明显分隔符的文本效果一般
适用场景:大部分通用场景
方式三:基于文档结构切块
针对有明确结构的文档(如Markdown、Word),可以根据其内部结构来切。
Markdown文档:
按标题层级切割。一级标题下的内容是一块,二级标题下的内容是一块。
# 第一章
这是第一章的内容...
## 1.1 小节
这是小节的内容...
## 1.2 另一小节
这是另一小节的内容...
切割结果:
- 块1:第一章 + 第一章的内容
- 块2:1.1 小节 + 小节的内容
- 块3:1.2 另一小节 + 另一小节的内容
Word文档:
按照Word的大纲级别(标题1、标题2...)来切割。
优点:完美保留文档结构,语义最完整
缺点:只适用于有明确结构的文档
适用场景:技术文档、产品手册、规范文档
方式四:语义分块
前面的方式都是基于"规则"的——数字符、看分隔符、看标题。
语义分块不一样,它是基于"意思"的——当话题转换的时候,就切一刀。
原理:
- 把文本按句子切开
- 计算相邻句子之间的语义相似度
- 相似度突然下降的地方,说明话题变了,这里切一刀
句子1:打印机支持双面打印。(相似度0.9)
句子2:双面打印可以节省纸张。(相似度0.3)← 话题转换
句子3:打印机的网络设置在控制面板中。
句子4:点击"网络设置"进入配置界面。
在句子2和句子3之间,话题从"双面打印"变成了"网络设置",相似度骤降,这里就是切分点。
优点:切出来的块语义最连贯
缺点:计算成本高(需要算相似度),效果依赖模型质量
适用场景:对效果要求高、能接受一定延迟的场景
方式五:大模型智能切块
最"智能"的方式——直接让大模型来切。
你把文档内容发给大模型,告诉它:"请帮我把这段文本按照主题进行切分,每个切分块应该是一个完整的语义单元。"
大模型会理解内容,然后给出切分结果。
示例Prompt:
请将以下文档内容按照主题进行智能切分。
要求:
1. 每个切分块是一个完整的语义单元
2. 相关的内容放在同一块
3. 输出JSON数组格式
文档内容:
{document_content}
优点:效果最好,能理解复杂的语义边界
缺点:成本高(要调用大模型),速度慢,不适合大规模处理
适用场景:少量重要文档、对效果要求极高的场景
各平台怎么做的
看看主流的AI平台是怎么实现分块的,可以给我们一些参考。
阿里云百炼
百炼支持6种切分方式:
- 智能切分(推荐)
- 按长度切分
- 按符号切分
- 按页切分
- 按标题切分
- 按正则切分
Dify
Dify主要支持两种:
- 通用分块(其实就是递归分块)
- 父子分块(后面会专门讲)
总结
这些平台的共同点是:递归分块是主流。
核心参数就三个:
- 分段标识符(用什么符号切)
- 分段最大长度(每块最大多少字)
- 分段重叠度(相邻块重叠多少字)
分块参数调优
最后聊聊怎么找到最佳的分块参数。
没有银弹
实话说,分块参数没有"最优值",只有"适合你场景的值"。
不同的文档类型、不同的问答场景,最佳参数都不一样。
调优方法
方法一:凭经验设初始值
- chunk_size:500-1000字
- overlap:chunk_size的10%-20%
方法二:准备测试集
收集一些典型的问答对,作为测试集。
方法三:调参验证
for chunk_size in [300, 500, 800, 1000]:
for overlap in [50, 100, 150]:
- 用这组参数切块
- 跑一遍测试集
- 记录准确率
选准确率最高的参数组合
方法四:观察bad case
上线后持续观察bad case(回答不好的问题),分析是不是切块的问题:
- 检索到的内容和问题相关度低 → 可能切块太大,噪音多
- 检索到的内容不完整 → 可能切块太小,语义断裂
- 检索到的内容重复 → 可能overlap设太大
通过bad case的表现反推参数问题:检索内容相关度低通常是切块太大(噪音多),检索内容语义断裂通常是切块太小,检索结果重复通常是overlap过大。这三个方向基本覆盖了80%的分块调优场景。
小结
这篇文章讲了文档分块的核心理论:
- 切块大小的权衡:太大不精准,太小不完整,要找平衡点
- 五种切块方式:固定大小、递归、基于结构、语义、大模型智能切块
- 递归分块是主流:大部分场景用递归分块就够了
- 参数需要调优:没有银弹,要根据具体场景调试
下一篇用ChunkViz可视化工具来直观感受不同分块策略的效果差异。