检索增强生成(RAG)流水线需要将文档分为文本块,为其生成 embedding 并保存,用于语义搜索。分块方式十分重要:文本块过大,可能超出 embedding 模型的限制,或包含无关内容;文本块过小,则可能丢失正确检索所需的上下文。简单按长度分块,可能在编码信息时移除上下文,使系统无法检索到相关数据。
例如,某个文本块写着:“这个训练营涵盖端到端 AI 工程工作流程。”单独看这句话,无法确定是哪个训练营、什么时间。缺少这些上下文,查询“SwirlAI 七月训练营涵盖什么内容?”时,可能完全检索不到该文本块。本课介绍保留文本块上下文与含义的高级策略,以提高检索相关性。
课程首先介绍分块策略,包括固定长度、基于结构和基于语义的分块;随后讨论 Contextual Embeddings,通过补充上下文改进文本块表示;最后介绍 Late Chunking,利用长上下文模型对整篇文档进行编码,再生成文本块向量,使各文本块的 embedding 保留完整上下文。
分块为什么影响 RAG?
理解高级方法前,需要回顾分块的原因。Embedding 模型具有固定上下文窗口,例如 512 或 1024 个 token,无法一次编码超过长度上限的文档。超出限制的文本需要截断或分块;直接截断可能丢失重要信息。因此,我们把文档分成符合模型输入长度的文本块,分别生成 embedding,保存到向量数据库供后续检索。这样,大型文档的各个部分都能在向量索引中得到表示。
分块还影响搜索相关性。检索时,每个文本块都是独立的信息单元。过小或缺少上下文的文本块可能无法独立表达意义,相关查询也难以找到它。文本块过大或混合无关主题时,embedding 会代表其中内容的平均语义,降低精确度。检索文本块的质量决定了 RAG 性能能够达到的上限。分块需要兼顾大小与语义完整性:满足模型限制、保持效率,同时让文本块能够独立理解。下面介绍比“每 N 个 token 分一次”更丰富的策略。
分块策略
分块策略大致包括固定长度、结构和语义三类,实际应用中常结合使用。
固定大小分块
固定大小分块是最直接的方法:设定目标大小,通常按 token 或字符计数,然后切分文本。可以采用模型最大上下文长度,例如 512、1024 个 token,也可以采用其中一部分,确保文本块不超限。本课示例使用上下文长度为 8192 的 OpenAI text-embedding-3-small。例如,以 1000 个 token 为目标,将长文章分为连续的 1000-token 文本块。这种方法容易实现,能使文本块符合模型长度要求,流水线逻辑也较简单,适合作为许多应用的起点。
固定长度可能在任意位置切断文档,例如句子中间或一个概念尚未解释完整的位置。重要上下文便可能遗漏,或分布到不同文本块。如果知识无法在一个文本块中独立表达,LLM 可能难以理解,检索也可能失败。部分实现采用文本块重叠,例如把前一块末尾几句话重复放到后一块开头,保留边界上下文。重叠会增加重复内容,也能改善连续性。文本块大小、边界与重叠范围都会影响检索效果,应认真调节。固定大小适合建立简单基线,但可能忽略文档逻辑结构。
下图展示这种分块策略:
假设蓝色框标记包含完整相关上下文的文本区域:
- 无重叠的固定大小分块中,前两处完整上下文都在中间被切断,没有任何一个文本块将其完整包含。
- 加入重叠后,文本块 2 和 4 分别包含了完整上下文。
这正是文本块重叠希望解决的问题。
结构分块:感知内容结构
结构分块,也称内容感知分块,依据文档结构决定切分位置。它尊重句子、文段、章节、标题、列表项等自然边界,使每个文本块对应语义连贯的内容。例如,文章按文段分块,学术论文按章节分块,让文本块对应有意义的内容单元。许多 embedding 模型围绕句子或文段表示进行训练和优化,采用这些单元有助于保留含义。
常见方法包括:
- 按句子分块:使用 NLTK、spaCy 等 NLP 库识别句子,再按 token 上限组合句子,让边界位于句末。spaCy 的句子分割器或 NLTK 的
sent_tokenize,通常比直接依据换行或句号切分更加准确。 - 按文段或章节分块:Markdown、HTML、PDF 等文档具有明确文段、章节或分节时,可以利用这些结构。例如,在 HTML 的
<p>标签或 Markdown 的#标题处切分,使相关内容保持在一起。代码或 JSON 可以按函数定义、顶层大括号等逻辑单元划分。目标是让每块成为连续且可独立理解的内容。一个实用判断是:人类无需周围文本就能理解这一块,模型通常也更容易理解。 - 混合递归分块:LangChain 的
RecursiveCharacterTextSplitter等工具结合结构与固定长度。它先尝试较大的分隔符,例如文段间的\n\n;如果文本块仍过大,再递归使用\n或空格等继续划分,直到符合大小限制。这样可以尽量保留结构,必要时才使用更小单位,兼顾固定长度和结构分块的特点。
文档具有清楚格式或逻辑单元时,结构分块效果较好。它更容易让指代与所指内容、解释与被解释事实保持在一起,保留上下文。但仍需处理特别长的章节,必要时继续分块;过短章节则可与相邻内容合并。例如,按文段分块时,某段超过 1000 个 token,仍需分成更小的部分。这种混合策略控制文本块大小,同时尽量遵循内容边界。
下图展示几种结构分块方式:
仍以蓝色框表示完整相关上下文:
- 按文段分块效果较好,每块包含一段相关内容。
- 按章节分块也没有在上下文中间切断,但第二块涵盖多个主题,会在一定程度上减弱 embedding 中的相关信号。
- 此例中,按句子分块效果较差。
在科学论文中,按文段或章节分块通常表现较好。
语义分块
语义分块依据文本含义决定边界,超越单纯字符计数或格式规则。它把讨论相同主题或概念的句子、文段放在同一文本块中,主题变化时开始新块。
一种实现方式如下:
- 为句子生成 embedding:将文档分成句子,并为每句生成向量。
- 比较相邻句子:计算各句与相邻句子向量之间的语义相似度,例如余弦相似度。
- 识别主题变化:相似度低于阈值时,可能意味着主题变化,可以在此设置边界。语义相似的句子保留在同一块,相似度显著变化时开始新块。
- 合并相近上下文:连续讨论同一主题的句子组合在一起,直到检测到主题变化。
这种方法生成的文本块大小不固定:某个主题可能占据很长一段,也可能每隔几句就变化。优势在于每块更可能围绕同一主题保持一致,有利于检索相关性。用户问题通常针对特定主题或事实,因此按主题分块可提高相关内容被检索到的机会。
沿用前面的示例:
左侧文档中,绿色标记代表语义相近的句子,红色标记代表主题变化。在这一理想情况下,语义分块可以完整包含相关上下文。
实现时需要认真选择相似度阈值,防止文本块过小或过大。通常仍需设置最小和最大大小,例如轻微主题变化时也不生成少于 X 句的块,超过 Y 个 token 时则强制切分。语义分块也可以加入重叠,例如边界句同时关联两个主题时,将其保留在两块中。语义相似度并不能完美识别逻辑边界:主题可能逐渐变化,也可能使用不同措辞描述同一概念,导致 embedding 相似度波动。
语义分块可能需要为每句话生成 embedding,或调用 LLM,计算开销较高,也需要认真调整参数。许多应用可以采用结构或固定长度分块,再补充章节标题、关键词等语义元数据,辅助检索。如果文档特别长,或内部主题差异较大,语义分块可能显著提高检索相关性。始终通过测量判断效果。
Contextual Embeddings 与 Contextual Retrieval
即使分块方法较先进,文本块仍可能缺少全局上下文。前面的“这个训练营涵盖端到端 AI 工程工作流程”便是例子。即使保留相邻句子,长文档中说明“训练营”名称为 SwirlAI 的位置,也可能不在同一文本块中。Contextual Embeddings 在生成 embedding 前,为每块补充上下文,使向量包含更广范围的信息。Anthropic 于 2024 年 9 月 19 日介绍的 Contextual Retrieval,便以此为核心。
具体方法是在每个文本块前添加一段针对该块的简短解释或摘要,再计算 embedding:
- 生成简短说明,补充文本中隐含的人物、事物、时间、地点等上下文。
- 把说明附加到文本块。
- 将增强后的文本输入 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 的成本通常显著降低。支持 prompt caching 的大型模型提供方可以应用这种方法。
下图展示 Contextual Retrieval 的预处理与检索阶段:
预处理:
- 按选定策略将每篇文档分块。
- 针对每个文本块,将它与整篇文档一起加入提示词。
- 加入指令,要求说明该块在全文中的上下文,并生成简短描述;把提示词发送给所选 LLM。
- 将生成的上下文与对应文本块组合。
- 通过 TF-IDF 编码器处理数据。
- 通过基于语言模型的 embedding 模型处理数据。
- 将第 5、6 步产生的数据存入支持高效搜索的数据库。
检索:
- 使用用户查询检索相关上下文:通过 ANN 搜索进行语义检索,通过 TF-IDF 索引进行精确检索。
- 利用 Rank Fusion 技术合并并去重结果,取得前 N 项。
- 重排序这些结果,缩小为前 K 项。
- 将第 10 步结果与用户查询一同提供给 LLM,生成最终答案。
Contextual Retrieval 还包括传统关键词搜索。Anthropic 建议使用相同的增强文本块建立 Contextual BM25 索引。即使原始文本块没有准确名称或短语,新增上下文也可能让 BM25 找到用户查询中的关键词。结合上下文 embedding 的语义搜索和上下文文本的词汇搜索,可以兼顾语义匹配与精确关键词匹配。Anthropic 的实验发现,单独使用 Contextual Embeddings 可将检索失败减少约 35%;结合 Contextual BM25 后,相关文本块漏检减少 49%;再用 cross-attention 模型重排序前 150 个结果,总体检索失败率相对基线下降 67%。
通过补充原文上下文,Contextual Embeddings 可以提高检索相关性。代价是索引过程更复杂,每项索引因增加文本而略大;收益是减少遗漏答案。
Late Chunking
前面的方法都在预处理阶段分块,可能补充上下文,再生成 embedding。Late Chunking 改变了处理顺序:传统方式先分块,再分别编码;Late Chunking 先编码整篇文档,再在向量表示阶段生成各个文本块。
具体过程如下:
- 使用长上下文 embedding 模型,让整篇文档或很大一部分作为单次输入。
- 一次输入全文,例如 6000 个 token。模型处理完整序列,使每个 token 的表示都受到全文上下文影响。
- 获得含上下文的表示后,确定文本块边界,可以沿用原先的边界;再聚合每块中的 token 向量,通常采用 mean pooling,形成文本块向量。
这些向量隐含全文上下文,因为编码阶段模型已经看到文本块范围之外的内容。Günther 等来自 Jina AI 和 Weaviate 的研究者提出这一方法,并报告其在多项检索任务上优于普通分块。相关论文首次提交于 2024 年 9 月 7 日。
论文:Late Chunking: Contextual Chunk Embeddings Using Long-Context Embedding Models。
Late Chunking 与 Anthropic Contextual Retrieval 的实现不同。前者依靠能够直接处理长输入、并提供 token 级表示的 embedding 模型,无需 LLM 生成上下文摘要。OpenAI text-embedding-3-small 等 API 虽然支持约 8192 个 token 的输入,但每段输入只返回一个向量,因此没有中间 token 表示时,无法直接实现 Late Chunking。通常需要自行运行开源模型,或使用支持这一能力的提供方。也可以本地运行长上下文 Transformer,输入全文,取得最后一层每个 token 的表示,再按文本块范围聚合。
Contextual Retrieval 需要额外提示词调用;Late Chunking 可以对每篇文档执行一次模型前向计算。它可能较高效,一次编码全文可能比单独编码 10 个文本块更快;代价是需要更大、更强的模型。
原论文列出的主要优势包括:
- 上下文完整性:文本块向量源于全文编码,能够保留别名、共指和主题关联等语义关系。
- 检索准确性:丰富的 embedding 让相似度搜索更容易匹配真正相关的文本块,即使查询词没有直接出现在块中,也可能找到相关内容,减少漏检。
- 充分利用长上下文模型:支持 8K 或 16K token 的模型可以在建立索引时利用长上下文丰富 embedding。
前两项也是本课各方法共同希望改进的指标,关键是比较 Late Chunking 相对于其他方法的改善程度。此外,它需要长上下文 embedding 模型,这些模型可能更大或更慢。
在 RAG 中实现 Late Chunking,仍涉及传统流水线中的步骤,只是顺序不同:
- 全文编码:将整篇文档或较大部分输入长上下文 embedding 模型,产生 token 表示序列,形成包含全文上下文的表示。
- 编码后分块:确定边界,可以预先计算,也可以在取得表示后决定。对各块范围内的 token 向量进行平均等聚合,得到包含块外上下文信息的文本块向量。
- 存储与检索:像通常一样把增强向量存入向量数据库。查询时通过相似度搜索寻找相关文本块,无需额外修改查询过程。其余步骤,包括取得前 K 块并提供给 LLM,保持一致。
Late Chunking 可以作为 Contextual Retrieval 的替代选择。是否要同时采用?通常会选择其中一种。Contextual Retrieval 更容易结合现有 embedding 模型,无需运行新的 embedding 模型,只需能够生成摘要的 LLM,最好还支持方便的 prompt caching。Late Chunking 需要专门的 embedding 配置,此后便无需 LLM 生成上下文。两者以不同方式解决文本块之间上下文丢失的问题。
准备采用上下文增强方法时,可以选取一组查询与文档,分别测量普通分块、Contextual Embeddings 和 Late Chunking 的检索召回率或答案准确率,比较改善程度与所需投入。
常见分块工具包括:
- LangChain:提供文本切分、embedding 与查询工具。
RecursiveCharacterTextSplitter可以自动依次尝试文段、句子和字符边界;也集成 PDF、HTML 解析等功能。它适合编排分块流水线,为多种格式提供现成分割器,便于使用固定长度或内容感知分块。 - LlamaIndex:围绕大型文档索引和高级检索设计,提供按标题、句子等分块的工具,与 LangChain 类似。
- Hugging Face Transformers 与 Sentence-Transformers:为 embedding 和分块提供基础。可以取得
all-MiniLM-L6-v2等短文本模型,或金融、法律等领域模型。Sentence-Transformers 还提供句子切分或滑动窗口处理长文本的相关工具。Hugging Face 的 tokenizers 适合按 token 固定分块,可准确计数并在 token 边界切分,比直接按字符计数更稳健,也能考虑多字节字符等情况。
评估分块策略时,最好准备具有真实相关性标注的查询—文档对。即使只有少量查询,只要知道应该命中哪篇文档或哪个章节,也能自动比较策略:分别建立索引,执行相同查询,观察哪种方法更经常检索到包含正确答案的文段。
改进 RAG 流水线时,可以采取以下步骤:
- 用简单分块建立基线,例如每块约 200~500 个 token,重叠约 10%~15%。
- 加入结构处理,例如保持句子完整、标题与正文相邻,测量是否改善。
- 发现上下文缺失或文本块含义不明确时,尝试 Contextual Embeddings,尤其适合 API embedding;能够使用长上下文编码器时,也可尝试 Late Chunking,减少相应失败情况。
- 利用上述工具快速实现和迭代,例如替换 LangChain 分割器,用 Hugging Face 模型或 API 重新编码,然后比较结果。
采用这些分块与 embedding 策略,可以提高检索相关性,进而改善 RAG 系统的准确性与可靠性。