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

RAG 文档切片为什么越短越差:重叠窗口、语义边界与召回证据

来源:17golang原创

时间:2026-08-26 19:05:28 170浏览 收藏

知识库问答里有一种很容易误判的现象:把 chunk_size 从 800 字降到 300 字,向量检索的相似度看起来更高,回答却开始丢条件、丢例外,甚至把一段配置说明拼成相互矛盾的结论。问题通常不在“切得不够细”,而在切片把标题、限定条件和代码示例拆散了。

要点速览
  • 先保住标题、列表项、代码块和“但是/除非”这类语义边界,再讨论切片长度。
  • 重叠窗口只负责补回边界上下文,不能替代结构化切片;overlap 过大反而会放大重复召回。
  • 调参要用固定问题集核对“证据是否完整”,不能只看向量分数或 top-k 数量。
  • 召回结果应保留 source、section、chunk_index 等元数据,方便定位是哪次切片破坏了答案。

先把“越短越准”的误区拆开

短切片确实可能让一个向量更聚焦,但 RAG 最终要返回的是可引用的证据,不是孤立的关键词命中。比如“超时单位为秒,连接池为空时使用默认值”被从同一个小节拆成两段,检索器可能只拿到后半句;生成模型看到“使用默认值”,却不知道它只适用于连接池为空的情况。

因此本轮调参把目标定成三个可检查的结果:问题相关段落能被召回,前置限定条件没有丢,多个相邻片段合并后仍能还原原文顺序。相似度分数只作为线索。

从原文结构开始,而不是先填一个固定数字

先准备一份包含标题、正文、列表和代码块的 Markdown 文档,给每个切片保留以下元数据:

{
  "source": "connection-pool.md",
  "section": "连接超时与重试",
  "chunk_index": 3,
  "start_offset": 1840,
  "end_offset": 2468
}

切片器遇到二级标题时重新开始;遇到 fenced code block 时先完整收集代码,再判断是否超过上限。正文段落可以按句子或自然段合并,但不要为了凑长度把一个表格行或一个“注意”段落硬拆开。

RAG 文档切片示意:标题和代码边界被保留后,召回证据从残句变为完整小节

chunk_size 只约束上限,不定义语义

可以从 500、800、1200 字符做三组实验,但每组都要记录真实切片数量、平均长度和最长长度。中文字符数、token 数和字节数不是一回事,不能把一个系统的 800 直接搬到另一个嵌入模型上。

一旦单个代码块本身超过上限,应该按函数或配置段拆分,并在每个片段前补上小节路径,例如“连接超时与重试 / retry_policy”。这比把代码从中间截断后依赖 overlap 补救可靠。

用重叠窗口补边界,但别让重复片段淹没候选集

结构边界处理完后,再调 overlap。实践中可以先用固定的 10%~20% 作为实验起点,然后用问题集观察边界问题是否减少。如果 top-k=5 的结果里有 4 条来自同一个原文段落,只是 chunk_index 不同,说明 overlap 或候选放大已经在制造重复,而不是增加有效证据。

def select_chunks(chunks, query, top_k=5):
    hits = vector_search(query, limit=top_k * 3)
    hits = dedupe_by_source_section(hits)
    return merge_adjacent(hits, max_gap=1)[:top_k]

这里的关键不在函数名,而在顺序:先扩大候选,再按 source 和 section 去重,最后合并相邻 chunk。直接把 top-k 从 5 调到 20,往往只是把重复内容一并交给生成模型。

建立一组能暴露切片问题的召回验收题

验收题不要只问“这篇文档讲了什么”。至少准备四类问题:需要标题上下文的问题、需要前置条件的问题、需要完整代码块的问题,以及答案分散在相邻段落的问题。每次只改变一个变量,记录命中的 source、section、chunk_index、score 和最终是否覆盖必要证据。

现象优先检查不要先做的事
命中残句,条件在上一段结构边界、相邻合并、overlap盲目提高 top-k
结果高度重复按 source/section 去重继续扩大 overlap
代码只剩半段代码块识别与超长块拆分让模型自行补全
分数高但答案错元数据过滤与问题集标签把高分当成正确证据
RAG 召回调参对照:候选片段去重并合并相邻证据后,答案条件保持完整

推荐一条可复用的调参流程

  1. 固定 20~30 个真实问题和期望证据,先保存原始文档与解析版本。
  2. 只做结构化切片,暂时不改 top-k,检查标题、列表、表格和代码块是否完整。
  3. 依次测试 500、800、1200 等长度,记录召回覆盖率、重复率和平均上下文长度。
  4. 在最稳定的长度上调整 overlap,每次只改一个比例,并观察重复候选是否上升。
  5. 最后加入元数据过滤、相邻合并和 rerank,再回放同一套问题集。

如果某个参数组合只让分数变高,却让“必要证据覆盖率”下降,就不要保留它。调参结果应能在日志里解释:哪类问题得到改善,代价是上下文变长还是候选数量增加。

常见误区与边界处理

把 overlap 当成语义切分器

overlap 只能复制窗口边缘的内容,不能判断一段代码是否属于上一个标题,也不能理解“仅在失败重试时生效”的限定关系。结构化解析必须先于窗口切分。

只用一条问题判断参数好坏

单条问题很容易被偶然命中掩盖。至少要覆盖标题、条件、代码、相邻段落四类问题,且固定文档和嵌入模型后再比较。

把 top-k 当成召回质量

top-k 是候选数量,不是证据完整度。扩大候选后要同步做去重、相邻合并和来源过滤,否则上下文会变长,生成模型反而更难区分主答案与旁支说明。

相关问答

RAG 的 chunk_size 应该固定成多少?

没有跨项目通用的数字。先按文档结构切分,再用固定问题集比较 500、800、1200 等候选,依据证据覆盖和重复率选择。

overlap 越大,召回一定越好吗?

不一定。它能减少边界截断,但会增加重复片段和索引体积。发现同一 section 占满 top-k 时,应先去重或降低 overlap。

为什么相似度很高,回答仍然缺条件?

相似度只说明向量接近查询,不保证限定条件在同一个 chunk。检查标题边界、相邻片段合并和元数据过滤,通常比继续提高 top-k 更有效。

最后的检查清单

  • 切片是否保留 section 路径、顺序和 source?
  • 代码块、表格行和条件句是否被截断?
  • 测试是否固定问题集,只改变一个变量?
  • 召回日志是否记录 score 之外的证据完整度与重复率?
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>