登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

RAG 分块重叠过大为什么会降低检索多样性

来源:17golang原创

时间:2026-10-08 23:15:55 409浏览 收藏

RAG 检索里,chunk_overlap 不是越大越稳。它的作用是让切分边界附近的句子在相邻块中保留一份上下文;当重叠区已经接近一个完整段落时,相邻 chunk 的向量会非常相似,top-k 很容易被同一来源的重复内容占满,其他章节反而进不来。更稳的处理是:先把 overlap 控制在跨边界所需的范围,再用候选集扩大、元数据去重和 MMR 重排保护多样性。

要点速览
  • 先按 source_id、section_id、chunk_index 统计相邻召回比例,确认是不是切分重复。
  • 切分优先保留标题、段落和句子边界,overlap 只承担跨边界补上下文。
  • 最终结果要同时看相关性、来源覆盖、重复比例和送入模型的上下文长度。

先判断:重复结果来自哪里

一次查询只看前几条文本,很难区分“语义确实相近”和“同一段文字被复制了几次”。建议给每个 chunk 保留 source_id、section_id、chunk_index 和字符范围。把召回结果按来源和章节分组,再计算相邻索引占比;如果前 5 条里有 3 条以上来自同一来源的连续索引,同时文本开头和结尾高度重合,优先检查切分参数,而不是先换 embedding 模型。

# 统计召回结果是否被同一来源的相邻分块占满
from collections import Counter

def repeat_ratio(results):
    # 只统计来源与分块序号,避免把不同来源的同词命中误判为重复
    same_source = Counter(item["source_id"] for item in results)
    adjacent = 0
    for left, right in zip(results, results[1:]):
        # 连续 chunk 往往说明重叠区或相邻段落被重复召回
        if left["source_id"] == right["source_id"] and right["chunk_index"] == left["chunk_index"] + 1:
            adjacent += 1
    return {
        "same_source_top1": same_source.most_common(1)[0][1] if results else 0,
        "adjacent_pairs": adjacent,
        "result_count": len(results),
    }
RAG 分块重叠、段落边界、向量索引与相邻 chunk 召回之间的静态结构说明图
图1:RAG 分块边界与重复召回的关系说明图,不是运行截图。

把 overlap 限制在边界上下文范围内

切分参数应该先回答“一个 chunk 要表达什么”,再回答“重叠多少”。LangChain 文档把文本切分描述为让大文档变成可独立检索、适配模型上下文窗口的较小块,并建议优先利用段落、句子等自然结构。对 Markdown、HTML 或知识库条目,标题和段落通常比固定字符数更可靠。

如果一个 chunk 约 500 个 token,重叠区可以先从 10% 左右的量级做基线,但这只是实验起点,不是通用答案。短 FAQ、代码函数、表格和长叙述的最佳比例并不相同。重叠区超过一个完整语义单元后,新增的上下文收益会变小,而索引条数、嵌入计算和重复召回都会增加。

现象优先检查调整方向
连续 chunk 占据 top-koverlap 与段落长度缩小 overlap,保留结构边界
答案缺少跨段定义切分是否打断标题/列表改用递归结构切分,而非盲目加 overlap
结果相关但来源单一fetch_k 与去重策略先扩大候选,再做来源或内容去重

用候选集和 MMR 把相关性与多样性分开调

不要把最终返回数量 k 和候选数量混成一个参数。可以先取更大的 fetch_k,然后过滤同一 source_id 的连续块,再用 MMR 选择最终上下文。MMR 的目标是同时考虑查询相似度和已选结果之间的相似度;lambda_mult 越偏向相关性,越不愿意牺牲相似度换多样性。

# 先扩大候选,再让 MMR 在相关性和多样性之间取舍
retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 5,          # 最终送入上下文的数量
        "fetch_k": 20,   # 先取更大的候选池,给去重留空间
        "lambda_mult": 0.55,  # 越小越重视候选之间的差异
    },
)

# 记录检索配置,便于离线回归时比较重复比例和来源覆盖
docs = retriever.invoke(query)
RAG 候选池、来源去重、MMR 相似度与多样性权衡的静态结构说明图
图2:候选池到 MMR 结果集的关系说明图,不是运行截图。

如果调低 lambda_mult 后结果变得“很分散但答非所问”,说明候选池本身不够相关,或去重条件过强。此时应先修正 chunk 的语义完整性和元数据,再逐步提高相关性权重。相反,如果结果仍来自同一章节,可以提高 fetch_k,并对同一章节设置保留上限,而不是继续扩大 overlap。

用离线回归集确定可接受范围

准备一组覆盖定义查找、跨章节比较、故障定位和多来源汇总的问题。每次只改变一个因素,记录命中章节数、相邻 chunk 比例、重复字符比例、上下文 token 数和答案是否引用了正确来源。这样才能看出某个 overlap 设置是在改善边界召回,还是只是让相似文本重复出现。

实践中可以保留低、中、高三档 overlap,先用小样本筛掉明显重复的方案,再扩大评测集。对代码、表格和短条目,结构切分和父子文档关系通常比统一比例更重要;对长叙述,适度 overlap 才有机会补回跨段语义。

相关问题

chunk_overlap 设为 0 会不会丢上下文?

会有这个可能,但不代表必须把 overlap 调大。先检查标题、段落、列表是否被完整保留;只有边界确实截断语义时,再增加少量重叠。

只用去重能解决重复召回吗?

不能。去重只能处理结果层面的重复,无法修复切分导致的语义残片,也可能误删同一来源中真正互补的段落。

MMR 和换 embedding 模型应该先做哪个?

如果重复结果明显来自连续 chunk,先检查切分和候选策略;当切分边界合理、查询仍普遍漏召回时,再用离线集评估 embedding 更换。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>