
在RAG(检索增强生成)知识库系统开发中,我们知道文档解析质量、分片策略、嵌入模型、召回策略、重排模型、查询优化等等这些影响了检索召回率和最终问答的质量。
分片策略是承上启下的关键纽带,它直接决定了文档如何被切转化为一个个 Chunk(文本块)。切分过大容易引入噪声,切分过小则会导致语义碎片化破坏了原文的结构逻辑,导致关键上下文在检索阶段被漏掉。
文档解析(如PDF)质量是RAG系统后续环节的基石,但本文不进行这方面的探讨,很多开源项目如MinerU都在推进这方面的质量,我们假设文档都能很好的被转为为Markdown。
目前RAG系统中比较常用的分片策略有(不代表所有):
- 固定大小切片:把文本按照固定长度/字符暴力切分,一般设置overlap(重叠)区域,每个chunk都带点冗余。
- 分隔符递归切片:比如先按段落切分,再按句子切分,最后按单词切分。
- 按文档结构切(标题、章节、代码块):算是方案
2的增强版本,比如标题、表格、代码块等就是天然的语义边界 - 按语义相似度切(基于向量嵌入或大模型),核心思想是语义主题发生变化的时候才切,但需要额外引入嵌入模型或推理模型,计算成本高,而且和模型能力也息息相关。
- 父子块双层策略(parent-child):父块保留完整上下文,子块负责向量语义匹配,这个其实可以结合上面的任意策略来实现。
当然分片策略也在不断演进,还有一些新的切片模式这里不再展开,我们只看WeKnora是如何做的,这在RAG实践中很有指导意义,甚至可以直接借鉴。(关于WeKnora的项目介绍:腾讯开源的企业级RAG(支持Wiki)知识库产品WeKnora,1.8万Star 开箱即用)
它的分片策略更偏方案3、方案5,细分分片策略为:
- 标题切分(heading):按Markdown标题切分,适合标题结构明显的文档
- 启发式切分(heuristic):按启发式标记切分(分页符、编号章节、"第X章"等7类标记)
- 递归分隔符切分(legacy):兜底策略,任何不命中的策略都会回到这层
- 父子分片(parent-child):父块用来存上下文完整信息,子块存入向量库用来检索,召回的时候映射会父片。
系统会通过分析文档结构依次选择合适的策略,不合格就降级到下一种,直到legacy模式兜底,同时用正则保护表格、公式、代码块等结构元素不被切断。

下面逐层拆解具体实现:
- 切分粒度
- 结构保护(公式、表格、代码块等)
- 保护单元的处理策略
- 三层策略链
- 递归分隔符切分(legacy策略)
- 标题切分器
- 启发式切分器
- 父子块双层分片
- 图片chunk:分片后处理
WeKnora的主要分片策略是先分析文档结构,再选择最合适的切分策略,最后用递归分隔符兜底。如果策略未设置或显式设为legacy / recursive,则直接走递归分隔符切分。
切分粒度
chunk_size = 512
chunk_overlap = 80
这个参数影响所有策略,块太长或者太短都影响质量,512也是常用的Chunk尺寸配置,最多1024。
这里chunk_size=512更准确说是默认或者理想目标尺寸,不是严格硬上限。可以把它理解成"默认希望切在512左右",而不是"必须卡死在512以内"。如果某个chunk最后切成720,在当前实现里也是合法的,其实系统隐形的上限是chunk_size * 2即1024。
chunk_overlap是相邻chunk之间的重叠字符数:也就是切分时新chunk会用上一个chunk的尾部80左右字符开始,主要避免边界处的上下文被截断,做一些冗余处理。

overlap在不同策略下的生效范围不一样:递归分隔符切分在所有chunk之间都应用,启发式切分在边界段之间应用,标题切分只在section内部做递归细分时才应用,section与section之间不重叠。
结构保护(公式、表格、代码块等)
分片前会先扫一遍文档的结构特征,通过两层机制识别需要保护的元素:

第一层:正则表达式识别结构元素
分片器内置了6类受保护的正则表达式,在分片前扫描全文,标记出所有需要保护的结构内容:
protected_regex = [
r"\$\$[\s\S]*?\$\$", # 数学公式(LaTeX风格)
r"!\[.*?\]\(.*?\)", # 图片(Markdown语法)
r"\[.*?\]\(.*?\)", # 链接(Markdown语法)
r"[ ]*(?:\|[^|\n]*)+\|[\r\n]+\s*(?:\|\s*:?-{3,}:?\s*)+\|[\r\n]+", # 表头
r"[ ]*(?:\|[^|\n]*)+\|[\r\n]+", # 表体
r"```(?:\w+)[\r\n]+[^\r\n]*", # 代码块
]
第二层:表头跟踪器(HeaderTracker)
表头跟踪器通过配置类定义表头块的开始和结束模式:
headerTrackerHook{
startPattern: `^\s*(?:\|[^|\n]*)+[\r\n]+\s*(?:\|\s*:?-{3,}:?\s*)+\|?[\r\n]+$`,
endPattern: `^\s*$|^\s*[^|\s].*$`,
priority: 15,
}
Markdown表格由表头行、分隔行和表体行组成。
跟踪器的作用不是阻止表格被切分,而是记住当前表的表头:当表格被切到多个chunk时,每个chunk开头都会自动带上表头,保证每个chunk都有列名上下文。注意这里的表头长度也计入chunk_size预算。这个机制发生在底层的SplitText递归分隔符逻辑里,标题切分和启发式切分本身不直接做表头跟踪,但在把超大章节/块下沉到SplitText分割时,这个机制会生效。
保护单元的处理策略
数学公式、图片、链接、表格(表头、表体)、代码块这6类正则标记出的保护单元,在装箱阶段作为不可分割的原子单元参与合并。
注意: 表格的保护粒度是行级别: 正则把表头+分隔行作为一个区间(span)、每行数据各作为一个区间,保证行内部不被切断,但整张表不是原子单元。
普通文本按分隔符切分,保护单元整体保留,最终一起装进chunk入向量库。这意味着一个几千字符的代码块或大表格,可能直接成为一个远超chunk_size目标的超大chunk。系统选择保护内容完整性优先于大小约束。
保护单元的上限是7500字符,超过这个长度的块(比如很长的代码块)会直接在换行或空格处强制切开,不再当原子处理,避免单个保护单元撑出过大的chunk。代价是几千字符的大chunk进向量库后,嵌入(embedding)模型可能截断输入,匹配精度也会下降。实际文档中这类超大保护单元不多,真遇到了更多靠BM25关键词检索命中。
三层策略链
文档结构分片策略会先走一轮全文档结构分析(profiling),再根据分析结果组装策略链,按优先级依次尝试。

第一步:结构扫描 (和上面的表格、公式、图片分析不同)
profiler对文档做一次单遍扫描,收集所有结构信号:Markdown标题数量、编号章节(2.3 Methods)、中/德/英文章节标记(第一章、Chapter 3、Kapitel 5)、全大写短行、视觉分隔线(---、===)、换页符\f、页脚(Page 3 of 10)、连续空行,以及文档语言(取前4096字节采样;CJK与Latin字符占比都达到15%判定为混合语言,CJK占比超过30%判定为中文,否则再额外检查德语特征,最后回落到英文)。
第二步:组装策略链
根据扫描结果,再决定哪些策略有资格进入候选链。策略有固定优先级:heading最高,heuristic次之,legacy永远兜底。
系统从高到低检查资格,满足条件的才加入链,不满足的直接跳过:
Tier 1: heading(标题切分) ← 需满足3个条件,优先级最高
Tier 2: heuristic(启发式切分) ← 需满足1个条件,次优先
Tier 3: legacy(递归分隔符切分)← 无条件,永远在链尾兜底
进入heading策略需要同时满足三个条件:
- Markdown标题 >= 3个
- 标题行数占总行数的比例超过0.5%(每200行至少1个标题)
- 存在可识别的标题层级作为文档骨架
策略会优先选出>=3次的标题层级(如H1出现了5次)作为主切分骨架,如果没有任何标题层级>=3次,就用最深的标题层级,这样小文档(可能只有H1加几个H2)也能走标题切分。
进入heuristic策略只需满足一个条件:
- 启发式标记总数≥5
- 存在换页符
- 存在章节标记(基于中/德/英文识别)
最终产出的策略链可能是[heading, heuristic, legacy]、[heading, legacy]、[heuristic, legacy],或者最坏情况直接是[legacy]。
不管哪种组合,legacy永远在最后兜底
第三步:逐级尝试、验证、降级
满足条件的策略进入链后,系统按优先级从高到低逐个执行。每个层级(tier)的输出都要过质量验证——验证通过就直接返回,不再往下走;验证失败才降级到下一个层级:
遍历 chain 中的每个 tier:
→ 执行该 tier 的切分逻辑
→ 产出 chunks
→ 质量验证(5项检查)
├─ 通过 → 返回结果,流程结束
└─ 失败 → 记录拒绝原因,降级到下一个 tier
质量验证包含5项检查:
| 检查项 | 条件 | 拒绝原因 |
|---|---|---|
| 空结果 | chunk数为0 | 该策略没有产出任何chunk |
| 单chunk大文档 | 只有1个chunk且文档超过2倍chunk_size | 切分完全没有生效 |
| 碎片化 | 超过25%的非末尾chunk不足50字符 | 碎片太多,语义信息被切碎 |
| 过度碎片化 | 所有chunk都不到chunk_size的1/4 | 块太小,没有完整语义单元 |
| 超大chunk | 最大chunk超过2倍chunk_size | 块太大,向量匹配精度下降 |
验证器设计的比较宽松:只拒绝明显有问题的输出,接受合理变异,这避免了在层级之间频繁振荡。
这里也能看出512不是硬上限:默认配置下,策略验证真正拿来判断"明显过大"的线是2 * chunk_size = 1024。也就是说,向量chunk的设计目标是512左右,而1024是"过大警戒线",一个720字符的chunk虽然偏大,但仅凭长度这一项,不会触发验证拒绝。
legacy策略即使验证不通过也会返回结果(它是策略链尾的最终兜底),保证分片器永远不返回空。所以1024也不是系统绝对硬上限,它更像是前面几个层级的质量警戒线。再往下,legacy内部还有更靠后的安全兜底,会继续强拆极端超大的块。
递归分隔符切分(legacy策略)
这是策略链的最后一层,也是标题切分和启发式切分内部做细分时共用的底层逻辑。

separators = ["\\n\\n", "\\n", "。"]
这是最小分隔符集合,实际使用时通常还会加上!、?、;、;等标点。
我记得LangChain的分片策略就是这套,其设计偏英文环境。中文场景下\n不太能代表句子边界,。?!这些标点全角符号的标点也是句子分隔。
递归的含义是:先用第一个分隔符
\n\n把文档切成段落(或者说大块),检查每段是否超过chunk_size;没超过的直接输出,超过的用下一个分隔符\n继续切;还超过的再用。切;仍然超过的就按chunk_size硬切。每层切出来的chunk之间都带overlap。
标题切分器和启发式切分器遇到超大章节/块时,也会把超限部分交给这层逻辑做细分,如果所有分隔符都切不动,最终会按chunk_size硬切,保证不超限。
标题切分器
确定主切分层级后,标题切分器按这个层级的标题位置把文档切成章节——每个标题到下一个标题之间是一个章节。

每个章节(section)在切分时会同步构建标题面包屑(breadcrumb)。
系统维护一个6层栈记录标题层级,遇到##时清空更深层级,输出# Chapter 1\n## Section 2\n### Subsection格式的面包屑(换行分隔,保留原始#前缀)。
面包屑存为chunk的ContextHeader字段,不占Content正文空间,但判断章节能否放进单个chunk时,面包屑长度算在chunk_size预算内。
章节太大进入legacy策略细分时,子chunk的大小预算不再扣面包屑。向量化时面包屑文本拼到文本前面,向量检索因此能同时感知文本内容和文档位置。
章节正文接近chunk_size时,面包屑可能把它推过阈值,导致本可以单chunk输出的章节被legacy重新切分。这是为了让chunk粒度贴近目标尺寸。
FAQ、变更日志(changelog)这类标题多但内容短的文档,相邻的小chunk会在共享面包屑下合并,避免碎片化。

面包屑不写入数据库,但是拼接到chunk中存入向量库,所以它可以影响"能不能命中"。
启发式切分器
如果文档标题数量不够或密度太低,但文档里有分页符、"第X章"这类章节信息时,策略会降级到启发式(heuristic)策略。
其逻辑本质上和标题切分器一样是规则驱动,没有语义理解或模型推理,只是用正则表达式识别另一类结构线索(非Markdown的视觉标记)来找切分边界。
进入这个策略只需满足一个条件:启发式标记总数≥5,或存在换页符、中/德/英文章节标记。(备注:前提是heading模式未触发)。

启发式标记共7类,按优先级从高到低。同一份文档里多种标记可以共存,系统会一次扫描全部收集,同一位置出现多个标记时保留优先级最高的:
| 优先级 | 类型 | 匹配示例 |
|---|---|---|
| 100 | 换页符\f | PDF转换后的分页标记 |
| 90 | 编号章节 | 2.3 Methods 、1.1.2 系统架构 |
| 85 | 中/德/英文章节 | 第一章 概述 、Chapter 3、Kapitel 5 |
| 70 | 全大写标题 | INTRODUCTION 、RELATED WORKS: |
| 60 | 视觉分隔线 | --- 、===、*** |
| 50 | 页脚 | Page 3 of 10 、页码 3/10 |
| 40 | 连续空行 | 3个以上换行符 |
找到所有边界后,先过滤掉落入保护区间的内容(公式、图片、链接、表格、代码块不能被切断),然后做贪心装箱:
- 从文档开头逐段累积内容
- 累积到chunk_size就输出一个chunk
- 两个边界之间的区域本身超过chunk_size时,递归交给legacy切分器做内部细分
- flush后新chunk的起始位置会回退overlap个字符,并对齐到最近的语义边界
产出的chunk继续做质量验证:碎片化太严重(超25%的非末尾chunk不足50字符)或超尺寸(最大chunk超2倍chunk_size即1024)都会被拒绝,降级到legacy策略。
其实整体流程和标题切分器一致: 找边界→按边界切分→超大块交给legacy→产出chunks→过策略链验证。区别在于边界来源(视觉标记vs标题层级)和装箱策略(贪心装箱+overlap对齐vs按section直切+小chunk合并)。
父子Chunk双层分片
WeKnora的父子chunk双层分片(parent-child)策略默认未开启,逻辑是把文档先切成比较大的父块,再把父块切成更小的子块。

parent_chunk_size = 4096
child_chunk_size = 384
child_overlap = child_size / 5 ≈ 20%(硬编码,不可单独配置)
切分步骤:先按4096切父块,再对每个父块按384切子块,两个值都是目标尺寸,不是硬上限。
父块和子块的切分都走策略链(heading/heuristic/legacy)逻辑,不是固定只走legacy兜底,但是按照配置子块大概率都会落入legacy策略处理。
子块按父块粒度独立切分,边界:如果一个父块只切出一个子块且内容完全相同,父块不存储,子块退化为独立chunk。
检索命中文本子块后,通过映射关系找出父块内容,在合并阶段使用父块内容并入上下文。
图片chunk:分片后处理
文档中图片、图表等也是重要的信息。
在文本分片完成后,还会从文档中提取图片,经过OCR和图片描述模型处理,生成两种额外的chunk类型:image_ocr(图片中的文字)和image_caption(图片内容描述)。

这两种chunk是独立的chunk,各自单独存储、单独向量化,不会合并进text父块的正文里。它们通过ParentChunkID指向包含该图片引用的text父块,召回时可以通过这条映射找回上下文。
图片chunk的创建独立于文本分片策略,不走上面说的三层策略链。无论文本用哪种策略切分,图片都走统一的OCR+描述流程。
最后
通过对WeKnora分片策略的拆解,我们可以学习到它的核心是一条自适应策略链:
扫描文档结构:按标题→启发式→递归分隔符的优先级逐级尝试 提取受保护元素:公式、表格(分表头和表体)、代码块等结构元素,保证这类元素不会被拆分 父子双层分片:把检索粒度和上下文完整性拆开,小块负责向量匹配,然后映射到大块用来加入上下文
不管是标题切分还是启发式策略都依赖文档自身的结构质量, 缺乏结构的文档最终会落到递归分隔符切块模式。所以切片策略固然重要,但是原始文档和解析后的质量也很重要。
WeKnora的分片策略和颗粒度我感觉已经可以支撑大部分知识库应用场景了,而且项目还有知识图谱,本篇暂未展开。
如果你也在开发自己的RAG知识库系统,这套分片策略非常具有参考价值,大部分场景已经够用了。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费】

426



被折叠的 条评论
为什么被折叠?



