第四章:Chunking 策略与粒度选择
4.1 为什么必须切分
切分通常有三个直接原因:
| 理由 | 说明 |
|---|---|
| 窗口限制 | 一篇长文档可能远超模型上下文预算,无法整篇塞入 |
| 检索精度 | 整篇文档的向量是全文语义的平均,会把具体细节稀释掉 |
| 成本与延迟 | 只把相关片段送进 Prompt,而不是整本手册 |
其中更直接影响检索质量的是第二条。
一个 Embedding 向量的表达能力是有限的。把一篇涵盖十个主题的长文压成一个向量,得到的是这十个主题的"平均语义"——它对任何一个具体问题都不够相似。
切分的目的,不只是为了装得下,而是为了让每个向量表达一个足够聚焦的语义单元。
4.2 粒度的核心矛盾
flowchart TB
G[chunk 粒度] --> S[切得太小]
G --> L[切得太大]
S --> S1[语义聚焦 检索精准]
S --> S2[上下文缺失
指代不明 结论没有前提]
L --> L1[上下文完整]
L --> L2[语义被稀释
混入无关内容 检索不准]
这是一个无法被完全消除的矛盾,只能被缓解——第五章的方法都是围绕这组权衡展开的。
4.2.1 切得太小会发生什么
一个 100 字的片段可能长这样:
「该比例不得超过前款规定的上限。」
这段话单独存在时毫无检索价值:「该比例」是什么?「前款」是哪一款?向量化之后它既不像任何一个真实问题,命中了也无法支撑回答。
这就是语义碎片化。
4.2.2 切得太大会发生什么
一个 5000 字的片段可能同时包含请假制度、报销制度和考勤制度。用户问报销时:
- 这个片段的向量因为混了另外两个主题,与问题的相似度下降,可能根本召不回来;
- 就算召回了,也把两段无关内容塞进了 Prompt,按第二章 2.4.2 节的结论,这会主动损害生成质量。
4.3 一个反直觉但重要的实证结论
常见做法是先上语义切分,认为「切分算法越聪明,效果越好」。
但系统性的对比实验给出了不同的结论:
chunk 的大小和重叠量,对最终效果的影响远大于切分算法的选择。
在多个数据集上的对比中,复杂的语义切分方法(先算句子向量、再找语义断点)相比简单的固定大小切分,增加了显著的计算成本,却没有带来稳定的效果提升。
这个结论在工程上的含义很直接:
- 先把 chunk size 和 overlap 调好,这是投入产出比最高的动作;
- 不要默认先上语义切分,它的复杂度和收益不成正比;
- 如果要用更聪明的方法,优先考虑基于结构的切分(按标题层级),它几乎零成本且效果稳定。
这里说的语义切分,主要指「用 Embedding 相似度找语义断点」这一类方法。而基于 LLM 的命题化切分(把段落改写成一组独立自足的陈述句)是另一回事,它在实体密集的问答任务上有明确收益——但成本高得多(第五章展开)。
4.4 常见切分策略
flowchart TB
C[切分策略] --> F[固定大小 + 重叠]
C --> R[递归分隔符]
C --> ST[结构化 按标题层级]
C --> SE[语义切分]
C --> SP[特殊内容专门处理]
| 策略 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 固定大小 + 重叠 | 按固定 Token 数切,相邻片段重叠一部分 | 简单、可预测、效果稳定 | 可能在句中截断 |
| 递归分隔符 | 依次按段落、句子、词尝试分隔,直到满足大小 | 尽量不破坏自然边界 | 对无明显分隔的文本退化为固定切分 |
| 结构化切分 | 按 Markdown 标题层级或文档章节切 | 保留文档天然语义边界,性价比最高 | 依赖前一步保留了结构信息 |
| 语义切分 | 计算相邻句子向量相似度,在低谷处切断 | 理论上贴合语义 | 成本高、收益不稳定 |
| 特殊内容处理 | 表格、代码、公式整体不切 | 避免破坏强结构内容 | 需要先识别出这些内容 |
工程上的推荐组合:结构化切分为主(有标题层级时按章节切),固定大小 + 重叠兜底(章节过长时二次切分),特殊内容单独处理(表格和代码块整体保留)。
4.5 参数怎么定
4.5.1 chunk size
一个常用的起点是 500–1000 Token。但这只是起点,不是答案。单独给一个数字意义不大,关键是说明调整方向:
| 情况 | 调整方向 | 理由 |
|---|---|---|
| 事实型问答(FAQ、参数查询) | 偏小(200–500) | 答案集中在一两句话里,小片段更精准 |
| 需要推理和综合的问答 | 偏大(800–1500) | 需要完整论证链,切碎后逻辑断裂 |
| 法律、合同类文档 | 按条款切 | 条款是天然的语义单元 |
| 技术文档、API 手册 | 按小节切 | 结构清晰,一节一主题 |
| 对话记录 | 按轮次或话题切 | 单轮太碎,整段太杂 |
另一个约束是 Embedding 模型本身:多数模型有最大输入长度限制(常见 512 Token),超出部分会被静默截断。如果 chunk 大小超过模型上限,超出的内容根本没有进入向量——这是一个不报错的严重 bug,务必检查。
4.5.2 overlap
重叠的作用是兜底:万一在关键位置切断了,重叠部分能让被切开的内容在相邻片段中至少完整出现一次。
常用值是 chunk size 的 10%–20%。
- 太小:起不到兜底作用;
- 太大:存储和计算成本上升,且同一内容多次命中会挤占 Top-K 名额,降低召回结果的多样性。
最后一点是重叠过大的隐性代价,容易被忽略。
4.5.3 怎么确定最终值
最终值要靠评测确定。流程是:
- 构造一个有标注答案的评测集(几十到几百条真实问题);
- 用不同的 chunk size / overlap 组合建库;
- 测检索层指标(Hit@K、MRR)和端到端答案质量;
- 选择效果与成本的平衡点。
关键提醒:检索指标好不等于最终答案好。必须同时看端到端指标(详见第十八章)。
4.6 粒度与后续环节的联动
chunk 粒度不是孤立决策,它会影响下游多个环节:
| 影响的环节 | 影响方式 |
|---|---|
| Top-K 取值 | 片段越小,需要的 K 越大才能覆盖同等信息量 |
| Prompt 预算 | K × chunk size 决定了送进模型的 Token 量 |
| Rerank 成本 | 候选数越多,精排成本越高 |
| 引用溯源粒度 | 片段越小,引用定位越精确 |
| 更新成本 | 片段越小,文档变更时重建的片段越多 |
所以「chunk 调大一点」不是一个局部改动,它会连锁改变 Top-K、Prompt 预算和成本结构。调参时应该整体评估,而不是只看检索指标。
4.7 常见错误
4.7.1 死记一个数字
「500 Token」是起点不是答案。不说明调整依据,等于没回答。
4.7.2 一上来就上语义切分
有实证表明它成本高、收益不稳定。应该先把 size 和 overlap 调好。
4.7.3 chunk 超过 Embedding 模型输入上限
超出部分被静默截断,不报错,排查极其困难。
4.7.4 所有文档类型用同一套参数
合同、代码、对话记录的天然语义单元完全不同。
4.7.5 把表格和代码块按字符数硬切
强结构内容被切开后基本失去价值,应该整体保留或专门处理。
4.7.6 只看检索指标不看端到端效果
召回率提升但答案变差的情况是真实存在的(例如召回了更多但更杂的内容)。
4.7.7 忽略重叠过大的副作用
同一内容在 Top-K 里重复出现,会挤占其他有效片段的名额。
4.8 本章总结
- 切分的目的,是让每个向量表达一个聚焦的语义单元,而不只是为了装得下;
- 核心矛盾:切小了语义碎片化,切大了语义被稀释——只能缓解,无法消除;
- 实证结论:chunk size 和 overlap 的影响远大于切分算法;语义切分性价比不高;
- 推荐组合:结构化切分为主 + 固定大小重叠兜底 + 特殊内容单独处理;
- 参数起点 500–1000 Token、重叠 10%–20%,但必须按文档类型和问题类型调整;
- 硬性检查:chunk 不能超过 Embedding 模型的输入上限,否则被静默截断;
- 粒度是全局决策,会连锁影响 Top-K、Prompt 预算、Rerank 成本和更新成本;
- 必须用评测集实测,且检索指标和端到端指标都要看。