想象你经营一家私房菜馆,客人点单后,你从菜市场买回一整条三文鱼、一整颗南瓜、一整扇排骨。你当然不会把整条三文鱼直接端上桌让客人自己撕着吃——那样既无从下筷,也找不到最美味的部位。你需要“备菜”:先按纹理和部位切成合适大小的块,既保证每块都完整入味,又不至于切得太碎让客人不过瘾。 大语言模型的应用系统(如 RAG,检索增强生成)也面临同样的“备菜”难题。它的“冰箱”——知识库——里存放着大量长文档。当用户提问时,系统要去冰箱里翻找相关资料,再把找到的食材(片段)交给大模型“厨师”烹饪成答案。如果整篇文档直接入库存取,就像把整头牛塞进冰箱,检索时既拖沓又找不到重点。所以,所有文档入库存放前,都要经过一道关键工序:分块(Chunking)。本文就用“备菜刀法”作比喻,带你一口气看懂语义分块、滑动窗口等主流分块策略,以及它们背后的取舍智慧。
对于 AI 从业者而言,分块策略直接决定 RAG 系统的检索质量、成本与可维护性。掌握它,你就能在知识库项目里做出让工程师和业务方都满意的技术决策。
目录
可视化演示
展示滑动窗口固定分块、语义分块嵌入相似度切分及四种策略性能对比,揭示分块粒度与检索质量的权衡关系。
本节配有交互动效(进入视口后自动加载,约 3KB)。
滑动窗口分块 固定大小窗口以stride步长滑动,重叠区保证上下文连续 stride=chunk_size-overlap 重叠区域减少语义断裂但增加存储开销
1. 为什么需要分块
你雇的帮厨从菜市场回来,把一整头牛放在案板上,问:“老板,直接下锅吗?”你当然会摇头。客人点的是“青椒牛柳”,不是“整牛火锅”。于是帮厨把牛按部位拆解:牛里脊切条、牛腩切块、牛骨熬汤,每一份恰好服务一道菜。
RAG 系统的“厨房”也遵循同样的逻辑。知识库接收文档时,如果整篇存进去,检索时每一轮都要把整篇文档与用户问题做比对。这不仅慢,而且返回的“答案食材”里掺杂了大量无关部位——客人想要牛柳,你端上来一头牛。所以,系统先把文档切成语义相对独立的片段,这些片段被称为块(chunk),每个块才是后续检索的最小单元。
块的大小直接影响两道工序:一是检索(query 与块的匹配),二是生成(把检索到的块喂给大模型)。块太小,一个完整观点被切成碎片,检索到的内容只有“半个证据”;块太大,噪音淹没重点。工程上通常用一个简单公式估算分块数量:
回到厨房场景:分块就像备菜的分寸拿捏。切得太粗,客人找不到想要的部位;切得太细,一炒就碎。理解了这个道理,我们才能继续看不同“刀法”——不同的分块策略,如何各显神通。
- L文档总长度:把整篇文档的字符数(或 token 数)加总,就像一头牛的总重量,是备菜前的原始量。
- K目标块长度:每块计划包含多少字符,由任务精度决定,相当于一盘菜想装多少食材。
- N最少分块数:总长除以块长并向上取整,保证每一块不会超过目标大小。
2. 语义分块原理:看纹理下刀
经验丰富的帮厨绝不闭着眼睛乱切。他拿刀前先观察纹理:牛肉的纹路顺着纤维走,南瓜的质地从皮到瓤渐变。纹理相近的地方一切到底,纹理突然断裂的地方才落刀——这样切出来的每块食材,都自带完整“身份”。
语义分块(Semantic Chunking)就是这种“看纹理下刀”的工程实现。它先把文档按句子拆开,再让每个句子通过一个嵌入模型变成一组数字,这组数字就是句子的“纹理坐标”,也叫向量。随后计算相邻两个句子向量的相似度:相似度高,说明两句话还在聊同一个话题;相似度突然跌到低谷,说明话题切换了——这里就是切分点。
实际操作时,相似度通常用余弦相似度计算:
语义分块的产出是“话题完整”的块:边界和自然语义对齐,检索时容易精准命中。缺点是每个句子都要做一次向量计算,成本比“尺子切法”高得多。帮厨看纹理下刀虽然慢,但端出来的每道菜都完整——这就是语义分块的独特价值。
- 分子向量点积:两个句子向量对应位置相乘再相加,衡量它们朝同一方向“走”了多远。
- 分母模长乘积:各自先算“长度”再相乘,作用是消除句子长短带来的干扰。
- 比值余弦相似度:结果落在 -1 到 1 之间,值越大,两个句子在语义空间里越“同向”。
3. 固定窗口法:尺子切菜
如果今天你请的帮厨是位临时工,他不想观察纹理,也不想算相似度。他直接拿出一把尺子,在长条案板上每隔10厘米“咔”一刀。整根胡萝卜、整条茄子全切成等长的小段,快速利落。
固定窗口分块(Fixed-size Chunking)就是这把“尺子”。它设定一个固定长度 K(比如 512 个 token 或 200 个字符),从文档开头每 K 个长度切一刀,不考虑句子、段落或语义。优点是极简单:不需要任何模型,速度最快,代码只有几行。对日志、结构化表格、代码仓库这类天然等长的文本,它完全够用。
但它有显而易见的短板:可能把一句话拦腰斩断,把一段完整的论证拆成两半。此时可用窗口遍历公式估算块数:
尺子切菜确实省事,但代价是切坏纹理:一句话可能被切成两半,就像把三文鱼腹部的花纹拦腰切断。所以它只适合“标准化食材”,遇到讲究语义的文档就要换更聪明的刀法。
- L - K可滑动范围:最后一个窗口的起点最多能到文档末尾之前的 K 个字符处。
- 除以 S可放几个步长:把可滑动范围除以步长,得到中间还能移动几步。
- 向下取整加一向下取整得到中间步数,再加首窗口 1 个,就是总窗口数。
4. 滑动窗口法:切蛋糕的重叠刀法
过生日切蛋糕时,你会让每一刀都和上一刀稍微重叠一点吗?其实不用。但如果你不是一次性切完,而是“边切边分给客人”,重叠刀法就有用了:每一刀都从上一刀边缘往后挪一段,让每一块蛋糕都带上一点上一块的花边——这样无论奶油花纹在哪里,每块都能分到完整的一层。
滑动窗口分块(Sliding Window Chunking)借鉴了这种重叠智慧。它仍使用固定窗口大小 K,但每次滑动距离 S 小于 K。于是相邻两个块之间有一段重复内容,称为重叠区。这段重叠让跨块上下文得以保留:上一块的结尾结论,会在下一块的开头再次出现,检索时无论命中哪一块,上下文都相对完整。
重叠的程度用重叠率 r 表示,步长与重叠率的关系是:
滑动窗口是“上下文连续性”与“存储效率”的折中。重叠太多,知识库膨胀、检索返回重复;重叠太少,连续性问题依旧。就像切蛋糕:刀刀重叠才能保住花纹,但每刀都切在同一个地方,那就永远切不出蛋糕了。
- K × (1 - r)有效新内容:每次滑动只往前带出 (1 - r) 比例的新内容,其余是重叠。
- S实际步长:窗口每步移动的距离,等于新内容长度。
- 重叠区长度K 减去 S,就是两块之间共享的字符数。
5. 递归分块法:冰箱分层收纳
整理冰箱时,最高效的思路是分层收纳:冷藏区放蔬菜、冷冻区放肉类;每个区里再用保鲜盒把牛肉、猪肉分开;如果牛肉还要细分,就再贴上标签“牛里脊”“牛腩”。这种“先分大类、再分小类”的结构化方法,就是递归分块(Recursive Chunking)的灵魂。
递归分块先按照文档的原生结构——章节、小节、段落、句子——构建一棵“文本树”。树的根是整篇文档,向下逐层展开到段落甚至句子。切分时“自顶向下”:先把文档按章切开,再把每章按节切开,直到达到目标粒度。每一层都提供不同粒度的块:上层适合概括型检索,下层适合精确型定位。
如果每层固定分成 b 个孩子,树的深度为 d,那么最终叶子块的数量是:
冰箱分层让你从“找肉”变成“先开冷冻区、再拿左边盒子”,检索也可以“先走上层再下钻”:用上层块判断方向,再顺着树往下找具体内容,效率和精确兼得。
- b每层分支数:每一层把文本平均分成 b 个子块,相当于每层往冰箱里放 b 个保鲜盒。
- d树的深度:从根到叶子的层级数,代表“章→节→段”的嵌套层数。
- N叶子块总数:分支数连乘 d 次,就是最终分出来的叶子块数量。
6. Agentic 分块:看单下刀的主厨
前几种刀法都遵循固定的规则,有经验的主厨却会“看单下刀”:客人点了寿喜锅,三文鱼切厚片;客人点了刺身,三文鱼切薄片;客人对鱼过敏,他直接换菜。Agentic 分块就是把这种“临场判断力”交给 AI 的技术。
Agentic 分块的核心是让大语言模型(LLM)充当帮厨。它先阅读文档的标题、章节名、段落大意和当前任务描述(比如“做一个政策条款问答系统”),然后自主决定:这篇文章要不要分层?哪些地方按语义切?哪一段值得整块保留?它甚至可以混合多种策略——前半篇用递归分块,中间某段用语义分块。
从数学视角看,LLM 是在候选策略集合中做选择,目标是最大化最终的检索质量:
看单下刀的主厨让每道菜都恰到好处,但主厨很贵、也慢。Agentic 分块同样如此:它能实现最灵活的分块方案,但每次决策都要调用 LLM,适合对检索质量要求高、知识库规模可控的场景。
- Π候选策略集合:所有可能的分块策略(固定、滑动、语义、递归等)构成一个集合。
- Q(π, D, T)质量函数:给定文档和任务,评估策略 π 能带来多好的检索效果。
- argmax挑选最优:在所有策略中找出让 Q 最大的那个 π。
7. 粒度权衡调参:厚薄之间找平衡
涮火锅的羊肉片要薄,红烧肉的块要厚,回锅肉要切得厚薄适中。没人会说“肉切得越薄越好”或“越厚越好”,一切要看菜式。分块粒度(chunk size)也是同一个道理:没有绝对最优,只有任务匹配。
粒度影响最核心的两个指标是召回率(Recalled relevant chunks / total relevant chunks)和准确率(Relevant retrieved chunks / total retrieved chunks)。细粒度块小、主题单一,检索时容易精确命中目标内容,所以召回率高;但块太短,上下文信息不足,生成阶段可能答得“没头没尾”。
粗粒度块上下文丰富,大模型能充分理解语境,但块内噪声多,可能把不相关的内容一起带给大模型,拉低准确率。两者常被合成一个综合指标 F1:
厚薄合适才好吃。分块大小没有标准答案,但有调参法则:先跑基线、再看指标、再微调。你的任务是决定这盘“菜”要什么口感,而不是执着于最流行的那把刀。
- P准确率:检索到的块中,真正相关的那部分占比。
- R召回率:所有相关块中,被检索出来的那部分占比。
- F1调和平均:把 P 和 R 揉成一个数,对两者一视同仁,防止只偏科。
8. 实践评估应用:用指标检验刀法
再好的刀法都要拿到真实宴席上验证。客人觉得好吃,菜才算成立;分块策略同样要在真实检索任务中接受检验。评估标准不是“切得整齐”,而是“检索质量好不好”。
实践一般分三步。第一步,选择基线:先跑固定窗口与滑动窗口,得到最基本的召回与准确率。第二步,对比升级:换成语义分块或递归分块,看是否带来显著提升。第三步,任务适配:政策评估类项目需要去查“某一具体条款”,就用细粒度语义分块;医学研究要跨段落阅读完整证据链,就用滑动窗口+重叠;商业情报要同时回答宏观和微观问题,就上 Agentic 混合策略。
评估时可把多个指标加权成一个综合分:
一场宴席检验刀法,一次评估检验策略。分块不是为了切得好看,而是为了让大模型“厨师”最终端出的答案让用户满意。掌握了这套评估方法,你就能在真实项目中从容地选择、比较、迭代你的分块策略。
- R召回率:是否把相关块都捞回来了,对应“该上的菜都上了”。
- P准确率:捞上来的块是否干净,对应“上来的菜都不是客人拒绝的”。
- 综合分 Sα、β 分别给召回和准确打分,再求和,得到这个策略的最终得分。
AI 应用场景
政策文件检索:语义分块让条款“找得准”
阶段:知识库构建与数据预处理
解决的问题:政策文件动辄几十页,条款交错。传统整篇入库时,用户问“新能源补贴的申领条件”,检索结果杂糅了大量背景内容;固定窗口又会把一条完整的申领条款拦腰截断,生成阶段答不到点子上。
具体案例:某政策评估团队用语义分块处理《“十四五”现代能源体系规划》。先把文件按章节拆成段落,再对每个句子做向量编码,计算相邻句相似度,在低谷处切分。提问“充电桩建设的用地审批流程”,系统能返回包含完整条款语义的块,评估人员复核工作量下降约 60%。
医学文献综述:滑动窗口保住证据链
阶段:知识检索与上下文组装
解决的问题:医学文献的结论常在前一段、统计分析在后一段。固定窗口把“结论与证据”拆到两个块里,检索只命中结论块时,大模型拿到“幸存者偏差”却看不到统计口径。
具体案例:肿瘤研究团队用滑动窗口处理 PubMed 摘要,窗口 256 token、重叠率 20%。生成综述时,“5 年生存率提升”这个结论块天然带有上一窗口的入组标准和统计方法,引用时证据链完整。团队将检索命中率从 52% 提升到 78%。
商业决策情报:Agentic 分块应对多变需求
阶段:动态知识管理与智能问答
解决的问题:高管的问题跨度极大:“公司去年整体营收多少”需要粗粒度上下文,而“华东区 Q3 单品销量”需要精确定位。固定一种粒度的分块策略无法两头兼顾。
具体案例:某商业情报系统让 LLM 根据 query 自动选择分块策略:宏观问题读取文档树的粗粒度根节点,细节问题下钻到细粒度叶子节点;系统还会缓存不同粒度的块,降低重复计算。最终知识库检索响应时间保持稳定,而答案引用的“可溯源性”显著提升。
Python 演示
# -*- coding: utf-8 -*-"""演示三种常见分块策略:固定窗口、滑动窗口、语义分块。使用词袋重合度近似语义相似度,避免依赖外部库。"""import re def split_sentences(text): """按中英文句号、感叹号、问号拆分句子。""" parts = re.split(r'[。!?!?]', text) return [p.strip() for p in parts if p.strip()] def tokenize(text): """简易分词:保留中文字符和英文单词。""" return re.findall(r'[\u4e00-\u9fa5]|[a-zA-Z]+', text.lower()) def jaccard_sim(sent_a, sent_b): """用 Jaccard 相似度近似句子语义相似度。""" set_a, set_b = set(tokenize(sent_a)), set(tokenize(sent_b)) if not set_a or not set_b: return 0.0 return len(set_a & set_b) / len(set_a | set_b) class FixedWindowChunker: """固定窗口分块:每 chunk 固定字符长度。""" def __init__(self, chunk_size=100): self.chunk_size = chunk_size def chunk(self, text): return [text[i:i + self.chunk_size] for i in range(0, len(text), self.chunk_size)] class SlidingWindowChunker: """滑动窗口分块:固定长度 + 前向重叠。""" def __init__(self, chunk_size=100, overlap_ratio=0.2): self.chunk_size = chunk_size self.step = max(1, int(chunk_size * (1 - overlap_ratio))) def chunk(self, text): chunks = [] start = 0 while start < len(text): chunks.append(text[start:start + self.chunk_size]) start += self.step return chunks class SemanticChunker: """语义分块:句子嵌入相似度低处切分。""" def __init__(self, threshold=0.25): self.threshold = threshold def chunk(self, text): sents = split_sentences(text) if len(sents) <= 1: return [text] merged = [sents[0]] for i in range(1, len(sents)): sim = jaccard_sim(sents[i - 1], sents[i]) if sim < self.threshold: merged.append('\n--- 语义分块 ---\n') merged.append(sents[i]) chunks = [] cur_chunk = [] for part in merged: if part == '\n--- 语义分块 ---\n': if cur_chunk: chunks.append('。'.join(cur_chunk)) cur_chunk = [] else: cur_chunk.append(part) if cur_chunk: chunks.append('。'.join(cur_chunk)) return chunks def main(): text = ( '语义分块与滑动窗口是RAG系统的关键策略。' '它们决定知识库如何被检索。' '语义分块依赖句子向量之间的相似度。' '相似度突然下降的地方就是切分点。' '滑动窗口则关注上下文连续性。' '每一刀都与上一刀有部分重叠。' '固定窗口实现最简单。' '但可能会切断重要的语义信息。' ) print('原始文档长度:', len(text)) print('\n=== 固定窗口(60字) ===') for i, c in enumerate(FixedWindowChunker(60).chunk(text)): print(f'[{i}] {c}') print('\n=== 滑动窗口(60字, 20%重叠) ===') for i, c in enumerate(SlidingWindowChunker(60, 0.2).chunk(text)): print(f'[{i}] {c}') print('\n=== 语义分块(阈值0.25) ===') for i, c in enumerate(SemanticChunker(0.25).chunk(text)): print(f'[{i}] {c}') if __name__ == '__main__': main()
self.step = max(1, int(chunk_size * (1 - overlap_ratio)))用重叠率算步长,重叠越多,步长越小,切出的相邻块共享内容越多。chunks.append(text[start:start + self.chunk_size])每次都从 start 处切一个固定长度的窗口,模拟“蛋糕刀法”。sim = jaccard_sim(sents[i - 1], sents[i])用 Jaccard 相似度近似句子语义距离,不需要外部向量模型。if sim < self.threshold:相似度低于阈值,说明话题断裂,插入分块标记。for i, c in enumerate(FixedWindowChunker(60).chunk(text)):逐个打印每个块的编号和内容,方便肉眼对比三种策略的差异。
常见误区
误区我一开始把文档都切成 2048 token 的大块,想着上下文越完整答案越准,结果检索老是返回答非所问的段落。
正解块越大,噪声越多,检索相似度被无关信息稀释;而且超出窗口限制还会被截断。理想块应该恰好覆盖“一个完整语义单元”,就像帮厨把整条鱼切段,而不是连着水桶一起下锅。
为何常见:直观上“完整”让人觉得会更准确,忽视了检索相关性是在“片段级”计算的。
误区固定窗口分块快、代码简单,也不用训练模型,大家都这么用,我也一直用。
正解固定窗口无视语义结构,常把句子、公式、条款拦腰切断。对于叙事型和论证型文档,检索质量明显下降。“够用”只在结构化日志类文档上成立。
为何常见:因为实现成本低,容易形成路径依赖;很多教程也以固定窗口为默认示例。
误区语义分块比固定窗口聪明,肯定是所有场景的最佳方案。
正解语义分块需要额外 embedding 计算,对长句子和短文本不稳定,有时会把紧密相关的内容误判为“话题断裂”,而且边界是估计的,并非绝对正确。它更适合语义边界清晰的论述文,不适合代码或表格这类结构化文本。
为何常见:新技术看起来“智能”,容易让人忽略成本与适用条件。
误区为了不丢上下文,我把重叠率设定成 90%,结果知识库膨胀了三倍,检索速度也变慢了。
正解重叠率过高会让同一信息出现在多个块里,不仅浪费存储,还会让检索返回大量内容雷同的块,产生冗余噪声。常见经验是 10%~30%,具体要随文本长度和任务微调。
为何常见:人直觉上认为“多重复=更保险”,没意识到检索去重和存储成本也是关键指标。
误区部署时选好一个分块策略,跑通之后就一直用,从没想过换。
正解分块策略必须跟着下游任务走。任务从“全文摘要”改成“条款检索”,最佳粒度就变了;语料从政策文件换成医学论文,语义边界也变了。定期评估并用评测指标(如 recall@k)回测是必要的。
为何常见:上线后能跑就懒得动,且评估分块对检索质量的影响需要额外工具和指标。
面试题
Q1. 分块的主要目的是什么?
- A.减小文档存储空间
- B.生成语义连贯检索单元← 正确答案
- C.加快模型训练速度
- D.让文档排版更美观
答案:B。分块的核心目的是把长文档切成适合检索和生成的语义单元,就像备菜是为了让每道菜的食材独立完整。减少存储并非主要目的,C、D 与分块无关。
Q2. 哪种分块策略会保留相邻块之间的重叠内容?
- A.固定窗口分块法
- B.滑动窗口分块法← 正确答案
- C.句子语义分块法
- D.章节主题分块法
答案:B。滑动窗口分块让每次滑动的步长小于窗口大小,从而产生重叠区,就像切蛋糕时刀刀带一点上一块的花边。固定窗口、语义分块和主题分块都不会刻意制造重叠。
Q3. 语义分块通常在什么位置切分?
- A.固定字符间隔处
- B.相似度最高的位置
- C.相似度突降的位置← 正确答案
- D.句子数量过半处
答案:C。语义分块先算相邻句子的语义相似度,相似度从高处突然跌到谷底的位置,往往就是话题切换处,所以在这里下刀。固定间隔是固定窗口的做法,与句子数量和相似度峰值无关。
Q4. 滑动窗口分块中,窗口大小 K=200 字符、重叠率 r=50%,步长 S 是多少?
- A.50字符
- B.100字符← 正确答案
- C.150字符
- D.200字符
答案:B。步长 S = K × (1 - r) = 200 × 0.5 = 100 字符。重叠率 50% 意味着每个窗口有一半内容与上一个窗口重复,就像每刀都要带回一半上一块的奶油。
Q5. 递归分块与固定窗口的核心区别是什么?
- A.递归按空格切分
- B.先粗后细逐层分级← 正确答案
- C.只用于英文文档
- D.不考虑文档结构
答案:B。递归分块按“章→节→段落”逐层分级,像冰箱分层收纳;固定窗口是一次性按固定长度切完。递归分块并不限定语言,也恰恰依赖文档结构。
Q6. 给定 6 个相邻句子的余弦相似度序列 [0.82, 0.79, 0.31, 0.77, 0.83],语义分块最可能在哪两个句子之间切分?
- A.第 3 和第 4 句之间← 正确答案
- B.第 1 和第 2 句之间
- C.第 5 和第 6 句之间
- D.第 2 和第 3 句之间
答案:A。切分点应选在相似度谷底,即 0.31 所在位置(第 3 与第 4 句之间)。其他位置的相似度都在 0.77 以上,说明话题连贯,不适合下刀。
Q7. 以下哪类任务最适合使用细粒度的分块?
- A.全文观点概括
- B.精确条款检索← 正确答案
- C.长篇小说梗概
- D.大型图片识别
答案:B。精确条款检索需要“一找就中”的短块,细粒度能提高召回精度。全文概括和小说梗概需要粗粒度上下文,图片识别与文本分块无关。
Q8. 下列哪些属于常见的分块策略?
- A.固定窗口分块← 正确答案
- B.滑动重叠分块← 正确答案
- C.递归分级分块← 正确答案
- D.随机抽样分块
答案:ABC。固定窗口、滑动重叠、递归分级都是工程中常用的分块策略,各有适用场景。随机抽样会破坏文档结构,不构成有效的分块策略。
Q9. 关于滑动窗口的重叠率,以下说法正确的有?
- A.上下文更连贯← 正确答案
- B.冗余存储更多← 正确答案
- C.重叠率越高越好
- D.检索噪声更大← 正确答案
答案:ABD。重叠率提高会增强上下文连贯性(A),也会让同一内容出现在多个块中,造成存储冗余(B)和检索噪声(D)。但重叠率并非越高越好,“越高越好”(C)是错误的极端化理解。
Q10. 语义分块的实现步骤包含哪些?
- A.句子向量化编码← 正确答案
- B.计算相邻相似度← 正确答案
- C.在相似度谷底切分← 正确答案
- D.按首行缩进切分
答案:ABC。语义分块的三步是:把句子编码成向量、计算相邻句子的相似度、在相似度谷底处切分。按首行缩进切分是排版规则,与语义计算无关。