切分重叠是什么
重叠是在相邻文本块之间保留一段共享内容,相当于给切分位置加保险:当答案正好横跨边界时,两块各自都能拿到完整的那句话,不会出现检索命中却答不全的情况。代价是索引体积变大、同一段内容更容易在召回结果里重复出现,所以重叠比例要和入库去重策略一起定,而不是单独调一个参数。
基本信息
| 作用 | 边界保险丝,保持跨块的语义连续 |
|---|---|
| 起始比例 | 块长的 10-20% 起步,再用评测集调整 |
| 计算单位 | 按字符或按 token,全链路口径必须统一 |
| 主要代价 | 索引体积上升,重复块占用召回名额 |
| 配套手段 | 入库按内容哈希去重,近重复可用 SimHash 判定 |
| 联动参数 | 切分单位决定重叠是否有意义,先定按段还是按句 |
典型使用场景
- 合同、规范、制度这类长条款文档:条款常被切在句中断开,重叠能补回上下文
- 带说明文字的表格与图注:说明与实际数据分块后容易各答一半
- 会议记录与访谈稿:话题在段落中间切换,硬切会丢掉切换前那句话
- 常见问答对入库:问答必须整体命中,重叠能降低问句与答案被切散的概率
- 切分调参实验:把重叠比例作为变量,用同一评测集比较命中率与答案完整度
切分与去重示例
切分与去重示例
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=800, # 目标块长,单位字符,按语料实测调整
chunk_overlap=120, # 约 15% 重叠,落在推荐的 10-20% 区间
separators=["\n\n", "\n", "。", "!", "?", " "], # 优先在句末断开
)
chunks = splitter.split_text(doc) # 长文档一次调用完成切分
seen, kept = set(), [] # 入库前去重,防止重复内容霸占召回
for c in chunks:
h = hashlib.sha256(c.strip().encode()).hexdigest()
if h not in seen: # 完全相同的内容只保留一份
seen.add(h); kept.append(c)常见现象与处理
| 现象 | 原因 | 处理 |
|---|---|---|
| 同一段反复被召回 | 重叠过大或文档重复入库 | 下调重叠比例并先做内容去重 |
| 答案缺半句 | 无重叠且断在句中间 | 加 10-20% 重叠并优先句末断开 |
| 索引体积暴涨 | 重叠比例过高叠加重复文档 | 降低比例,入库阶段先去重 |
| 引用定位不准 | 未记录块内字符偏移 | 保存字符级起止偏移用于高亮 |
| 块长忽长忽短 | 分隔符优先级设置过粗 | 按语义层级排分隔符并设最小块长 |
重叠按字符还是按 token 计算要与检索口径一致:切分按字符、embedding 按 token 的混用会让「15% 重叠」这类经验值失去意义。
Markdown、HTML 这类有层级结构的文档,先按标题层级切,再对超长小节做二次按长度切分,通常比一次性按长度切的效果好,重叠也可以更小。