Deep Read

RAG 分块策略详解

分块(Chunking)是 RAG 离线建库的核心一步:把长文档切成大小合适、语义完整的小块,再去向量化、入库、检索。它看似简单,却几乎决定了整个系统的效果上限——本篇专门把"怎么分"讲透。

RAG 的整体流程、为什么要分块等背景,见 Naive RAG 概述Naive RAG 讲解RAG,全称 Retrieval-Augmented Generation(检索增强生成),是过去两年大模型应用落地里最重要的工程方案之一。 如果把大模型比作一个"很聪明但记忆固定的学生",那么 RAG 做的事情就是:先去外部资料里找答案,再带着资料回答问题。 而 Naive RAG,可以理解为最基础、最原始、也是最容易落地的一代 RAG 方案。它的结构并不复杂,但已经足够解决很多实际问题,比如企业知识库问答、内部文档检索、客服机器人、私有数据接入大模型等。 一、为什么需要 RAG 大语言模型能力很强,但存在三类核心局限。 1. 时效性有限 模型知识来自训练数据,而训练数据有截止时间。最新政策、财报、内部 SOP、个人知识库文档——这些如果没被放进训练数据,模型就无法直接回答。 2. 知识覆盖不完整 即使训练了海量语料,也不可能覆盖所有垂直领域和每家企业的内部资料。医疗、法律、金融等高门槛领域,以及内部文档、合同、产品手册、私有接口说明——模型可能"懂一点",但不够深、不够准。 3. 幻觉问题 大模型在不知道答案时,仍可能生成一个看起来很像答案的内容——语气自信,内;这里默认你已了解基本范式,直接聚焦分块策略本身。


一、为什么分块质量决定 RAG 上限

即使用最强的大模型、最精心的 Prompt,分块不当依然会让问答系统出现:

  • 上下文缺失、关键信息被切散;
  • 事实性错误、拼接不连贯;
  • 检索到无关片段,进而诱发幻觉或错误引用。

根因往往是同一个误区:按固定长度硬切,无视标题、段落、列表、表格等语义边界。一个定义、一个条件、一段因果,被拦腰截断后,向量化和检索都会失真。

因此,高质量分块要同时照顾三件事:

  • 贴合自然语义 / 结构边界,不在句子或语义中间硬切;
  • 设置适度重叠(overlap),缓解块与块之间的断裂;
  • 保留元数据(metadata),如章节路径、块类型,以支持可追溯性与重排。

一句话目标:在"上下文完整性"与"信息密度"之间取得动态平衡——块太大会引入噪声、稀释关键信息,块太小则缺乏上下文、难以理解语义。

下文按"基础 → 结构感知 → 语义/主题 → 高级 → 混合"由浅入深展开。为方便对照,基础策略统一使用下面这段示例文本:

text = (
    "自然语言处理(NLP),作为计算机科学、人工智能与语言学的交融之地,致力于赋予计算机解析和处理人类语言的能力。"
    "在这个领域,机器学习发挥着至关重要的作用。利用多样的算法,机器得以分析、领会乃至创造我们所理解的语言。"
    "从机器翻译到情感分析,从自动摘要到实体识别,NLP 的应用已遍布各个领域。随着深度学习技术的飞速进步,"
    "NLP 的精确度与效能均实现了巨大飞跃。如今,部分尖端的 NLP 系统甚至能够处理复杂的语言理解任务,"
    "如问答系统、语音识别和对话系统等。NLP 的研究推进不仅优化了人机交流,也对提升机器的自主性和智能水平起到了关键作用。"
)

二、基础分块

最常用、最容易落地的一类,按文本表层特征切分。

1. 固定长度分块

按预设字符数(或 Token 数)直接切割,不看文本结构。

  • 优点:实现简单、通用、速度快,存储与检索效率高。
  • 缺点:极易破坏语义边界,块大小也不好调。
  • 适用场景:结构很弱的纯文本,或作为初期基线方案。
  • 参数建议(中文)chunk_size 300–800 字,chunk_overlap 取 10%–20%。
def split_by_fixed_char_count(text, count):
    return [text[i:i + count] for i in range(0, len(text), count)]

chunks = split_by_fixed_char_count(text, 100)  # 每 100 字切一块
for i, chunk in enumerate(chunks):
    print(f"块 {i} - 长度 {len(chunk)} - 内容: {chunk}")

2. 固定长度 + 重叠窗口

在固定长度基础上,让相邻块保留一段重叠内容(overlap),用滑动窗口的方式切,以保住跨块的语义连续性。生产环境里 overlap 常设为块大小的 10%~20%

  • 优点:比纯固定分块更稳,能减少语义断裂。
  • 缺点:数据冗余增加、存储成本变高,也可能带入更多噪音。
# count 为块的字符数,stride 为步长,相邻块重叠 count - stride 个字符
def sliding_window_chunks(text, count, stride):
    return [text[i:i + count] for i in range(0, len(text), stride)]

chunks = sliding_window_chunks(text, 100, 80)  # 块长 100,步长 80 → 重叠 20
for i, chunk in enumerate(chunks):
    print(f"块 {i} - 长度 {len(chunk)} - 内容: {chunk}")

3. 按句子分块

先按句子切,再视情况聚合成块。一句话往往已是较完整的语义单元,中文场景尤其适用。

  • 优点:语义自然,利于问答匹配与引用。
  • 缺点:块可能过短、缺上下文,对需要长上下文的复杂答案不够友好。
  • 注意:避免用 NLTK(不支持中文标点);中文推荐 HanLP、Stanza 或正则分句。
import re

# 按中文句末标点切分;re.split 会在匹配到 。?! 处断开
sentences = re.split(r"[。?!]", text)
for i, chunk in enumerate(sentences):
    print(f"句 {i + 1} - 长度 {len(chunk)} - 内容: {chunk}")

4. 递归字符分块

LangChain 中最常用的方案(RecursiveCharacterTextSplitter)。核心思想:优先按更自然的分隔符切,块仍太大就换更细的分隔符再切,最后尽量合并到接近目标长度。相比机械固定切分,更贴合真实文档。

  • 优点:兼顾语义边界与块大小控制,即插即用。
  • 缺点:对高度格式化内容(表格、代码)效果一般。
  • 分隔符建议(中文,从粗到细):标题(Markdown / 编号)→ \n\n(段落)→ \n(换行)→ → 空格、字符(兜底)。
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=20,                       # 目标块长度
    chunk_overlap=5,                     # 重叠长度
    separators=["\n\n", "\n", "。", ","],  # 从粗到细依次尝试
)
chunks = splitter.split_text(text)
for i, chunk in enumerate(chunks):
    print(f"块 {i + 1} - 长度 {len(chunk)} - 内容: {chunk}")

基础分块经验:推荐 RecursiveCharacterTextSplitter + overlap 作为默认起点。chunk_size 一般 300~500 tokens(或字符),chunk_overlap 取其 5%–20% 防止上下文断裂。同时避免两个极端:小于 ~100 tokens 的块语义不完整;超过模型上下文上限的块无法嵌入。不同类型文档要动态调参。


三、结构感知分块

不再只看字符长度,而是利用文档固有结构(标题、列表、代码块、表格、对话轮次)作为分块边界,让块对齐到内容的自然单元。

1. 结构化文本分块(Markdown / HTML 等)

以标题层级为父节点,聚合其后的内容;超长章节再二次细分。

  • 优点:逻辑清晰、可追溯性强、信噪比高。
  • 实施要点
    • 先解析结构(如用 BeautifulSoup 或正则);
    • 合并同一父标题下过短的块;
    • 为每个块注入父级节点路径(如 指南 > 安装)。
  • 适用场景:技术文档、手册、白皮书等强结构文本。

2. 对话式分块

以"说话人 + 轮次"为单位,按话题窗口聚合。

  • 优点:保持对话连贯性,支持"谁说的、在哪说的"追溯。
  • 重叠方式:用"轮次重叠"(如前后各 1–2 轮)而非字符重叠。
  • 检索优化:召回后做"邻接扩展",拼接命中轮次的前后轮。
  • 适用场景:客服对话、会议纪要、访谈记录。

通用建议:优先用"结构重叠"(如父标题路径)替代大比例字符重叠;并为每个块写入丰富的 metadata(标题、层级、类型等),它对后续检索与重排的提升往往很显著。


四、语义与主题分块

不依赖物理结构,而是依据语义连续性或话题转移来切分,目标是让每个块内部高度内聚。

1. 语义分块

  • 原理:计算句向量,当相邻句子的语义相似度骤降(出现"语义突变")时切分。
  • 流程
    1. 中文分句;
    2. 句向量编码(如 BGE-M3);
    3. 检测新颖度(novelty)或相似度下降点;
    4. 结合最小 / 最大块长约束做切分。
  • 优点:块内高度内聚,适合高精度检索。
  • 缺点:计算开销大;对短文档效果有限。
  • 适用场景:论文、白皮书、技术论证类文档。

2. 主题分块

  • 原理:对句向量聚类(如 KMeans),在主题标签切换处切分。
  • 流程
    1. 句向量 + 聚类;
    2. 序列平滑(避免标签来回抖动);
    3. 主题命名(关键词或摘要)。
  • 优点:能捕捉宏观话题边界。
  • 缺点:需预设主题数;小文档不适用。
  • 适用场景:长篇报告、书籍、多主题综述。

五、高级分块策略

在基础切分之上,引入"检索时如何组装"的设计,进一步兼顾精准召回与上下文完整。

1. 小-大分块(Small-Big Chunking)

  • 思想:用"小块"(句子)做高精度召回,用"大块"(段落)提供上下文。
  • 流程
    1. 构建句子级索引 + 段落存储;
    2. 检索命中句子后,聚合到其所属段落;
    3. 交叉编码重排,组装局部上下文(命中句 ± 邻近窗口)。
  • 优势:兼顾精准性与上下文完整性。
  • 适用场景:需要"句级证据 + 段落解释"的问答系统。

2. 父子段分块(Parent-Child Chunking)

  • 思想:显式维护"父块(段落)— 子块(句子)"的映射关系。
  • 优势:支持灵活的检索组装,易于去重与回链。
  • 与小-大分块的关系:父子段是数据建模方式,小-大是检索流程,两者常配合使用。

3. 代理式分块(Agent-Based Chunking)

  • 思想:用小型 LLM Agent 动态判断分块边界。
  • 规则
    • 不在代码 / 表格 / 公式中间切分;
    • 保持标题链路完整;
    • 输出结构化 JSON(含偏移量、路径、切分理由)。
  • 适用场景:高度复杂、混合格式的非结构化长文档。
  • 注意:需加规则护栏 + 自动回退机制,控制成本。

六、混合分块(Hybrid Chunking)

核心理念:单一策略难以覆盖所有场景,应"先粗后细、按需细化"——前面各类策略不是互斥选项,而是可以组合的工具。

推荐流程:

  1. 粗切:先按结构(标题 / 段落 / 代码块)初步分割;
  2. 细化:分文本类型分别处理——
    • 普通文本 → 递归或句子分块;
    • 超长 / 混杂文本 → 语义分块;
    • 对话 → 对话式分块;
    • 原子块(代码 / 表格)→ 保持完整、不切;
  3. 索引:同时构建"小块索引"与"大块存储";
  4. 检索:小块召回 → 父块聚合 → 局部上下文组装。

按"质量 — 成本"分档,可参考下面三档落地:

档位策略组合适用场景
Fast结构 → 递归快速上线、资源有限
Balanced(推荐)结构 → 递归 + 异常块语义分块 + 小-大检索大多数生产环境
QualityBalanced + Agent 精修 + 强 rerank高精度要求场景

七、选型与调优总结

  • 分块是 RAG 的基石,其质量直接影响最终答案的事实性与连贯性。
  • 避免固定长度硬切,优先尊重文档的自然结构与语义边界。
  • 重叠窗口(overlap) 是维持上下文连续的关键,但不宜过大(通常 ≤ 20%)。
  • Metadata 至关重要:标题路径、块类型、位置信息等能显著提升检索与重排效果。
  • 混合策略最稳健:结合结构、语义与任务目标,动态选择最优切法。
  • 调优方法论
    • 固定检索与重排,只调分块参数,做受控对比;
    • 用 Recall@k、nDCG、事实性(faithfulness)等指标评估;
    • 观察块长分布,动态调整 chunk_size 与分隔符。

最终目标:让知识以连贯、可追溯、高内聚的方式呈现给 LLM,而不是一堆碎片化的噪声。


相关笔记:Naive RAG 概述Naive RAG 讲解RAG,全称 Retrieval-Augmented Generation(检索增强生成),是过去两年大模型应用落地里最重要的工程方案之一。 如果把大模型比作一个"很聪明但记忆固定的学生",那么 RAG 做的事情就是:先去外部资料里找答案,再带着资料回答问题。 而 Naive RAG,可以理解为最基础、最原始、也是最容易落地的一代 RAG 方案。它的结构并不复杂,但已经足够解决很多实际问题,比如企业知识库问答、内部文档检索、客服机器人、私有数据接入大模型等。 一、为什么需要 RAG 大语言模型能力很强,但存在三类核心局限。 1. 时效性有限 模型知识来自训练数据,而训练数据有截止时间。最新政策、财报、内部 SOP、个人知识库文档——这些如果没被放进训练数据,模型就无法直接回答。 2. 知识覆盖不完整 即使训练了海量语料,也不可能覆盖所有垂直领域和每家企业的内部资料。医疗、法律、金融等高门槛领域,以及内部文档、合同、产品手册、私有接口说明——模型可能"懂一点",但不够深、不够准。 3. 幻觉问题 大模型在不知道答案时,仍可能生成一个看起来很像答案的内容——语气自信,内 · Embeddings 向量化Embeddings 向量化Embeddings 是 RAG 的基础设施之一。它解决的核心问题很简单:如何把人类语言转换成机器可以计算“语义距离”的数字表示。 它对应 [[Naive RAG 概述]] 离线流程里的"向量化"一步——文档被 [[RAG的分块策略详解|分块]]之后,正是靠 Embedding 转成向量才得以检索。 一、Embeddings 向量化是什么 在数学里,向量(Vector)是一个由多个数值组成的有序数组,可以理解为一个点在高维空间中的坐标。 在 RAG 场景中,向量化就是把一句话、一个段落,甚至一个文档切片,转换成一个固定维度的数值数组。一个文本块对应一个向量,例如: - [0.8, -0.3, 0.5, 0.34, 0.12, 1.78, 9.12, 2.34, -0.23, 0.5](10 维) - [5, 9, ..., -0.23, 0.5](1024 维) 维度越高,通常能表达更丰富的语义特征,但也意味着更高的存储和计算成本。 image.png 这些向量由 Embedding 模型(嵌入模型) 生成。它和大语言模型一样,底层通常基于 Transformer 架构