chunk重叠overlap设多少:切断上下文的坑
一句话先说清:RAG 切文档的时候,相邻两块之间要不要留一段重叠(overlap),留多少,这事比很多人想的重要。重叠太小,一句关键的话被生生切两半,两边都召回不全;重叠太大,向量库里全是冗余,检索还容易把好几块几乎一样的段落一起捞回来。我拿一份运维手册实测过一组,今天把怎么定 overlap 讲透。
不留重叠会怎样
先看反面教材。我最早图省事,按固定 500 字硬切,零重叠。结果有个问题死活答不对:「数据库主从切换后,缓存要不要手动刷新」。扒日志发现,手册里这句话的答案——「主从切换后需手动执行 flush,否则会读到旧数据」——刚好被切在两个 chunk 的接缝上。前半块「主从切换后需手动」,后半块「执行 flush 否则读到旧数据」,单看哪一块向量都跟问题不够贴,两块都没进 top5。
这就是零重叠的典型坑:语义被物理切割截断。一个完整的因果、一组步骤、一个定义,被字数硬生生劈开,两半各自残缺。
overlap 怎么定,给个实测参考
我固定 chunk 大小 500 字,只动 overlap,拿同一份标注集量 recall@5:
overlap | recall@5 | 向量库膨胀 | 备注 |
0 | 0.78 | 基准 | 接缝处的答案捞不全 |
50(10%) | 0.88 | +11% | 性价比最高 |
100(20%) | 0.91 | +25% | 还在涨但变缓 |
200(40%) | 0.92 | +60% | 几乎不涨了,纯浪费 |
规律很清楚:从 0 加到 10%~20%,召回明显往上走;再往上加,收益迅速摊平,存储和检索成本却线性涨。所以我现在的默认值就是chunk 大小的 15% 左右,比如 500 字的块留 75 字重叠,按句子边界对齐,别从字中间切。
def split_with_overlap(text, size=500, overlap=75): chunks, start = [], 0 while start < len(text): end = start + size chunks.append(text[start:end]) start = end - overlap # 回退 overlap,制造重叠 return chunks重叠太大的反噬
overlap 拉到 40% 那次,我还碰上个意外问题:检索回来的 top5 里,有三段内容高度雷同,因为它们本就大面积重叠。等于五个名额被同一坨信息占了三个,真正多样的资料反而挤不进来,模型看到的视野变窄了。后来我在重排那步加了个去重,把相似度过高的块合并,才缓过来。
所以重叠不是越大越保险。它解决的是「接缝截断」,代价是「冗余和重复召回」,得卡在一个平衡点。
还有个更省心的法子
如果你的文档结构清楚(有标题层级、有明确的步骤编号),与其纠结字数重叠,不如按语义边界切——一个小节一块、一个步骤一块,天然不会从句子中间断开,overlap 的需求就小很多。我现在的策略是:结构化文档优先按标题切,实在是大段连续正文才退回固定长度+重叠。
这些切法我没自己写一堆解析代码。智能体配在一个零代码就能搭 RAG 的平台上,文档传上去能选切分方式、调 chunk 大小和重叠比例,向量化它自动做,我主要精力花在调参和量召回上。当然它给的切分策略也就那几档,真遇到特别刁钻的版式,还得自己预处理过一遍再喂进去,别指望它全自动搞定。
(模型和知识库我都走讯飞星辰 MaaS,托管的现成大模型和 RAG,直接调没自部署)
你们 overlap 一般留多少?有没有也遇到过答案卡在接缝上死活召回不到的?评论区说说你的切分配方。