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_size300–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. 语义分块
- 原理:计算句向量,当相邻句子的语义相似度骤降(出现"语义突变")时切分。
- 流程:
- 中文分句;
- 句向量编码(如 BGE-M3);
- 检测新颖度(novelty)或相似度下降点;
- 结合最小 / 最大块长约束做切分。
- 优点:块内高度内聚,适合高精度检索。
- 缺点:计算开销大;对短文档效果有限。
- 适用场景:论文、白皮书、技术论证类文档。
2. 主题分块
- 原理:对句向量聚类(如 KMeans),在主题标签切换处切分。
- 流程:
- 句向量 + 聚类;
- 序列平滑(避免标签来回抖动);
- 主题命名(关键词或摘要)。
- 优点:能捕捉宏观话题边界。
- 缺点:需预设主题数;小文档不适用。
- 适用场景:长篇报告、书籍、多主题综述。
五、高级分块策略
在基础切分之上,引入"检索时如何组装"的设计,进一步兼顾精准召回与上下文完整。
1. 小-大分块(Small-Big Chunking)
- 思想:用"小块"(句子)做高精度召回,用"大块"(段落)提供上下文。
- 流程:
- 构建句子级索引 + 段落存储;
- 检索命中句子后,聚合到其所属段落;
- 交叉编码重排,组装局部上下文(命中句 ± 邻近窗口)。
- 优势:兼顾精准性与上下文完整性。
- 适用场景:需要"句级证据 + 段落解释"的问答系统。
2. 父子段分块(Parent-Child Chunking)
- 思想:显式维护"父块(段落)— 子块(句子)"的映射关系。
- 优势:支持灵活的检索组装,易于去重与回链。
- 与小-大分块的关系:父子段是数据建模方式,小-大是检索流程,两者常配合使用。
3. 代理式分块(Agent-Based Chunking)
- 思想:用小型 LLM Agent 动态判断分块边界。
- 规则:
- 不在代码 / 表格 / 公式中间切分;
- 保持标题链路完整;
- 输出结构化 JSON(含偏移量、路径、切分理由)。
- 适用场景:高度复杂、混合格式的非结构化长文档。
- 注意:需加规则护栏 + 自动回退机制,控制成本。
六、混合分块(Hybrid Chunking)
核心理念:单一策略难以覆盖所有场景,应"先粗后细、按需细化"——前面各类策略不是互斥选项,而是可以组合的工具。
推荐流程:
- 粗切:先按结构(标题 / 段落 / 代码块)初步分割;
- 细化:分文本类型分别处理——
- 普通文本 → 递归或句子分块;
- 超长 / 混杂文本 → 语义分块;
- 对话 → 对话式分块;
- 原子块(代码 / 表格)→ 保持完整、不切;
- 索引:同时构建"小块索引"与"大块存储";
- 检索:小块召回 → 父块聚合 → 局部上下文组装。
按"质量 — 成本"分档,可参考下面三档落地:
| 档位 | 策略组合 | 适用场景 |
|---|---|---|
| Fast | 结构 → 递归 | 快速上线、资源有限 |
| Balanced(推荐) | 结构 → 递归 + 异常块语义分块 + 小-大检索 | 大多数生产环境 |
| Quality | Balanced + 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 架构