应用工程

RAG Chunking 策略的量化评测:别再凭感觉调 chunk_size

中文 RAG 工程实战那篇文章讲过几种常见的 chunk 切分实现,但只停留在”这几种方法怎么写代码”,没回答一个更实际的问题:在你自己的文档集上,到底哪种切分策略更好、chunk_size 该设多大? 大多数团队的做法是抄一个 500 字 + 50 字重叠的经验值就上线了,从来没验证过这个数字是不是适合自己的场景。本文用量化实验的方式把这件事讲清楚。

一、评测怎么做:不重复造轮子

评测指标本身不需要重新发明,我们在LLM 应用 RAG 效果评测框架里已经给过 Context Recall 和 MRR 的实现,本文直接复用这套指标,只是把变量换成”chunking 策略”。核心思路是固定 embedding 模型和检索 top-k,只改变切分方式,观察指标变化——这样才能确认差异是切分策略带来的,而不是其他变量干扰的结果。

二、四种参与对比的切分策略

策略做法直觉上的优劣
固定长度按字符数硬切,前后留重叠区实现最简单,但可能把一句话甚至一个表格行切成两半
按句子/段落沿自然语言边界切分,凑够目标长度语义完整性更好,但长度波动较大
滑动窗口重叠固定长度基础上加大重叠比例(如 30%)缓解边界截断问题,但存储和检索成本上升
语义/递归切分先按标题/段落层级递归拆分,逼近语义边界后再控制长度效果通常最好,但实现复杂度和处理耗时更高

三、关键变量:chunk_size 和 overlap 的扫描实验

单独换切分方法只是第一层变量,chunk_size 本身的取值同样会显著影响结果,所以实验设计上要做二维扫描:固定一种切分策略,分别在 200/500/800/1200 字符四档 chunk_size 下测一遍,同时搭配 0%/10%/30% 三档重叠比例,组合起来跑一轮网格实验。

def sweep_chunking(build_index_fn, chunk_fn, eval_fn, sizes, overlaps, docs, eval_set):
    results = []
    for size in sizes:
        for overlap in overlaps:
            chunks = chunk_fn(docs, chunk_size=size, overlap=overlap)
            index = build_index_fn(chunks)
            score = eval_fn(index, eval_set)
            results.append({"size": size, "overlap": overlap, **score})
    return results

跑完把结果按 context_recall 排序,通常能看到一个明显的”甜点区间”——chunk_size 太小会导致语义碎片化,检索到的片段缺乏上下文;太大又会把不相关内容和目标答案混在一起,降低 rerank 之后的精确率。这个甜点区间因文档类型而异,没有放之四海而皆准的默认值,这也是为什么”抄一个 500 字的经验值”经常在实际业务上表现平庸。

四、不同文档类型,对切分策略的敏感度不一样

实测中最有价值的发现往往不是”哪个策略总分最高”,而是不同文档类型对切分策略的敏感度差异很大:

  • 长篇说明文/文章类文档:语义/递归切分相对固定长度的提升最明显,因为这类文档天然有清晰的标题层级可以利用
  • 表格密集型文档:固定长度和滑动窗口都容易把表格从中间切断,导致检索到的片段”有表头没数据”或反过来。这类文档建议在切分逻辑里加表格边界检测,宁可让 chunk 长度不均匀,也不要把表格切碎
  • 代码/API 文档:按函数或代码块边界切分(而不是字符数)效果显著优于通用切分策略,因为一段代码被从中间切断基本等于检索结果失效
  • FAQ/问答对文档:天然适合按问答对为单位切分,chunk_size 和 overlap 这两个参数在这类文档上的影响反而不大

这个结论的实际意义是:不要用一套 chunking 参数应付所有文档类型,混合文档库应该先按类型分流,再各自应用最适合的切分策略,而不是找一个”平均意义上还不错”的通用参数。

五、一个容易被误解的直觉:重叠区不是越大越好

很多人下意识觉得 overlap 加大总是更安全,实测中这个直觉并不总成立。重叠区过大(比如超过 40%)会导致向量库里出现大量高度相似的 chunk,检索 top-k 时容易被同一段内容的多个重叠版本占满名额,反而挤掉了其他真正相关但没被重复收录的片段,实际拉低了检索多样性。10%-20% 的重叠比例在多数场景下已经能覆盖边界截断问题,继续加大收益递减且有副作用。

六、落地建议:怎么给自己的文档定起点参数

  1. 从文档类型出发,不要从”经验值”出发——先判断你的文档是长文、表格、代码还是 FAQ,按上一节的规律选切分方法起点
  2. 用二维扫描实验找 chunk_size 和 overlap 的甜点区间,不要只测一组参数就上线
  3. 至少构建 20-30 条带标准答案的评测样本,覆盖不同段落位置的答案(开头/中间/结尾),避免评测集本身有偏
  4. 把这套评测接入 CI,参考把模型评测接进 CI 流水线的思路,未来换 embedding 模型或调整文档格式时能第一时间发现召回率下降

七、常见坑

  • 只看 Context Recall,不看 rerank 后的精确率:召回率高但塞进 prompt 的片段质量差,一样会拉低最终生成质量,两个指标要一起看
  • 评测集和生产查询分布不一致:用工程师自己编的测试问题跑评测,往往比真实用户提问更规范、更容易命中,评测分数会虚高
  • 切分策略调好之后从不复测:文档库内容会持续增长和变化,半年前调好的参数不代表现在依然最优,建议按季度复跑一次评测

八、相关阅读