RAG 分块:大小与重叠怎么选
RAG 分块不是把文档切成固定长度就结束。一个块能否独立表达证据、重叠部分是否保住跨边界信息,以及最终上下文里有多少重复内容,都会影响后面的检索和回答。先确定长度单位,再讨论大小,能避免很多配置误解。
检索融合见RRF 混合检索,候选排序见Cross-Encoder 重排。分块发生在它们之前。English version
字符数和 token 数先分清
配置中的 chunk_size 并不天然代表 token。LangChain 的递归字符分割器示例用 len 衡量长度,因此按字符计数;换成其他长度函数,计量方式也随之改变。模型输入限制则要按对应 tokenizer 计算,不能把“500字符”直接当作“500 token”。
中文、英文、代码和标点的切分情况不同。即使字符数相同,token 数也可能不同。工程上可以记录两种长度:字符长度便于检查原文位置,token 长度用于输入预算。标题、提示词和其他附加文本也要算进实际输入。
大小决定证据如何被打包
短块更容易聚焦一件事,但也可能把条件与结论拆开。长块保留更多语境,却可能混入多个主题,或在重排、生成阶段触及输入限制。两者不是单向的质量关系。
| 观察到的问题 | 优先检查 |
|---|---|
| 找到了结论,缺少适用条件 | 边界、标题与相邻语境 |
| 检索材料很多,却夹杂多个主题 | 段落结构及块大小 |
| 关键句被截断 | 真实token预算及截断位置 |
| 前几条结果内容几乎相同 | 重叠与上下文去重 |
这张表是排查建议,不是实验结论。先保留章节标题、段落和来源位置,再在足够长的段落内部细分,通常更便于追踪证据。代码块、表格和带条件的操作说明应单独检查,不能只看平均块长度。
重叠能保住边界,也会重复存储
用固定窗口解释成本最直观。块大小记为 C,重叠记为 O,要求 0 ≤ O < C。步长 S = C − O。下面用20个单位、C=8、O=2演示;“单位”可以是字符或token,但同一次计算中必须一致。
窗口分别覆盖 [0,8)、[6,14)、[12,20)。累计24个单位,其中4个是重复覆盖;这不是增加了4个新事实。对很长的序列,忽略尾部与元数据时,累计长度相对原文约为 C/(C−O)。这个近似描述重复量,不是质量提升比例,也不等于实际磁盘空间倍数。
递归分割器会尝试依次使用分隔符,重叠是目标值,不能假定其每个输出窗口都遵循这张固定步长图。中文没有普遍的空格词边界,官方文档建议补充中文句号等分隔符;实际语义边界仍要抽样检查。
用代码验证覆盖与成本
下面是独立教学函数,不是 LangChain 的递归算法。它只按固定单位窗口返回半开区间,不做语义识别。已执行对长度0到39、大小1到11及所有合法重叠的覆盖检查,也检查每块不超过最大长度。
def spans(length, size, overlap):
if length < 0 or size <= 0 or not 0 <= overlap < size:
raise ValueError('invalid window parameters')
result = []
start = 0
while start < length:
end = min(start + size, length)
result.append((start, end))
if end == length:
break
start += size - overlap
return result
assert spans(0, 8, 2) == []
assert spans(8, 8, 2) == [(0, 8)]
assert spans(20, 8, 2) == [(0, 8), (6, 14), (12, 20)]
for n in range(40):
for size in range(1, 12):
for overlap in range(size):
ranges = spans(n, size, overlap)
covered = {i for a, b in ranges for i in range(a, b)}
assert covered == set(range(n))
assert all(0 < b-a <= size for a,b in ranges)
print('spans:', spans(20, 8, 2))
print('stored units:', sum(b-a for a,b in spans(20, 8, 2)))
print('coverage checks passed')
输出是 [(0,8),(6,14),(12,20)],累计长度24。空输入返回空列表;最后一个窗口到达末尾后停止,不额外创建只有重复内容的尾块。固定步长窗口数量还可以写为:N=0 时为0,否则为 1 + ceil(max(0,N−C)/(C−O))。
需要明确处理 O≥C,否则步长可能为零或负数。代码拒绝这种配置。真实系统还要保存文档ID、版本、区间与访问权限,避免重复块变成无法追踪来源的独立材料。
比较方案时固定哪些变量
不要同时更换分块大小、嵌入模型、检索策略和重排模型,然后把答案变化归因于分块。先固定其他组件,在同一查询集上比较几种边界或重叠设置。
评估不仅看是否检索到相关块,还要看是否包含完整回答证据。改变分块会改变块ID与数量,直接比较块级命中率容易产生偏差;可以把证据标注映射到原始文档区间,再比较是否覆盖这些区间。同时记录索引条目、累计token、最终上下文重复率和延迟。
没有适用于所有文档的最佳长度。对结构明确的文档,先尊重边界;对跨段依赖,尝试适量重叠或在检索后扩展相邻片段;对重复拥挤,检查去重和上下文选择。每次只改一个主要变量,才能知道哪一步真正有效。
参考资料
LangChain: Recursive text splitter
补发说明:本文于北京时间2026年10月9日补发,文章日期保留原计划19:00。区间、代码与成本计算为原创教学示例,未进行模型效果基准测试。
支持
如果这篇文章对你有帮助,欢迎支持本站。
二维码可点击放大。更多支持方式见支持页面。


