文本分块(Chunking)不是简单地把长文档“剪短”,而是在检索粒度、上下文完整性和处理成本之间做取舍。
块过小,可能缺少标题、限定条件和上下文,导致语义不完整;块过大,则可能同时包含多个主题,使向量表示失焦,并挤占生成模型的上下文空间。因此,分块质量不能只用块长度判断,还要看它是否保留了回答问题所需的最小完整语义。
本文不讨论具体框架或产品,而是按“主要依靠什么信号决定切分边界”这一标准,将分块方法划分为五类:
- 固定大小分块;
- 语义分块;
- 递归分块;
- 结构分块;
- 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[检索和问答评测]
无论采用哪种方法,最终都需要满足三个约束:
- 块不能超过下游模型可以安全处理的长度;
- 块应尽量保留最小完整语义;
- 块必须能够追溯到原文位置和所属结构。
2. 固定大小分块
固定大小分块只依据长度切分文本,是最容易实现和预测的方法。
2.1 实现逻辑
先确定长度单位:
- 字符:计算简单,但字符数与模型 Token 数没有固定换算关系;
- Token:更接近模型输入限制,但必须使用与下游模型相匹配的 Token 计算方式。
设块大小为 W,相邻块重叠长度为 O,则步长为:
1 | S = W - O |
分块过程如下:
- 从位置
0读取最多W个单位; - 生成第一个块,并记录起止位置;
- 向后移动
S个单位; - 重复读取,直到文档结束;
- 删除空块,并根据需要合并过短的尾块。
例如 W=200、O=40 时,各块覆盖范围依次为:
1 | [0, 200) |
这里的数字只用于说明算法,不代表推荐参数。
2.2 关键实现细节
固定大小并不等于任意位置硬切。工程上通常还需要处理:
- 若边界落在句子中间,可在限定范围内向前寻找句末;
- 代码、URL、表格单元格等不可随意断开的内容应受保护;
- 重叠区域必须按原文位置截取,避免重复拼接造成字符缺失或顺序错误;
- 最后一个块过短时,可以并入前块,但合并后仍要检查长度上限;
- 空白归一化不能破坏原文偏移映射。
一旦加入“优先寻找句末”等规则,它就不再是完全严格的等长切分,而是“固定预算 + 轻量边界修正”。
2.3 优点、缺点与适用场景
优点:
- 实现简单,时间复杂度接近线性;
- 块大小稳定,容易估算索引数量和成本;
- 不依赖文档格式和外部模型;
- 适合作为其他方法的长度兜底。
缺点:
- 不知道主题和结构,容易切断句子、条款或步骤;
- 同一主题可能被拆到多个块,不同主题也可能被装入同一块;
- 增加重叠只能降低边界损失,不能真正识别语义边界。
它适合结构较弱、规模较大、需要稳定吞吐的文本,也适合作为所有复杂分块方法的最终长度保护机制。
3. 语义分块
语义分块依据相邻内容之间的语义变化决定边界。它关注的不是“已经写了多少 Token”,而是“主题是否发生了明显转换”。
3.1 实现逻辑
一个常见实现包含以下步骤:
- 将文档切成句子或较小的基础单元;
- 为每个基础单元生成向量;
- 计算相邻单元之间的距离;
- 在距离明显增大的位置设置候选断点;
- 合并断点之间的连续单元;
- 对过短块进行相邻合并,对过长块使用长度兜底。
若第 i 个基础单元向量为 v_i,可以用余弦距离描述相邻语义变化:
1 | d_i = 1 - cosine_similarity(v_i, v_(i+1)) |
当 d_i 超过阈值时,就把 i 与 i+1 之间标记为候选边界。
3.2 阈值如何确定
固定相似度阈值很容易受语言、Embedding 模型和文档类型影响。更稳妥的做法是根据单篇文档或同类文档的距离分布动态判断,例如:
- 选择距离分布中较高的分位点;
- 识别距离曲线的局部峰值;
- 比较当前距离与附近窗口的平均距离;
- 同时设置最小块和最大块长度,约束断点结果。
阈值不是越敏感越好。阈值过低会产生大量碎片,过高则会把多个主题合并。
3.3 关键实现细节
- 基础单元质量:句子切错后,向量距离再准确也无法恢复正确边界;
- 上下文向量:可以为单句生成向量,也可以把前后句加入向量输入,降低短句歧义;
- 相邻约束:通常只比较原文中相邻单元,避免改变文档顺序;
- 长度约束:语义连续不代表可以无限合并,仍需设置硬上限;
- 批量计算:Embedding 应批量生成,并缓存文本哈希与向量;
- 版本记录:更换模型或阈值后,边界可能整体变化,需要记录配置版本。
3.4 优点、缺点与适用场景
语义分块能够发现没有明确标题或空行的隐含主题变化,适合访谈、会议纪要和长篇叙述文本。
它的主要问题是成本更高、阈值不稳定,而且“向量距离突然增大”只是主题变化的代理信号,不保证边界一定符合具体问答任务。对于法规、表格等结构明确的文档,语义信号通常不应覆盖可靠的结构边界。
4. 递归分块
递归分块使用一组从强到弱排列的分隔符,优先在更自然的边界切分,只有块仍然过长时才使用更细粒度的规则。
4.1 实现逻辑
以普通文本为例,分隔符优先级可以表示为:
1 | 章节空行 → 段落换行 → 句末标点 → 子句标点 → 空格 → 字符 |
核心过程如下:
1 | split(text, level): |
递归结束必须有明确兜底。最后一级通常按字符或 Token 窗口切分,否则遇到没有空格和标点的长字符串时可能无法终止。
4.2 分隔与合并
递归分块不只是“依次调用多个分隔符”。一个可靠实现需要同时处理:
- 分隔符是否保留:句号、换行和标题标记可以保留在前块、后块,或单独作为元数据;
- 短片段合并:切出的片段应按原文顺序贪心合并,但合并结果不能超过上限;
- 超长片段递归:只对当前层无法控制的片段进入下一层;
- 重叠生成:应从已完成块的尾部选择完整基础单元,而不是再次随意截取;
- 偏移维护:分隔符的删除或前后归属不能破坏原文位置。
4.3 优点、缺点与适用场景
递归分块不需要模型,性能稳定,又比纯固定大小更能保留段落和句子边界,适合作为通用文本的基础方案。
它仍然不理解语义。若两个主题之间没有明显格式边界,它可能错误合并;若文本充满短换行,它又可能产生过多候选片段。因此,分隔符顺序必须根据语言和文档类型配置,不能把同一套字符列表用于所有内容。
5. 结构分块
结构分块依据文档显式结构确定边界。结构可能来自标记语法,也可能来自版面分析和领域规则。
5.1 实现逻辑
结构分块通常先把文档转换成树或有序节点序列:
1 | 文档 |
然后执行:
- 识别节点类型、层级和原文位置;
- 根据业务语义选择不可拆分的原子节点;
- 从叶子节点向上组合内容;
- 合并时保留祖先标题和必要元数据;
- 节点超过长度上限时,在节点内部调用递归或固定大小分块;
- 输出块内容、结构路径、节点类型和原文偏移。
结构分块的关键不是“看到换行就切”,而是理解换行代表标题、列表项、表格行还是普通换行。
5.2 不同结构的原子单元
| 文档类型 | 常见原子单元 | 应保留的上下文 |
|---|---|---|
| Markdown/技术文档 | 标题节点、段落、代码块 | 完整标题路径、语言类型 |
| 法规/合同 | 条、款、项 | 法规名、章、节、适用范围 |
| FAQ | 一组问题与答案 | 分类、产品、版本 |
| 表格 | 表头与一行或一条业务记录 | 列名、主键、单位 |
| 演示文稿 | 单页或连续主题页 | 文件名、页码、章节 |
| 邮件 | 单封邮件或线程消息 | 主题、发件人、时间、引用关系 |
所谓“不可拆分”也不是绝对规则。一个超长代码块或表格行仍可能超过模型限制,此时必须选择截断、摘要、字段投影或更细粒度拆分,并明确记录数据损失。
5.3 父子分块是结构分块的增强
父块保存章节或较大的完整语义,子块保存便于精确匹配的片段。它通常按以下方式工作:
- 先通过结构分块生成父块;
- 在每个父块内部生成更小的子块;
- 保存
parent_id、标题路径和版本; - 用子块参与匹配;
- 命中后根据上下文预算回填父块或父块片段;
- 对多个子块指向同一父块的结果进行去重。
父子关系解决的是“精确匹配需要小块,完整理解需要大块”的矛盾,但它不能修复质量差的父块。若一个父块已经混合多个主题,回填只会放大噪声。
5.4 优点、缺点与适用场景
结构分块最适合标题、条款、问答、行列等边界可靠的文档。它能够保留语义成立所需的结构上下文,也便于按章节、页码和字段过滤。
它高度依赖解析质量和领域规则。扫描件标题识别错误、表格跨页、层级编号不规范时,错误结构会被稳定地传播到所有块中。因此必须抽查结构树,而不能只检查最终块大小。
6. LLM 分块
LLM 分块让大模型根据主题、论点、任务目标和篇章结构判断边界。它可以处理规则难以描述的复杂文本,但不能把模型输出直接当作可信结果。
6.1 实现逻辑
一种可验证的实现流程是:
- 为原文中的句子或段落分配稳定编号;
- 按上下文窗口将编号后的内容送入模型;
- 要求模型只返回边界编号、主题标签和理由;
- 将边界编号映射回原文偏移;
- 合并相邻窗口产生的重复或冲突边界;
- 检查是否存在内容遗漏、顺序改变和长度超限;
- 对不合法结果重试或降级到确定性分块方法。
建议让模型返回结构化结果,而不是重写原文。例如:
1 | { |
边界 4 表示在第 4 个基础单元之后切分。最终块仍从原文截取,避免模型改写、遗漏或虚构内容。
6.2 长文档的窗口处理
长文档无法一次交给模型时,需要分窗口判断:
- 相邻窗口保留少量重叠基础单元;
- 只接受窗口中部的边界,边缘边界交给相邻窗口确认;
- 使用稳定编号对重复结果去重;
- 对冲突边界按置信度、投票或确定性规则裁决;
- 最后进行一次全局长度和覆盖率检查。
这里的窗口重叠用于保证边界判断拥有上下文,不等同于最终索引块之间必须重叠。
6.3 关键风险
- 相同输入可能产生不同边界,难以完全复现;
- 模型可能遗漏编号、改变顺序或返回非法格式;
- 提示词、模型和采样参数变化会导致索引整体漂移;
- 处理成本和延迟明显高于规则方法;
- 模型理解的“主题完整”未必与真实检索问题一致。
因此,生产实现应固定模型与提示词版本,保存原文偏移和原始响应,设置结构校验、重试上限及确定性降级路径。
6.4 适用场景
LLM 分块适合结构混乱但主题关系复杂、人工规则难以覆盖、且分块质量收益足以抵消成本的文档。对于格式稳定或规模极大的数据,优先使用结构或递归分块通常更容易控制。
7. 五类方法对比与选型
下表描述的是常见工程特征,不是对所有数据集都成立的基准测试结论:
| 对比项 | 固定大小 | 语义 | 递归 | 结构 | LLM |
|---|---|---|---|---|---|
| 边界质量 | 低到中 | 中到高 | 中 | 结构可靠时高 | 潜力高但不稳定 |
| 计算成本 | 低 | 中 | 低 | 中 | 高 |
| 结果稳定性 | 高 | 中 | 高 | 高 | 低到中 |
| 对解析质量依赖 | 低 | 中 | 中 | 高 | 中到高 |
| 配置难点 | 长度与重叠 | 模型与阈值 | 分隔符优先级 | 结构规则 | 提示词与校验 |
| 典型用途 | 兜底、弱结构文本 | 隐含主题变化 | 通用文本 | 规范文档 | 复杂非规范文本 |
可以按以下顺序选择:
- 文档存在可靠标题、条款、问答或行列结构时,优先结构分块;
- 没有可靠结构,但段落和句子边界基本可信时,优先递归分块;
- 主题转换隐含、格式边界较弱时,再考虑语义分块;
- 规则和向量都难以表达边界,且成本允许时,考虑 LLM 分块;
- 无论主方法是什么,都使用固定大小机制处理最终超长块。
这不是绝对优先级。最终选择应由真实查询和评测结果决定,而不是由算法复杂度决定。
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 常见误区
- 把字符数当成 Token 数:不同语言、代码和模型的换算关系不同;
- 只调块大小,不检查解析结果:很多问题实际来自 OCR 和阅读顺序;
- 认为语义方法不需要长度上限:主题连续的内容仍可能超过模型限制;
- 认为结构边界永远正确:解析错误会直接污染结构分块;
- 用重叠掩盖错误边界:重叠增加成本,但不会修复主题混合;
- 让 LLM 直接重写块内容:这会破坏原文可追溯性;
- 只看块数量和平均长度:这些指标不能代表检索和回答质量;
- 一次调多个变量:无法判断收益来自分块、Embedding 还是重排变化。
11. 总结
五类分块方法解决的是同一个问题:如何为长文档找到可检索、可理解且可控制成本的边界。
- 固定大小分块用长度决定边界,简单稳定,适合作为兜底;
- 语义分块用相邻内容的语义变化决定边界,适合隐含主题转换;
- 递归分块按分隔符优先级逐层细化,是通用文本的可靠基础方案;
- 结构分块利用标题、条款和行列等显式结构,适合规范文档;
- LLM 分块根据主题和任务判断边界,能力强,但成本和不确定性最高。
实际系统通常不是五选一,而是选择一种主要边界方法,再组合长度兜底、标题补全、相邻窗口或父子回填。最终效果必须通过真实问题集验证,不能仅凭块预览或算法名称判断。