分块(Chunking)是 RAG 检索效果的核心环节。简单来说,就是把长文档切成一小段一小段的文本块,让系统能够按语义精准召回。RAG 通过嵌入向量做语义检索,每个 chunk 需要承载单一完整的语义;一旦切割质量跟不上,语义被拦腰截断,检索效果就会大打折扣。
目前主流的分块策略有 8 种:固定长度分块、按句子分块、递归分块、文档结构分块、语义分块、上下文增强分块、由小到大分块、智能体分块。递归分块是工程上的默认首选;文档结构分块依托标题与章节边界;语义分块依靠向量相似度识别主题切换;上下文分块为 chunk 补充元数据消除代词歧义;由小到大分块实现“小块检索、大块生成”;智能体分块则交由大模型切分,适合少量高价值复杂文档。

10 个关键问题 Q&A
Q1:RAG 为什么一定要做 chunk 分块?
A:模型上下文窗口有限;降低 token 成本;避免信息淹没;小块语义单一,语义检索更准确。
Q2:分块的核心目标是什么?
A:每个文本块包含一个完整观点,单独取出也能读懂,保证向量语义清晰。
Q3:工业项目默认推荐哪种分块?
A:递归分块,优先段落边界,超限再降级切句子 / 空格,兼顾性能与效果。
Q4:文档结构分块的关键技巧是什么?
A:切割时携带上级标题路径,让 chunk 自带章节上下文,单独使用不会丢失归属。
Q5:语义分块靠什么判断切割点?
A:计算相邻句子嵌入向量相似度,相似度骤降代表主题切换,在此切分。
Q6:上下文增强分块解决了什么痛点?
A:解决代词(它、该规则)指代模糊问题,给 chunk 补充来源、章节信息。
Q7:Small-to-big(由小到大)分块的思路?
A:用小子块检索保证召回精度;命中后取出对应的大父块交给模型,提供完整上下文。
Q8:chunk 重叠 overlap 的作用与推荐值?
A:防止答案跨两个 chunk 导致信息断裂;推荐为块大小 10%–20%。
Q9:如何选择合适的 chunk token 大小?
A:根据回答信息体量:简短事实 200–300 token;手册流程 500–800 token;长文档 1000–1500 token,用真实问句测试验证。
Q10:修改 chunk 大小 / 嵌入模型后,需要做什么?
A:必须重建向量索引,旧的嵌入向量会失效。

什么是 RAG?
在深入分块之前,先要搞清楚 RAG 本身。RAG = Retrieval(检索)+ Augmented(增强)+ Generation(生成)。
拆成三部分理解:
- 检索:查找有用信息
- 增强:把额外信息补充进来
- 生成:输出回答
所以 RAG 是这样一套系统:先找到相关信息,把信息附加到用户提问中,再交给 AI 模型,让模型依托这些信息生成答案。
举个例子:我们有一份 500 页的员工手册,向 AI 提问:入职第一年我能休多少天假? 大模型在训练时没有读过这份手册——它的训练数据来自公网,而这份手册是存放在本地电脑里的私有文档,模型本身不知道答案。这时 RAG 就派上用场了,一共三步:
- 在手册中检索,找到讲解年假规则的片段;
- 将该片段和用户问题拼接在一起;
- 把问题 + 文本片段交给大模型,要求模型依据这段内容作答。
模型相当于拿到了对应的原文,就能给出准确答案,不需要对模型重新训练。RAG 的工作方式,相当于让模型参加开卷考试,而不是要求它记住全部资料。
但新的问题来了:RAG 该怎么检索 500 页手册?不可能每次都把整整 500 页全部传给模型——体量太大、速度慢、成本高。所以我们要预先把文档切分成小段文本。这些小段文本,就叫做 chunk(文本块)。
什么是文本块(Chunk)?
文本块,就是从长文档里截取出来的一小段文字。
简单比喻:文档是一整条巧克力,文本块就是其中一小块。
假设手册中有这样一段话:
员工入职第一年享有 24 天带薪年假。年假不可结转至下一年。病假单独核算,上限 12 天。
如果按句子切分,就能得到 3 个文本块。每一块都是独立简短的信息片段。之后当有人问病假相关问题时,不需要读取整本手册,只需要取出第三个文本块即可。
这个切割过程,就叫分块(chunking)。
为什么需要文本分块?
一共有四个核心原因。
-
大模型存在上下文窗口上限
模型单次能够一次性读取的文本总量存在限制,这个限制叫做上下文窗口。窗口大小以 token(令牌) 计量。一个 token 是文本的最小单元,大约等于 3/4 个单词,100 token 约等于 75 个单词。这个概念后文会反复用到。
500 页手册拥有数百万 token,远超上下文窗口上限,因此只能传入相关片段。
-
成本与速度
就算文档能塞进上下文窗口,每次传入全文依然很慢、开销昂贵。每传入一个 token 都需要计费。只传入一小段文本,远比传入 500 页全文划算。
-
回答准确性(最重要)
如果一次性投喂海量文本,关键信息会被淹没。模型容易被无关内容干扰,混淆不同页面里的事实。只给模型 3 段简短相关文本,回答会干净准确。少量、准确的文本,永远优于大量、充满噪声的文本。
-
检索环节本身就需要小段文本
检索依靠语义相似度比对。小段文本只承载单一清晰含义;超大文本块混杂几十种不同主题,含义变得模糊,检索效果大幅下降。下一节会详细解释这一点。
检索到底是如何工作的?
理解检索原理之后,所有分块策略就很好理解了。RAG 内部检索不是关键词检索,而是语义检索。
完整流程:
步骤 1:每个文本块转为一组数字向量
将每个文本块送入一个小型模型——嵌入模型(Embedding Model)。模型读取文本块,输出一长串数字,形如 [0.12, -0.98, 0.45, ...]。这串数字就叫做嵌入向量(embedding),可以理解为这段文本含义的“地址”。主题相近的文本块,对应的向量地址在空间上距离更近。
步骤 2:所有向量存入向量数据库
向量数据库,专门用来做一件事:根据给定向量地址,查找空间上距离最近的其他向量。
文档切割、生成嵌入向量、入库存储,这整套一次性预处理流程叫做索引构建。索引提前建好,用户提问时才会调用。
步骤 3:用户问题同样转为向量
用户提问:“我有多少天病假?”,同一个嵌入模型会把这句话也转为向量地址。
步骤 4:召回相似度最高的文本块
向量数据库对比问题向量与所有文本块向量,返回距离最近的几条,一般取 Top3 或 Top5。真实系统里,这一步之后通常还有重排器(reranker),对召回结果按真实相关性重新排序,再送入大模型。
步骤 5:将召回的文本块送入大模型
把文本块连同用户提问一起交给模型,生成答案。
这里有个关键前提:每个文本块只承载一个清晰含义。一旦一个文本块混杂十个无关观点,它的向量就是十种含义的平均,无法和任何查询靠近。这正是分块策略至关重要的根本原因。
分块处理不当会带来什么后果?
举个例子直观理解。手册原文:
员工入职第一年享有 24 天带薪年假。如需申请,请登录 HR 系统,至少提前 7 天提交申请。
如果粗暴按固定字符切割,切口刚好落在句子中间:
- 文本块 1:员工入职第一年享有 24 天带薪年假。如需申请,请登录 HR 系统,至少提前
- 文本块 2:7 天提交申请。
用户提问:申请休假需要提前多久提交? 文本块 2 包含答案,但它只是残缺片段,里面没有“休假、申请”这类关键词。它的语义向量和用户问题差距很大,检索时不会被召回。反而文本块 1 会被检索出来,但句子在答案前中断。模型读到之后,要么回答不知道,更糟的情况是编造数字。
核心问题:劣质的文本块会悄悄毁掉最终回答。因此分块的核心目标很简单:在一个完整语义单元结束的地方切割,不要随机截断。
下面介绍的每一种策略,本质都是寻找这类切割点的更智能方案。我们由最简单的方案开始,逐步升级。
1. 固定长度分块(Fixed-size Chunking)
固定长度分块:无论文本内容,每达到固定字符数 / 令牌数就切割。这是最简单的策略。例如设定每 500 字符切一刀。
示例伪代码:
def fixed_size_chunks(text, size=500):
chunks = []
for i in range(0, len(text), size):
chunks.append(text[i:i + size])
return chunks
这段代码每隔 500 字符截取一段,作为文本块。
✅优点:
- 实现简单,处理速度快
- 每个块长度可预测,不会超出模型上下文限制
- 适用于任意文本,包括结构混乱的脏文本
❌缺点:
- 可能切断单词、句子,甚至语义单元,就像前面休假申请的例子
- 召回的文本块经常是破碎片段
该方案的问题在于,切割位置只由字符计数决定,不考虑语义。下一种策略将改善这个问题。
2. 按句子分块(Chunking by Sentence)
按句子分块:先把原文切分成单个句子,再将若干句子合并为一个文本块。不再按 500 字符切割,而是只在句号、问号、感叹号处切分。这个改动,完全避免句子被拦腰切断。
示例伪代码:
import re
def sentence_chunks(text, sentences_per_chunk=5):
sentences = re.split(r'(?<=[.!?])\s+', text)
chunks = []
for i in range(0, len(sentences), sentences_per_chunk):
group = sentences[i:i + sentences_per_chunk]
chunks.append(" ".join(group))
return chunks
代码用正则在句末标点 + 空格的位置切分出句子,每 5 个句子合并为一个文本块。
✅优点:
❌缺点:
- 文本块长度参差不齐,长句和短句差异巨大
- 合并逻辑依旧盲目:第 5 句是一个话题结尾,第 6 句是全新话题,该策略依然会把它们合并到同一块
该方案尊重句子,但忽略段落与章节边界。下一种策略解决这个问题。
3. 递归分块(Recursive Chunking)
递归分块:优先在文档最强自然边界切割;如果切出来的片段依然过大,再使用次级弱边界继续切分。这是工业项目里最常用的方案,思路非常巧妙。
文档天然存在边界,优先级从高到低:
- 段落空行(最强边界)
- 单行换行
- 句号
- 空格(最弱边界)
执行逻辑:优先按空行切割。切完如果片段仍然超过最大块长,单独拿这个片段,用换行符继续切;如果还是过大,再用句号切;仍然超限,最后用空格切割。只有必要时,才降级使用更弱分隔符,因此叫递归分块。
示例伪代码:
def recursive_chunks(text, size=500, separators=["\n\n", "\n", ". ", " "]):
if len(text) <= size or not separators:
return [text]
separator = separators[0]
parts = text.split(separator)
chunks = []
for part in parts:
if len(part) <= size:
chunks.append(part)
else:
# 片段依旧过大,使用下一级更弱分隔符递归处理
chunks.extend(recursive_chunks(part, size, separators[1:]))
return chunks
代码优先使用 \n\n 空行分割;只有片段超标,才递归调用,使用后面的分隔符。
注:这段代码为了便于理解做了精简。生产实现还会把相邻的极短片段合并,避免产生大量单行小块。
✅优点:
- 大部分文本块刚好对应自然段落,承载完整语义
- 不会超出设定块长,最坏情况降级到按空格切分
- 适配绝大多数文档,是绝大多数 RAG 系统的默认方案
❌缺点:
- 仅遵循文本排版,不理解语义。如果一个段落内包含多个无关主题,依旧会全部放在同一个块中。
该方案把文档都当成纯文本,忽略文档自带标题。下一种策略解决这个问题。
4. 基于文档结构分块(Document Structure Based Chunking)
基于文档结构分块:利用文档自带标题、章节、表格,沿着文档结构边界切割。
很多文档不是纯文本:Markdown 有 #、## 标题;网页 HTML 有 h1/h2 标签;PDF 包含章节;代码文件包含类和函数。文档作者本身就标记好了话题起止位置,忽略这些信息非常浪费。
示例 Markdown 文档:
# 休假政策
## 带薪年假
员工入职第一年享有24天带薪年假。
## 病假
病假上限12天,连续休假超过3天需要提供医生证明。
按 ## 二级标题切割,得到两个干净文本块,一块专门讲年假,一块专门讲病假,主题互不混杂。
有一个关键技巧:需要把上级标题路径附加到文本块内。第二个文本块存储形式如下:
休假政策 > 病假 病假上限 12 天,连续休假超过 3 天需要提供医生证明。
附加标题路径之后,就算单独取出这个文本块,也能知道它所属文档与章节。
✅优点:
- 由文档作者定义分割边界,分割质量通常很好
- 附带标题路径,每个文本块拥有清晰归属
❌缺点:
- 仅适用于带有结构化标记的文档;会议录音转写稿、扫描件文本没有标题,无法使用
- 章节篇幅长短差异巨大:有的章节两行,有的长达几十页。超长章节内部,依然需要结合尺寸限制再次切分
工程实践中一般组合使用:先按标题切分,超长章节内部再用递归分块。这套组合几乎能处理绝大多数真实文档。
5. 语义分块(Semantic Chunking)
语义,即含义。语义分块:在主题切换的位置切割文档;通过语义相似度自动识别主题切换点。
前面所有策略都是基于文本形态切割(字符、句子、段落、标题)。语义分块是第一个基于含义切割的方案。
执行步骤:
- 将文档拆分为单个句子
- 给每个句子生成嵌入向量
- 依次遍历相邻句子,计算语义相似度。向量距离近 = 主题相同
- 相邻句子相似度突然大幅下降,代表主题切换,在此处切割
举例:
- 员工入职第一年享有 24 天带薪年假。
- 年假不可结转至下一年。
- 公司食堂开放时间:早 8 点至晚 6 点。
- 食堂提供早、午餐和晚间简餐。
第 1、2 句都是年假,相似度高;第 2 句和第 3 句话题完全无关,相似度骤降,就在这里切分。最终得到两个文本块:年假、食堂。全程不需要任何标题。
✅优点:
- 分割边界跟随真实主题,每一块只讲一件事
- 无标题文档也能用:例如转录稿、聊天记录
❌缺点:
- 速度慢、成本高:需要为每一句话单独生成嵌入向量
- 需要调优相似度下降阈值:阈值太敏感,会产生大量极小文本块;阈值宽松,生成大块文本
- 如果整篇文档是连贯单一主题,会找不到合适切割点
但就算分割完美,单独取出的文本块也可能丢失上下文。下面的策略解决这个痛点。
6. 上下文增强分块(Contextual Chunking)
先理解一个很容易被忽略的问题。假设分块结果如下:
病假限额自 4 月起上调至 18 天,并且取消了医生证明要求。
用户提问:当前病假上限是多少? 这段文本包含答案,但仔细看:文中没有“病假”二字,只写了“限额”。阅读完整文档的人知道指病假,但这个文本块单独存入向量库,失去上下文。它的向量语义模糊,检索时无法命中。丢失上下文的文本块就失去价值。
上下文增强分块:在存入向量库前,在每个文本块头部增加简短上下文描述,保证文本块可以独立读懂。
改造后的文本块:
来自员工手册,休假政策章节,关于病假:病假限额自 4 月起上调至 18 天,并且取消了医生证明要求。
现在文本自带归属信息,包含“病假”关键词,更容易匹配用户查询。
两种生成上下文前缀的方式:
-
低成本方案
使用已有元数据(文件名、标题路径)拼接,几乎无额外开销,提升效果显著
-
高成本方案
把文本块 + 全文交给大模型,让模型总结一句话描述该片段。效果更好,但每个文本块都要调用一次大模型。
✅优点:
- 大幅提升检索准确率,尤其针对大量代词(它、此、上述)的文本
- 可以叠加在任意其他分块策略之上,不需要二选一
❌缺点:
- 高成本方案在索引阶段会产生大量模型调用开销
- 上下文描述会占用文本块的 token 配额
这是投入产出比极高的优化手段,低成本方案几乎零开销,强烈建议使用。
但现在我们还面临一个矛盾:小块检索准,但回答信息不足;大块信息充足,但检索不准。下一种策略解决这个两难。
7. 由小到大分块(Small-to-big Chunking)
矛盾点:
- 小块:语义单一,检索容易命中;但送入模型时信息单薄,不足以完整作答
- 大块:信息充足,适合生成答案;但向量是多个主题混合,检索很难命中
我们希望兼顾两者:用小块检索,把对应的完整大块送入模型。
由小到大分块的做法:文档做两层切割。第一层切为父块(大段,例如完整章节);再将每个父块切为多个子块(短句 / 短段落)。向量库只存储子块的嵌入向量;每个子块记录自己属于哪个父块。
查询阶段:检索子块。命中某个子块之后,不把这个小子块传给模型,而是取出它所属的完整父块。
示例:
- 父块:完整《病假》章节,共 12 句话
- 用于检索的子块:
- “病假上限 12 天。” → 归属父块:病假章节
- “连续休假 3 天以上需要医生证明。” → 归属父块:病假章节
- “限额在 4 月上调至 18 天。” → 归属父块:病假章节
用户提问:休病假是否需要医生证明?第二个子块精准命中。系统不会只传入这一句话,而是送入完整病假章节。模型同时看到天数、证明规则、4 月更新条款,输出完整答案。
✅优点:
- 同时实现精准检索 + 丰富上下文
- 不再需要寻找一个兼顾检索和生成的完美固定块长
❌缺点:
- 需要维护两层文本块,索引流水线复杂度上升
- 最终 Prompt 体积变大,单次查询成本上升
- 多个子块命中同一个父块时,需要去重,避免重复传入同一章节
到这里,前面所有策略都是人工写死切割规则。下一种策略交给模型自己决定切割位置。
8. 智能体驱动分块(Agentic Chunking)
智能体驱动分块:直接将文档交给大模型,由模型自主决定切割位置。前面所有策略都是人定义规则;而这个方案让模型像人类编辑一样阅读文档,标记分割边界。
给模型的提示示例:
阅读下方文档。将文档切分为片段,每一段只包含一个完整观点。不要在表格、列表中间切断。给每个片段写一行标题。
模型返回分段结果 + 每段标题,直接作为文本块入库。
✅优点:
- 处理规则很难搞定的复杂场景:跨页表格、编号列表、法律条款(不可截断)
- 模型生成的单行标题,可直接作为文本块的上下文信息
❌缺点:
- 成本最高,速度最慢
- 结果不稳定:同一份文档两次处理,分块结果可能不一样
- 不适合百万级大规模文档
因此只适合少量高价值、格式杂乱文档,例如合同、医疗记录、财报文件。其余场景,前面的策略性价比更高。
学习完所有策略,下面两个参数,无论选择哪一种分块方案都必须配置:分块重叠。
分块重叠(Chunk Overlap)
分块重叠:后一个文本块,重复包含前一个文本块末尾的少量内容。
为什么需要重叠?再好的分块策略,切口必然落在某处。有时候答案刚好跨两个文本块:一半在块 1 末尾,一半在块 2 开头,两个块单独拿出来都无法完整回答问题。重叠就是应对这种情况的安全兜底。
示例,无重叠:
- 块 1:…… 休假需要至少提前 7 天申请。
- 块 2:紧急休假不受这条规则约束。
块 2 中“这条规则”指代前文。如果只召回块 2,模型不知道指代哪一条规则。
开启重叠:
- 块 1:…… 休假需要至少提前 7 天申请。
- 块 2:休假需要至少提前 7 天申请。紧急休假不受这条规则约束。
块 2 开头重复上一段最后一句话,块 2 自身就语义完整,问题解决。
常用经验值:重叠量为块总长度的 10%~20%。例如 500 token 的块,重叠设置 50~100 token 效果很好。
注意:重叠不是无代价的。重复文本会重复入库、生成向量,向量库体积变大。重叠过高会导致同一段内容多次被召回,浪费 Prompt 空间,重叠量要控制在较小范围。
如何选择分块大小
所有人都会问这个问题:没有万能固定数字,但有一套可靠判断逻辑:文本块大小,应该匹配你的资料中单次回答需要的信息体量。
分三类场景:
场景 1:简短事实问答
产品目录、FAQ、定义清单。答案通常一两句话。推荐小块:200~300 token。每个块刚好一条事实,无冗余。
场景 2:说明、流程类文档
技术文档、员工手册、教程。答案一般一到两个段落。推荐中等块:500~800 token。大多数项目的安全默认值。
场景 3:长推理、叙事类内容
论文、法律合同、会议纪要。回答需要大量上下文支撑。推荐大块:1000~1500 token。搭配由小到大分块效果更佳。
实操选型方法:准备 20 条真实用户提问。先用 500 token 块 + 50 token 重叠搭建系统,测试这些问题能否召回正确文本块。再测试 300、1000 token,对比效果,选择表现最优的参数。基于真实问题测试,不要凭猜测选定。
重要提醒:嵌入模型本身也有输入上限。文本块一旦超过嵌入模型最大输入长度,超出部分会被静默丢弃,不会参与向量计算。块长必须小于嵌入模型的上下文上限。
全部策略对比表
| 策略 |
切割方式 |
成本 |
质量 |
适用场景 |
| 固定长度分块 |
按 N 个字符截断 |
极低 |
低 |
快速原型、杂乱无结构文本 |
| 按句子分块 |
在句末标点切割 |
极低 |
低~中 |
干净简短文本 |
| 递归分块 |
优先段落,逐级降级 |
低 |
良好 |
绝大多数项目,默认首选 |
| 基于文档结构分块 |
沿着标题、章节切割 |
低 |
很好 |
Markdown、HTML、带标题 PDF |
| 语义分块 |
在主题语义突变处切割 |
高 |
很好 |
无标题文档,例如转录稿 |
| 上下文增强分块 |
在任意分块结果上附加上下文描述 |
中~高 |
很好 |
包含代词、引用关系的任意文档 |
| 由小到大分块 |
用小子块检索,返回大父块 |
中 |
很好 |
需要完整上下文的长文档 |
| 智能体驱动分块 |
由大模型自主标记切割边界 |
极高 |
优秀 |
少量高价值、格式复杂文档 |
注意:这些策略并非互相排斥。上下文增强、由小到大分块是叠加层,可以和其他方案组合,而不是替代它们。
一套很强、工程实用的组合方案:
- 清洗文档,先按标题切分;
- 超长章节使用递归分块,设置少量重叠;
- 给每个文本块添加上下文描述;
- 长章节启用由小到大分块:检索子块,返回完整父块。
这套方案开发简单、运行成本可控,覆盖绝大多数业务场景。遇到特殊文档,后续再叠加语义分块或智能体驱动分块。
常见踩坑
绝大多数分块相关问题,都源于下面这些错误,需要规避:
-
直接对原始文件分块
PDF 未经清洗会混入页码、页眉页脚,这些噪声混入文本块,污染语义。分块前必须预处理清洗文本。
-
切断表格、代码块
半个表格片段,还不如没有表格。表格、代码块、列表要作为完整单元保留,即使超出块长。
-
不存储元数据
每个文本块需要附带源文件、章节、页码、时间。支持检索过滤,同时可以给用户展示信息来源。
-
所有文档使用同一套块参数
FAQ 和法律合同,需要完全不同的块尺寸。不同类型文档要使用独立配置。
-
从不做效果评估
很多团队选定块大小之后不再验证。需要维护测试问题集持续评估,这是检验分块效果唯一可靠手段。
-
参数变更后忘记重建索引
一旦修改块长、重叠量、更换嵌入模型,所有已存向量都会失效,需要全量重建索引。
总结
分块看起来只是 RAG 流水线里一个小小的环节,但它决定后续全部流程。如果相关文本片段检索不出来,再好的提示词、更大的模型,都无法救回回答质量。
记住核心准则:一个文本块承载一个完整观点,单独拿出来也能读懂。 我们介绍的所有策略,本质都是实现这个目标的不同手段。
推荐起步方案:优先使用递归分块,利用文档标题,给每个块添加上下文,设置少量重叠,并用真实问题评估效果。这套方案就可以很好解决检索问题。
如果你正在搭建 RAG 链路,也欢迎在云栈社区与开发者持续交流分块调优与工程实践经验。
