文本分块(Chunking)不是简单地把长文档“剪短”,而是在检索粒度、上下文完整性和处理成本之间做取舍。

块过小,可能缺少标题、限定条件和上下文,导致语义不完整;块过大,则可能同时包含多个主题,使向量表示失焦,并挤占生成模型的上下文空间。因此,分块质量不能只用块长度判断,还要看它是否保留了回答问题所需的最小完整语义。

本文不讨论具体框架或产品,而是按“主要依靠什么信号决定切分边界”这一标准,将分块方法划分为五类:

  1. 固定大小分块;
  2. 语义分块;
  3. 递归分块;
  4. 结构分块;
  5. LLM 分块。

1. 分块问题的统一模型

1.1 分块之前先完成解析

PDF、Word、Markdown、网页和表格不能直接被同一种规则可靠处理。分块之前通常需要先完成:

  • 文本提取与 OCR;
  • 阅读顺序恢复;
  • 标题、段落、列表、表格和代码块识别;
  • 页码、章节路径和原文位置记录;
  • 空白、乱码及重复页眉页脚清理。

分块器只能利用解析器提供的内容和边界。若双栏 PDF 的阅读顺序已经错乱,或者标题被识别成普通正文,后续再复杂的分块算法也很难恢复原始语义。

1.2 五类方法的分类依据

五类方法的区别在于首要边界信号不同:

方法 主要边界信号 是否理解文档结构 是否需要模型
固定大小分块 字符数或 Token 数
语义分块 相邻内容的语义变化 Embedding 模型
递归分块 有优先级的分隔符 部分
结构分块 标题、条款、行列、版面等结构 不一定
LLM 分块 大模型对主题和任务的判断 LLM

重叠窗口、父子关系、标题补全和摘要不是新的分块大类。它们不负责确定第一层边界,而是在基础块之上补充上下文或改变检索方式。

1.3 一条通用处理流水线

flowchart LR
    A[原始文档] --> B[解析与清洗]
    B --> C[生成基础单元]
    C --> D{选择边界判定方法}
    D --> D1[固定大小]
    D --> D2[语义]
    D --> D3[递归]
    D --> D4[结构]
    D --> D5[LLM]
    D1 --> E[长度与完整性校验]
    D2 --> E
    D3 --> E
    D4 --> E
    D5 --> E
    E --> F[补充元数据与上下文]
    F --> G[Embedding 与索引]
    G --> H[检索和问答评测]

无论采用哪种方法,最终都需要满足三个约束:

  1. 块不能超过下游模型可以安全处理的长度;
  2. 块应尽量保留最小完整语义;
  3. 块必须能够追溯到原文位置和所属结构。

2. 固定大小分块

固定大小分块只依据长度切分文本,是最容易实现和预测的方法。

2.1 实现逻辑

先确定长度单位:

  • 字符:计算简单,但字符数与模型 Token 数没有固定换算关系;
  • Token:更接近模型输入限制,但必须使用与下游模型相匹配的 Token 计算方式。

设块大小为 W,相邻块重叠长度为 O,则步长为:

1
S = W - O

分块过程如下:

  1. 从位置 0 读取最多 W 个单位;
  2. 生成第一个块,并记录起止位置;
  3. 向后移动 S 个单位;
  4. 重复读取,直到文档结束;
  5. 删除空块,并根据需要合并过短的尾块。

例如 W=200O=40 时,各块覆盖范围依次为:

1
2
3
4
[0, 200)
[160, 360)
[320, 520)
...

这里的数字只用于说明算法,不代表推荐参数。

2.2 关键实现细节

固定大小并不等于任意位置硬切。工程上通常还需要处理:

  • 若边界落在句子中间,可在限定范围内向前寻找句末;
  • 代码、URL、表格单元格等不可随意断开的内容应受保护;
  • 重叠区域必须按原文位置截取,避免重复拼接造成字符缺失或顺序错误;
  • 最后一个块过短时,可以并入前块,但合并后仍要检查长度上限;
  • 空白归一化不能破坏原文偏移映射。

一旦加入“优先寻找句末”等规则,它就不再是完全严格的等长切分,而是“固定预算 + 轻量边界修正”。

2.3 优点、缺点与适用场景

优点:

  • 实现简单,时间复杂度接近线性;
  • 块大小稳定,容易估算索引数量和成本;
  • 不依赖文档格式和外部模型;
  • 适合作为其他方法的长度兜底。

缺点:

  • 不知道主题和结构,容易切断句子、条款或步骤;
  • 同一主题可能被拆到多个块,不同主题也可能被装入同一块;
  • 增加重叠只能降低边界损失,不能真正识别语义边界。

它适合结构较弱、规模较大、需要稳定吞吐的文本,也适合作为所有复杂分块方法的最终长度保护机制。

3. 语义分块

语义分块依据相邻内容之间的语义变化决定边界。它关注的不是“已经写了多少 Token”,而是“主题是否发生了明显转换”。

3.1 实现逻辑

一个常见实现包含以下步骤:

  1. 将文档切成句子或较小的基础单元;
  2. 为每个基础单元生成向量;
  3. 计算相邻单元之间的距离;
  4. 在距离明显增大的位置设置候选断点;
  5. 合并断点之间的连续单元;
  6. 对过短块进行相邻合并,对过长块使用长度兜底。

若第 i 个基础单元向量为 v_i,可以用余弦距离描述相邻语义变化:

1
d_i = 1 - cosine_similarity(v_i, v_(i+1))

d_i 超过阈值时,就把 ii+1 之间标记为候选边界。

3.2 阈值如何确定

固定相似度阈值很容易受语言、Embedding 模型和文档类型影响。更稳妥的做法是根据单篇文档或同类文档的距离分布动态判断,例如:

  • 选择距离分布中较高的分位点;
  • 识别距离曲线的局部峰值;
  • 比较当前距离与附近窗口的平均距离;
  • 同时设置最小块和最大块长度,约束断点结果。

阈值不是越敏感越好。阈值过低会产生大量碎片,过高则会把多个主题合并。

3.3 关键实现细节

  • 基础单元质量:句子切错后,向量距离再准确也无法恢复正确边界;
  • 上下文向量:可以为单句生成向量,也可以把前后句加入向量输入,降低短句歧义;
  • 相邻约束:通常只比较原文中相邻单元,避免改变文档顺序;
  • 长度约束:语义连续不代表可以无限合并,仍需设置硬上限;
  • 批量计算:Embedding 应批量生成,并缓存文本哈希与向量;
  • 版本记录:更换模型或阈值后,边界可能整体变化,需要记录配置版本。

3.4 优点、缺点与适用场景

语义分块能够发现没有明确标题或空行的隐含主题变化,适合访谈、会议纪要和长篇叙述文本。

它的主要问题是成本更高、阈值不稳定,而且“向量距离突然增大”只是主题变化的代理信号,不保证边界一定符合具体问答任务。对于法规、表格等结构明确的文档,语义信号通常不应覆盖可靠的结构边界。

4. 递归分块

递归分块使用一组从强到弱排列的分隔符,优先在更自然的边界切分,只有块仍然过长时才使用更细粒度的规则。

4.1 实现逻辑

以普通文本为例,分隔符优先级可以表示为:

1
章节空行 → 段落换行 → 句末标点 → 子句标点 → 空格 → 字符

核心过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
split(text, level):
如果 text 不超过长度上限:
返回 text

使用当前层级的分隔符切出片段

如果当前分隔符无法切开:
使用下一层级继续处理 text

按长度预算合并相邻短片段

对仍然过长的片段:
使用下一层级递归处理

返回全部结果

递归结束必须有明确兜底。最后一级通常按字符或 Token 窗口切分,否则遇到没有空格和标点的长字符串时可能无法终止。

4.2 分隔与合并

递归分块不只是“依次调用多个分隔符”。一个可靠实现需要同时处理:

  1. 分隔符是否保留:句号、换行和标题标记可以保留在前块、后块,或单独作为元数据;
  2. 短片段合并:切出的片段应按原文顺序贪心合并,但合并结果不能超过上限;
  3. 超长片段递归:只对当前层无法控制的片段进入下一层;
  4. 重叠生成:应从已完成块的尾部选择完整基础单元,而不是再次随意截取;
  5. 偏移维护:分隔符的删除或前后归属不能破坏原文位置。

4.3 优点、缺点与适用场景

递归分块不需要模型,性能稳定,又比纯固定大小更能保留段落和句子边界,适合作为通用文本的基础方案。

它仍然不理解语义。若两个主题之间没有明显格式边界,它可能错误合并;若文本充满短换行,它又可能产生过多候选片段。因此,分隔符顺序必须根据语言和文档类型配置,不能把同一套字符列表用于所有内容。

5. 结构分块

结构分块依据文档显式结构确定边界。结构可能来自标记语法,也可能来自版面分析和领域规则。

5.1 实现逻辑

结构分块通常先把文档转换成树或有序节点序列:

1
2
3
4
5
6
7
8
文档
├── 标题
├── 章节
│ ├── 段落
│ ├── 列表
│ ├── 代码块
│ └── 表格
└── 章节

然后执行:

  1. 识别节点类型、层级和原文位置;
  2. 根据业务语义选择不可拆分的原子节点;
  3. 从叶子节点向上组合内容;
  4. 合并时保留祖先标题和必要元数据;
  5. 节点超过长度上限时,在节点内部调用递归或固定大小分块;
  6. 输出块内容、结构路径、节点类型和原文偏移。

结构分块的关键不是“看到换行就切”,而是理解换行代表标题、列表项、表格行还是普通换行。

5.2 不同结构的原子单元

文档类型 常见原子单元 应保留的上下文
Markdown/技术文档 标题节点、段落、代码块 完整标题路径、语言类型
法规/合同 条、款、项 法规名、章、节、适用范围
FAQ 一组问题与答案 分类、产品、版本
表格 表头与一行或一条业务记录 列名、主键、单位
演示文稿 单页或连续主题页 文件名、页码、章节
邮件 单封邮件或线程消息 主题、发件人、时间、引用关系

所谓“不可拆分”也不是绝对规则。一个超长代码块或表格行仍可能超过模型限制,此时必须选择截断、摘要、字段投影或更细粒度拆分,并明确记录数据损失。

5.3 父子分块是结构分块的增强

父块保存章节或较大的完整语义,子块保存便于精确匹配的片段。它通常按以下方式工作:

  1. 先通过结构分块生成父块;
  2. 在每个父块内部生成更小的子块;
  3. 保存 parent_id、标题路径和版本;
  4. 用子块参与匹配;
  5. 命中后根据上下文预算回填父块或父块片段;
  6. 对多个子块指向同一父块的结果进行去重。

父子关系解决的是“精确匹配需要小块,完整理解需要大块”的矛盾,但它不能修复质量差的父块。若一个父块已经混合多个主题,回填只会放大噪声。

5.4 优点、缺点与适用场景

结构分块最适合标题、条款、问答、行列等边界可靠的文档。它能够保留语义成立所需的结构上下文,也便于按章节、页码和字段过滤。

它高度依赖解析质量和领域规则。扫描件标题识别错误、表格跨页、层级编号不规范时,错误结构会被稳定地传播到所有块中。因此必须抽查结构树,而不能只检查最终块大小。

6. LLM 分块

LLM 分块让大模型根据主题、论点、任务目标和篇章结构判断边界。它可以处理规则难以描述的复杂文本,但不能把模型输出直接当作可信结果。

6.1 实现逻辑

一种可验证的实现流程是:

  1. 为原文中的句子或段落分配稳定编号;
  2. 按上下文窗口将编号后的内容送入模型;
  3. 要求模型只返回边界编号、主题标签和理由;
  4. 将边界编号映射回原文偏移;
  5. 合并相邻窗口产生的重复或冲突边界;
  6. 检查是否存在内容遗漏、顺序改变和长度超限;
  7. 对不合法结果重试或降级到确定性分块方法。

建议让模型返回结构化结果,而不是重写原文。例如:

1
2
3
4
{
"boundaries": [4, 9, 15],
"topics": ["安装条件", "安装步骤", "故障处理"]
}

边界 4 表示在第 4 个基础单元之后切分。最终块仍从原文截取,避免模型改写、遗漏或虚构内容。

6.2 长文档的窗口处理

长文档无法一次交给模型时,需要分窗口判断:

  • 相邻窗口保留少量重叠基础单元;
  • 只接受窗口中部的边界,边缘边界交给相邻窗口确认;
  • 使用稳定编号对重复结果去重;
  • 对冲突边界按置信度、投票或确定性规则裁决;
  • 最后进行一次全局长度和覆盖率检查。

这里的窗口重叠用于保证边界判断拥有上下文,不等同于最终索引块之间必须重叠。

6.3 关键风险

  • 相同输入可能产生不同边界,难以完全复现;
  • 模型可能遗漏编号、改变顺序或返回非法格式;
  • 提示词、模型和采样参数变化会导致索引整体漂移;
  • 处理成本和延迟明显高于规则方法;
  • 模型理解的“主题完整”未必与真实检索问题一致。

因此,生产实现应固定模型与提示词版本,保存原文偏移和原始响应,设置结构校验、重试上限及确定性降级路径。

6.4 适用场景

LLM 分块适合结构混乱但主题关系复杂、人工规则难以覆盖、且分块质量收益足以抵消成本的文档。对于格式稳定或规模极大的数据,优先使用结构或递归分块通常更容易控制。

7. 五类方法对比与选型

下表描述的是常见工程特征,不是对所有数据集都成立的基准测试结论:

对比项 固定大小 语义 递归 结构 LLM
边界质量 低到中 中到高 结构可靠时高 潜力高但不稳定
计算成本
结果稳定性 低到中
对解析质量依赖 中到高
配置难点 长度与重叠 模型与阈值 分隔符优先级 结构规则 提示词与校验
典型用途 兜底、弱结构文本 隐含主题变化 通用文本 规范文档 复杂非规范文本

可以按以下顺序选择:

  1. 文档存在可靠标题、条款、问答或行列结构时,优先结构分块;
  2. 没有可靠结构,但段落和句子边界基本可信时,优先递归分块;
  3. 主题转换隐含、格式边界较弱时,再考虑语义分块;
  4. 规则和向量都难以表达边界,且成本允许时,考虑 LLM 分块;
  5. 无论主方法是什么,都使用固定大小机制处理最终超长块。

这不是绝对优先级。最终选择应由真实查询和评测结果决定,而不是由算法复杂度决定。

8. 同一段文本的分块差异

假设文档包含“安装条件、安装步骤、升级说明”三个主题,其中安装条件与步骤位于同一章节,升级说明有独立标题。

方法 可能的处理结果
固定大小 按长度切分,边界可能落在安装步骤中间
语义 检测到“安装”到“升级”的语义变化,在主题转换处断开
递归 优先按空行或句末切分,再按预算合并相邻内容
结构 根据章节标题形成“安装”和“升级”两个块,并保留标题路径
LLM 根据任务要求,可能把安装条件与步骤保留为一个完整操作单元

这个例子只说明边界依据的差异,不能证明哪一种方法一定更好。如果用户经常只问某个安装步骤,小块可能更容易命中;如果答案必须同时满足前置条件,包含条件和步骤的完整块才更有用。

9. 分块后的上下文增强

基础边界确定后,还可以增加以下增强能力:

9.1 相邻块重叠

把前一块末尾的内容带入后一块,降低答案跨边界的风险。代价是块数量、向量存储和重复召回增加。重叠应根据跨边界失败样本设置,而不是固定套用某个比例。

9.2 标题路径补全

正文块可能只有“执行以下命令”,却没有说明产品和章节。将祖先标题作为元数据或检索文本补充进去,可以消除这类歧义,但应区分原文正文和附加上下文。

9.3 检索后窗口扩展

索引时保存较小块,命中后再读取前后相邻块。这不会改变索引边界,但会扩大交给生成模型的上下文。扩展时需要控制去重、顺序和总 Token 预算。

9.4 父块回填

先匹配子块,再返回其所属父块或父块中的相关区域。与简单相邻窗口相比,它利用的是结构关系,而不是物理距离。

这些增强策略可以组合使用,但每增加一层上下文,都可能引入更多噪声和成本。

10. 参数调优与验收

10.1 分块结果检查

至少统计:

指标 用途
块 Token 数 P50/P90/P99/最大值 发现碎片块和超长块
每文档块数 发现解析异常或分块爆炸
原文覆盖率 检查是否有内容遗漏
重复文本占比 评估重叠成本
结构路径覆盖率 检查标题和条款上下文
空块、乱码块比例 发现解析和清洗问题

原文覆盖率检查应基于稳定偏移或内容哈希,不能只比较清洗后的字符串长度。

10.2 检索评测

评测集应覆盖:

  • 单个事实即可回答的问题;
  • 答案跨句或跨段的问题;
  • 需要标题、适用范围或表头的问题;
  • 容易命中相似错误章节的干扰问题;
  • 文档中不存在答案的问题。

比较分块方案时,应固定 Embedding 模型、重排模型、Top-K 和查询集,只改变分块变量。可以记录 Recall@K、排序质量、有效上下文占比、最终答案正确率、索引成本和查询延迟。

其中“有效上下文占比”需要预先定义,例如:返回上下文中,与标准答案证据对应的 Token 数占全部返回 Token 数的比例。没有统一口径的指标不能直接横向比较。

10.3 常见误区

  1. 把字符数当成 Token 数:不同语言、代码和模型的换算关系不同;
  2. 只调块大小,不检查解析结果:很多问题实际来自 OCR 和阅读顺序;
  3. 认为语义方法不需要长度上限:主题连续的内容仍可能超过模型限制;
  4. 认为结构边界永远正确:解析错误会直接污染结构分块;
  5. 用重叠掩盖错误边界:重叠增加成本,但不会修复主题混合;
  6. 让 LLM 直接重写块内容:这会破坏原文可追溯性;
  7. 只看块数量和平均长度:这些指标不能代表检索和回答质量;
  8. 一次调多个变量:无法判断收益来自分块、Embedding 还是重排变化。

11. 总结

五类分块方法解决的是同一个问题:如何为长文档找到可检索、可理解且可控制成本的边界。

  • 固定大小分块用长度决定边界,简单稳定,适合作为兜底;
  • 语义分块用相邻内容的语义变化决定边界,适合隐含主题转换;
  • 递归分块按分隔符优先级逐层细化,是通用文本的可靠基础方案;
  • 结构分块利用标题、条款和行列等显式结构,适合规范文档;
  • LLM 分块根据主题和任务判断边界,能力强,但成本和不确定性最高。

实际系统通常不是五选一,而是选择一种主要边界方法,再组合长度兜底、标题补全、相邻窗口或父子回填。最终效果必须通过真实问题集验证,不能仅凭块预览或算法名称判断。