艾体宝干货|RAG 分块策略:分块方式是如何影响检索效果的

4 阅读9分钟

RAG Pipeline 总是召回一堆不相关的内容?或者检索结果总差一点点,偏偏缺的就是最关键的那句话?十有八九,问题不在 embedding 模型,也不在 prompt,而是出在文档切割方式上。

为什么分块如此重要

构建 RAG 系统时,"分块"这个环节往往被当成一个不起眼的预处理步骤,随手套个默认参数就跑了。但实际上,它直接决定了检索的精度、索引体积、查询延迟,乃至最终 LLM 生成答案的质量。

分块(Chunking)的本质,是把文档切成更小的片段,再分别做 embedding 并写入向量索引,供后续检索使用。切得太细,每个片段都失去了上下文,模型完全不知道这段话在讲什么;切得太粗,一个向量里同时塞进了好几个主题,检索时哪个都匹配不准。

这个问题之所以存在,根本原因在于:​embedding 模型有固定的最大输入长度​。超出长度的文本会被直接截断,后半段就消失了。分块是在这个约束下,尽可能保留语义完整性的一种妥协。

但另一个现实是:​没有一种分块策略能适配所有文档类型​。一份法律合同、一个 Python 代码仓库、一篇技术博客,信息组织方式完全不同,用同一套策略处理,至少两种会得到很差的结果。

主流分块策略

固定长度分块(Fixed-size Chunking)

最简单粗暴的方式:按字符数、词数或 token 数均匀切割,相邻块之间可以设置一定的重叠(overlap)。切割时完全不考虑句子或段落边界。

优点是实现零门槛,适合快速做 baseline 验证。缺点也很明显:它会在任意位置截断,把本来连贯的概念一刀两断。对于结构化程度较高的文档,基本不推荐在生产中使用。

递归分块(Recursive Chunking)

相比固定分块,递归分块要聪明不少。它维护一个有优先级的分隔符列表(通常是:段落换行 → 单行换行 → 空格),依次尝试切割。如果某个块仍然超过目标大小,就用更细粒度的分隔符继续切。

这样的好处是能较好地保留文档的全局结构,代价是块的大小会有一定波动。对于大多数 RAG Pipeline 来说,​递归分块是一个合理的起点​:语义连贯性可以接受,实现简单,而且不需要在索引构建阶段调用 embedding API,成本低。

语义分块(Semantic Chunking)

如果觉得固定边界还是太机械,语义分块尝试从另一个角度解决问题:​在话题真正切换的地方划定边界​。

具体做法是:先对每个句子分别做 embedding,然后计算相邻句子之间的余弦相似度。当相似度低于某个阈值时,认为话题发生了切换,在此处放置一个块边界。

理论上很美好,但实践中表现参差不齐。有研究对比语义分块与递归分块,发现语义分块产生的块数更多,索引体积更大,但检索效果的提升并不总是稳定。这不意味着语义分块一定更差——关键是要在自己的数据集上做对比实验,不要凭直觉下结论。

结构感知分块(Document-structure-based Chunking)

如果文档本身有明确的结构(比如技术文档、API 参考手册、财报、法律条文),最好直接利用这些结构:按标题、章节、段落、表格来划分块,只有当某个元素超过最大块大小时才进一步切割,否则保持整体。

法律文本就是一个典型场景。它内部有复杂的嵌套章节和大量交叉引用,通用分块策略会破坏这些逻辑关联。当文档结构本身就携带语义信息时,感知结构的分块方式能保留这些关系。

基于 LLM 的分块(Agentic Chunking)

最高端的方式是让 LLM 直接参与分块。它会把文本分解为"原子命题"——每个命题只包含一个独立的事实陈述——再把相关命题归组成块。

这种方式的好处是极度灵活,缺点也同样明显:索引构建延迟高、成本高,对 prompt 工程的要求也很高。​适合文档量不大但价值极高的场景​,比如高质量知识库、内部 SOP 等。

块大小如何影响检索质量

选定策略之后,块大小是下一个关键参数。一个重要结论是:查询类型决定了最优块大小。

  • 精确事实类查询​("某个 API 参数的默认值是多少")——小块更优。有实验表明,将块大小从 1024 token 降至 64 token,事实型 recall@1 可以提升 10 到 15 个百分点。原因是每个向量代表一个更精确的概念,匹配更准。
  • 叙述理解类查询​("这个架构方案的整体思路是什么")——大块更优。小块会丢失上下文的连贯性,这类问题需要更完整的段落才能回答准确。

另一个常被忽视的坑:​塞给 LLM 的上下文并非越多越好​。实验数据显示,当检索上下文超过约 2500 token 时,生成质量会开始下降。堆积大量块进 prompt,可能反而稀释了有效信息。

生产中最常见的分块错误

1. 块太小

很多人的块大小参数来自几年前的教程默认值(比如 200 字符),但 embedding 模型已经迭代了好几代。块大小是一个需要调优的超参数,不应该直接复制旧配置。

2. 块太大

反过来,块如果横跨多个主题,单个向量就会"语义模糊"——什么都有,什么都不精确。把整份 PDF 作为一个块更是常见错误的极端情况,直接触发了前面提到的上下文上限问题。

一个实用的折衷方案:​**用小块做检索(保证精度),把检索到的块周边的更大"父块"一起送给 LLM 生成(保证上下文完整性)**​。检索粒度和生成上下文粒度解耦,各司其职。

3. 忽略文档结构

对所有文档类型(PDF、HTML、Markdown、代码、表格)套用同一套分块策略,会丢失文档结构中蕴含的语义信息。表格被从表头处截断、函数在函数体中间被切开、列表项和对应的说明文字被分离——这些都会产生语义残缺的块。

4. 丢弃 Metadata

就算块的边界划得很准,如果丢失了元信息,这个块也只是一段孤立的文字。​文档标题、章节标题、页码等 metadata 对检索排序和生成质量都有帮助​。尤其是跨块的问题(需要综合多块信息才能回答),metadata 更是关键线索。

5. 只用向量检索

向量检索擅长语义相似度,但对精确的关键词匹配(比如专有名词、产品型号、代码中的函数名)并不可靠。​混合检索(Hybrid Search)是更稳健的选择​:将 BM25 的关键词匹配分数与稠密向量的相似度分数融合(常用 Reciprocal Rank Fusion,即 RRF 算法),两者互补。

新方向:减少跨块的上下文损失

上面这些问题的本质,几乎都指向同一个根源:​块边界导致上下文断裂​。近几年涌现出一些专门针对这个问题的新技术,值得了解:

Late Chunking​:先对完整文档做 embedding(让每个 token 的表征都感知到整篇文档的上下文),再从中切出块向量。有基准测试显示,这种方式在长文档检索任务上平均比 naive chunking 提升约 3%。前提是的 embedding 模型上下文窗口足够大。

Contextual Retrieval​:在对每个块做 embedding 之前,先用 LLM 为这个块生成一段上下文摘要,把"这段话在整篇文档中的位置"告诉模型。结合混合检索和 reranking,可以显著减少检索失败率。缺点是索引构建阶段对每个块都要调一次 LLM,大语料库成本不容忽视。

Pseudo-Instruction Chunking(PIC):用文档级别的摘要来指导块边界的划定,但不需要对每个块单独调用 LLM。有对比实验显示,在 6 个 QA 数据集上,PIC 的 hits@5 为 58.4,优于固定分块的 54.5 和语义分块的 56.0。

这三种方法都是在用更多的计算换取更好的检索质量,要不要用、用哪种,取决于的语料规模、延迟预算和现有检索质量与目标之间的差距。

分块决策的连锁反应

分块策略不只影响检索质量,还会影响整个基础设施。块越多,向量越多,索引越大,内存占用越高,索引构建越慢——这是一条连锁反应链,不能割裂来看。

索引算法的选择会被迫跟着变。在大规模场景下,HNSW 索引可以达到数百 GB。更细粒度的分块会推高向量总数,可能迫使从基于内存的索引迁移到基于存储的方案,这两者在吞吐量和精度上的特性并不相同。

Reranking 的代价也值得重视。Reranker 可以弥补检索质量的不足,但代价是延迟:有实验测到加入 reranker 后延迟增加了 9.2 倍(从 0.22 秒到 2.02 秒)。如果的分块策略本身有缺陷,reranker 只是在打补丁,而不是根治问题。

实践建议

没有放之四海而皆准的最优块大小,但的数据集和查询类型通常会指向一个较优解。以下是一个可操作的起点:

  1. 用递归分块做 baseline​,先跑通整个 Pipeline
  2. 梳理的查询类型分布​:精确查询多还是叙述类查询多,决定块大小的方向
  3. 把块大小和分块策略当成超参数​,系统性地做 ablation 实验
  4. 提前规划向量索引的架构​,别等到向量数量暴涨才想着换方案

分块策略就是检索策略。在这一步做的每个决策——用哪种策略、块多大、保留哪些 metadata——都会传导到检索精度和基础设施成本上。值得认真对待。