分块策略与 Contextual Embeddings

Chunking strategies and Contextual embeddings

检索增强生成(RAG)流水线需要将长文档切分为文本块(Chunk),生成对应的 embedding 并存入向量数据库,以支持语义搜索。分块方式直接影响检索质量:文本块过大,可能超出 embedding 模型的输入长度限制,或混入无关内容;文本块过小,则容易丢失准确检索所需的上下文。若仅按固定字符或 token 长度机械切分,可能会剥离关键背景信息,导致系统无法召回相关数据。

例如,某段被切分出来的文本块仅有一句:“这个训练营涵盖端到端 AI 工程工作流程。”单看这句话,无法得知它指的是哪个训练营、在什么时间举办。一旦缺失这些上下文,当用户查询“SwirlAI 七月训练营涵盖什么内容?”时,检索器很可能完全漏掉该文本块。

本课介绍如何在分块与向量化阶段保留上下文与完整语义,以提高检索相关性:首先对比固定长度、基于结构和基于语义的分块策略;接着介绍通过补充文档背景改进文本块表征的 Contextual Embeddings;最后介绍利用长上下文编码器先对全文编码、再池化生成分块向量的 Late Chunking 方法。

分块为什么影响 RAG?

理解高级方法前,需要回顾分块的原因。Embedding 模型具有固定上下文窗口,例如 512 或 1024 个 token,无法一次编码超过长度上限的文档。超出限制的文本需要截断或分块;直接截断可能丢失重要信息。因此,我们把文档分成符合模型输入长度的文本块,分别生成 embedding,保存到向量数据库供后续检索。这样,大型文档的各个部分都能在向量索引中得到表示。

其次,分块方式决定了搜索相关性的上限。在检索阶段,每个文本块都是独立的信息单元:分块过小或缺乏上下文时,文本块难以独立表达完整含义,相关查询也不易命中;分块过大或混杂多个无关主题时,生成的 embedding 会退化为多主题的平均语义,拉低匹配精确度。因此,理想的分块策略需要在满足模型长度限制、保障处理效率的同时,尽可能让每个文本块具备独立、完整的语义。

分块策略

常见的分块策略可分为固定长度分块、结构分块和语义分块三类,在实际工程中常组合使用。

1. 固定大小分块

固定窗口分块根据目标 embedding 模型的输入 token 上限设定切分长度。课程贯通示例以 600 字符窗口和 80 字符重叠演示分块 ID 生成与更新逻辑;在完整应用中,还需使用真实的 tokenizer 检查 token 长度(字符数与 token 数需分别计量),并注意保留章节标题与表格表头。

固定长度可能在任意位置切断文档,例如句子中间或一个概念尚未解释完整的位置。重要上下文便可能遗漏,或分布到不同文本块。如果知识无法在一个文本块中独立表达,LLM 可能难以理解,检索也可能失败。部分实现采用文本块重叠,例如把前一块末尾几句话重复放到后一块开头,保留边界上下文。重叠会增加重复内容,也能改善连续性。文本块大小、边界与重叠范围都会影响检索效果,应认真调节。固定大小适合建立简单基线,但可能忽略文档逻辑结构。

下图展示了固定长度分块及重叠机制的作用:

图 1 · 固定长度分块与重叠
对同一文档采用固定长度分块时,重叠区域可以保留跨边界的上下文。

假设图中蓝色框代表包含完整相关上下文的文本区域:

  • 在无重叠的固定大小分块中,前两处完整上下文均被从中间切断,没有任何一个文本块完整包含它们。
  • 引入重叠区域后,文本块 2 和文本块 4 分别完整覆盖了这两处上下文。

这正是文本块重叠希望解决的问题。

2. 结构分块:感知内容结构

结构分块(又称内容感知分块)依据文档自身的自然边界(如句子、段落、章节、标题、列表项等)决定切分位置,例如将普通文章按段落切分、将学术论文按章节切分,使每个文本块对应一个语义连贯的内容单元。由于许多 embedding 模型本身就是围绕句子或段落表征进行训练和优化的,按自然结构切分有助于保留原始语义。

常见实现方式包括:

  • 按句子分块:使用 NLTK(sent_tokenize)或 spaCy 等 NLP 库的句子分割器识别句子边界(通常比单纯按换行符或句号切分更准确),再在不超过 token 上限的前提下将相邻句子组合为文本块,确保切分点始终落在句末。
  • 按段落或章节分块:利用 Markdown、HTML、PDF 等文档中明确的段落或标题标记切分(例如在 HTML 的 <p> 标签或 Markdown 的 # 标题处断开),或将代码与 JSON 按函数定义、顶层大括号等逻辑单元拆分。一个实用的衡量标准是:如果人类读者无需翻看前后文就能读懂该文本块,模型通常也更容易理解。
  • 混合递归分块:以 LangChain 的 RecursiveCharacterTextSplitter 为代表,将结构边界与长度上限结合起来。它先尝试按较大的结构分隔符(如段落间的 \n\n)切分;若切出的文本块仍超出长度上限,再递归降级使用 \n 或空格继续细分,直到满足尺寸要求,从而在控制分块大小的同时尽量保留自然结构。

当文档格式规范、逻辑层次清晰时,结构分块更容易把代词与其指代对象、结论与其解释依据保留在同一块内。不过,实际处理时仍需应对极端长度:超出上限的长段落(如超过 1000 个 token)仍需二次拆分,而过短的碎片化小节则可与相邻内容合并。

下图对比了不同粒度的结构分块效果:

图 2 · 按文段、章节或句子分块
在相同文档上,按文段、章节和句子确定边界,会产生不同的分块范围。

同样以蓝色框代表完整相关上下文:

  • 按段落(文段)分块效果较好,每个文本块恰好包含一段完整的相关内容。
  • 按章节分块虽未在上下文中间截断,但第二个文本块混入了多个主题,会在一定程度上稀释 embedding 中的核心语义信号。
  • 此例中,按句子分块效果较差。

在该示例中,按单句分块因粒度过细而效果较差(在科学论文等语料中,按段落或章节分块通常表现更优)。

3. 语义分块

语义分块跳出了字符计数与固定排版标记的限制,直接根据文本的语义变化决定切分边界:将讨论同一主题或概念的连续句子归入同一文本块,当检测到主题发生转移时则开启新块。

典型实现流程如下:

  1. 生成句子级 embedding:先将文档拆分为句子,并为每个句子计算向量表示。
  2. 计算相邻句相似度:计算相邻句子向量之间的语义相似度(如余弦相似度)。
  3. 识别主题转换边界:当相邻句子的相似度低于设定阈值时,视为主题发生切换,在此处设置分块边界。
  4. 合并相近上下文:连续讨论同一主题的句子组合在一起,直到检测到主题变化。

语义分块生成的文本块长度并不固定,但每块内容更可能聚焦于单一主题。由于用户查询通常针对特定主题或事实,按主题内聚分块有助于提高相关内容被检索命中的概率。

沿用前面的示例:

图 3 · 主题变化与语义分块
主题变化位置帮助确定语义分块边界,右侧显示组合后的语义内容块。

在图 3 左侧文档中,绿色标记代表语义相近的句子,红色标记代表主题切换处。在理想情况下,语义分块能够完整框定相关的上下文区间。

实现时需要认真选择相似度阈值,防止文本块过小或过大。通常仍需设置最小和最大大小,例如轻微主题变化时也不生成少于 X 句的块,超过 Y 个 token 时则强制切分。语义分块也可以加入重叠,例如边界句同时关联两个主题时,将其保留在两块中。语义相似度并不能完美识别逻辑边界:主题可能逐渐变化,也可能使用不同措辞描述同一概念,导致 embedding 相似度波动。

语义波动与计算开销:由于主题可能是渐进过渡的,或者同一概念采用了不同措辞,单纯依赖句子向量相似度未必总能精准捕捉逻辑边界。此外,逐句计算 embedding(或调用 LLM 判断边界)的计算开销较高、参数调优较复杂。因此,许多应用会优先采用结构分块或固定长度分块,再通过附加章节标题、关键词等元数据来辅助检索;而在处理超长文档或内部主题跨度极大的语料时,语义分块则可能显著提升检索相关性(具体收益应始终通过实验评测确认)。

Contextual Embeddings 与 Contextual Retrieval

Contextual Embeddings(上下文嵌入)通过在生成 embedding 之前为每个文本块前置补充一段简短的全局背景说明,使生成的向量同时编码局部内容与全文语境。Anthropic 于 2024 年 9 月 19 日发布的 Contextual Retrieval 即以这一思路为核心,具体步骤为:

具体方法是在每个文本块前添加一段针对该块的简短解释或摘要,再计算 embedding:

  1. 生成一段简短说明,补全该文本块中隐含的人物、事物、时间、地点等全文背景。
  2. 将这段背景说明拼接到原始文本块开头。
  3. 将拼接增强后的文本送入 embedding 模型计算向量。

生成的向量既表示文本块本身,也包含该块在整篇文档中的位置与意义。

Contextual Retrieval 使用一部分 embedding 表示能力编码全局上下文,减少因缺少关键词或指代信息而漏检相关文本块的情况。

Anthropic 的做法是调用 LLM,将整篇文档与当前待处理的文本块一同放入提示词,让模型生成简明扼要的定位说明。提示词模板如下:

文本
<document>
{{WHOLE_DOCUMENT}}
</document>

Here is the chunk we want to situate within the whole document

<chunk>
{{CHUNK_CONTENT}}
</chunk>

Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else.

知识库中的每个文本块都执行这一过程。

对知识库中的每个文本块均执行上述处理。为控制对每个分块重复传入全文带来的调用成本,可利用 Prompt Caching(提示词缓存):将整篇文档固定在提示词最前端,借助服务商的 KV Cache 机制复用前缀计算结果,从而大幅降低重复前缀 token 的计费成本。

下图展示了 Contextual Retrieval 的离线预处理与在线检索全流程:

图 4 · 上下文检索的预处理与查询阶段
预处理为每个文本块补充文档上下文;查询同时使用向量检索和词项检索,经排序融合与重排序后向LLM提供相关内容。

预处理:

  1. 按选定策略将每篇文档分块。
  2. 针对每个文本块,将它与整篇文档一起加入提示词。
  3. 加入指令,要求说明该块在全文中的上下文,并生成简短描述;把提示词发送给所选 LLM。
  4. 将生成的上下文与对应文本块组合。
  5. 为加入上下文的分块建立 BM25 词法索引。图中 TF-IDF 表示词频与逆文档频率信息;BM25 在此基础上还考虑词频饱和与文档长度。
  6. 通过基于语言模型的 embedding 模型处理数据。
  7. 将第 5、6 步产生的数据存入支持高效搜索的数据库。

检索:

  1. 使用用户查询检索相关上下文:通过 ANN 搜索进行语义检索,通过 BM25 词法索引检索词项匹配的结果。
  2. 利用 Rank Fusion 技术合并并去重结果,取得前 N 项。
  3. 重排序这些结果,缩小为前 K 项。
  4. 将第 10 步结果与用户查询一同提供给 LLM,生成最终答案。

除了语义向量检索,Anthropic 还建议使用相同的上下文增强文本块构建 Contextual BM25 词法索引:即便原始分块中未出现准确的产品名或专有短语,前置补充的背景说明也能帮助 BM25 命中用户查询中的关键词。根据 Anthropic 公布的实验数据,单独使用 Contextual Embeddings 可将检索失败率降低约 35%;结合 Contextual BM25 后,相关文本块的漏检率降低 49%;在此基础上再使用交叉注意力(Cross-Attention)重排序模型对前 150 个候选结果精排,总体检索失败率相对基线下降了 67%。该方法以更高的离线索引构建复杂度与略微增加的存储体积为代价,换取了检索召回率的显著提升。

通过补充原文上下文,Contextual Embeddings 可以提高检索相关性。代价是索引过程更复杂,每项索引因增加文本而略大;收益是减少遗漏答案。

Late Chunking(延迟分块)

无论是常规分块还是 Contextual Embeddings,都遵循“先切分文本块、再编码向量”的顺序。而由 Jina AI 与 Weaviate 的 Günther 等研究者于 2024 年 9 月 7 日提出的 Late Chunking(参见论文 Late Chunking: Contextual Chunk Embeddings Using Long-Context Embedding Models)则反转了这一顺序:先用长上下文模型对整篇文档进行编码,再在 token 级向量表示上切分并池化生成各个文本块的向量。

具体执行步骤如下:

  1. 全文编码:选用支持长上下文的 embedding 模型,将整篇文档(或很大一部分,例如 6000 个 token)作为单次输入送入模型前向计算,使每个输出 token 的隐藏层表示都融合全文上下文信息。
  2. 一次输入全文,例如 6000 个 token。模型处理完整序列,使每个 token 的表示都受到全文上下文影响。
  3. 编码后分块与池化:在获得包含全文语境的 token 级表示序列后,按照预设或动态确定的文本块边界,对每个文本块覆盖范围内的 token 向量执行聚合(通常采用均值池化 Mean Pooling),得到该文本块的最终 embedding。

这些向量隐含全文上下文,因为编码阶段模型已经看到文本块范围之外的内容。Günther 等来自 Jina AI 和 Weaviate 的研究者提出这一方法,并报告其在多项检索任务上优于普通分块。相关论文首次提交于 2024 年 9 月 7 日。

论文:Late Chunking: Contextual Chunk Embeddings Using Long-Context Embedding Models。

模型接口要求:Late Chunking 不依赖 LLM 生成文本摘要,而是直接依赖能够处理长输入、且支持输出 token 级中间表示 的 embedding 模型。需要注意,像 OpenAI text-embedding-3-small 这类商用 API 虽然支持约 8192 个 token 的长输入,但每次调用仅返回单个池化后的句子/文档级向量,不暴露底层的 token 级向量,因此无法直接用于实现 Late Chunking。工程上通常需要自行部署开源长上下文 Transformer 模型(获取最后一层每个 token 的表示后再按分块区间池化),或选用原生支持该能力的模型服务商。

计算开销与取舍:Contextual Retrieval 需要为每个分块发起额外的 LLM 提示词调用;Late Chunking 则只需对每篇文档执行一次长上下文编码前向计算(一次编码全文可能比分别编码 10 个独立文本块更快),但代价是需要部署参数量更大或推理更重的长上下文编码器。

原论文总结的核心优势:

  • 上下文完整性:分块向量源自全文编码,自然保留了跨段落的别名、代词共指与主题关联。
  • 检索准确性:富含全局语境的 embedding 有助于在相似度搜索中召回未直接出现查询词但实质相关的片段,减少漏检。
  • 充分利用长上下文模型:发挥 8K 或 16K 窗口编码器在建索引阶段的长文本建模优势。

前两项也是本课各方法共同希望改进的指标,关键是比较 Late Chunking 相对于其他方法的改善程度。此外,它需要长上下文 embedding 模型,这些模型可能更大或更慢。

在 RAG 中实现 Late Chunking,仍涉及传统流水线中的步骤,只是顺序不同:

  1. 全文编码:将整篇文档或较大部分输入长上下文 embedding 模型,产生 token 表示序列,形成包含全文上下文的表示。
  2. 编码后分块:确定边界,可以预先计算,也可以在取得表示后决定。对各块范围内的 token 向量进行平均等聚合,得到包含块外上下文信息的文本块向量。
  3. 存储与检索:像通常一样把增强向量存入向量数据库。查询时通过相似度搜索寻找相关文本块,无需额外修改查询过程。其余步骤,包括取得前 K 块并提供给 LLM,保持一致。

在实际项目中,通常会在 Contextual Retrieval 与 Late Chunking 之间择一使用:若希望直接复用现有的商用 API embedding 模型,且具备支持 Prompt Caching 的 LLM,Contextual Retrieval 更容易落地;若具备自托管长上下文开源 embedding 模型的条件,则可采用 Late Chunking 省去离线 LLM 摘要生成环节。

准备采用上下文增强方法时,可以选取一组查询与文档,分别测量普通分块、Contextual Embeddings 和 Late Chunking 的检索召回率或答案准确率,比较改善程度与所需投入。

常用分块工具与迭代实践步骤

  • LangChain:提供 RecursiveCharacterTextSplitter 等现成分割器,可按段落、句子和字符边界依次回退切分,并内置 PDF、HTML 等格式解析器,便于快速编排固定长度或结构感知的分块流水线。
  • LlamaIndex:围绕大规模文档索引与高级检索设计,同样提供按标题、句子等结构切分的丰富工具。
  • Hugging Face Transformers 与 Sentence-Transformers:提供 all-MiniLM-L6-v2 等通用短文本模型及金融、法律等垂直领域模型,并配套句子切分与长文本滑动窗口工具。其中 Hugging Face 的 tokenizers 库非常适合执行基于 token 的精确分块,能夠准确计数并在 token 边界切分,比单纯按字符切分更稳健(尤其适合处理多字节字符)。

评估分块策略时,最好准备具有真实相关性标注的查询—文档对。即使只有少量查询,只要知道应该命中哪篇文档或哪个章节,也能自动比较策略:分别建立索引,执行相同查询,观察哪种方法更经常检索到包含正确答案的文段。

改进 RAG 流水线时,可以采取以下步骤:

  1. 建立简单基线:先用固定长度分块(如每块 200~500 个 token,设置 10%~15% 重叠)跑通基线,并准备一组带真实相关性标注的“查询—文档/章节对”用于自动化评测。
  2. 引入结构感知:保持句子完整、令标题与紧随其后的正文留在同一块内,在评测集上检验召回率是否提升。
  3. 按需引入上下文增强:当错误分析显示漏检主要源于分块脱离全文语境或指代不明时,若使用 API embedding 可尝试 Contextual Embeddings,若具备长上下文 token 级编码器则可尝试 Late Chunking,对比各自的检索提升幅度与工程投入。
  4. 工具化快速验证:借助 LangChain 分割器与 Hugging Face 模型快速切换不同配置,通过对照实验数据选定最终方案。

采用这些分块与 embedding 策略,可以提高检索相关性,进而改善 RAG 系统的准确性与可靠性。

以文档结构分块

在课程主线项目中,飞书 docx 文档的标题、段落、block_id 与表格单元格提供了天然的结构分块边界;多维表格(bitable)则按记录 ID(record_id)切分并保留字段名。简短的代码示例采用字符窗口演示,完整工程实践则应校验目标模型的实际 token 限制并保留表格表头。再次强调,实现 Late Chunking 必须具备获取 token 级向量表示的能力,不能仅凭单向量 API 标称的最大输入长度就推断其支持 Late Chunking。

练习:记录本课所用配置、输入、预期结果与失败分支,并用脱敏数据复现。

搜索课程

正在加载课程目录…