RAG分块策略完全指南:为什么你的知识库总是答非所问?
RAG效果差八成出在分块上。本文用原理加代码讲透固定长度、语义分块、父子分块、上下文增强等策略的取舍,并给出选型决策表。
做过 RAG 知识库的人几乎都踩过同一个坑:明明文档里写着答案,模型却答不上来,或者答得牛头不对马嘴。 大多数人第一反应是换个更强的模型,或者调高检索 top-k,结果收效甚微。真相往往更朴素——问题出在**分块(chunking)**上。
一、分块为什么是 RAG 的命门
RAG 的链路是:文档 → 分块 → 向量化 → 检索 → 拼进提示 → 生成。分块处在最上游,它决定了什么信息被”打包”成一个检索单元。分块一旦切错,后面再好的模型也救不回来:
- 切得太碎:单块信息不完整,模型拿到的是”半句话”,无法推理。
- 切得太大:一块里塞了多个主题,向量被平均化,检索时匹配度下降。
- 切在错误位置:把”结论”和”支撑它的条件”切到两个块里,检索到结论却缺前提。
二、主流分块策略逐一拆解
1. 固定长度分块(Fixed-size) 按字符或 token 数硬切,通常带一点重叠(overlap)。优点是简单、快、可控;缺点是经常在一句话中间切断,语义破碎。 适用:日志、结构化程度低的短文本、快速原型。
2. 递归分块(Recursive Character) 按”段落 → 句子 → 词”的优先级递归切分,尽量在自然边界断开。这是 LangChain 默认策略,也是最稳妥的基线。 适用:绝大多数通用文档。
3. 语义分块(Semantic Chunking) 计算相邻句子的 embedding 相似度,在相似度骤降处切分——那里往往是话题转折点。效果通常优于固定分块,但成本高(要先算向量)。
4. 结构感知分块(Markdown/HTML Aware) 按标题层级切分,让每个 chunk 携带所属的标题路径。对技术文档、法律合同、说明书尤其有效。
5. 父子分块(Parent-Child) 用小块做检索(精准匹配),但把小块所在的”父块”(更大上下文)喂给模型。这解决了”检索要小、生成要大”的矛盾,是目前召回质量最高的方案之一。
6. 上下文增强分块(Contextual Retrieval) 给每个 chunk 前面补一段由模型生成的”这段在整篇文档中的位置和主题”说明,再去做向量。据公开测试可显著降低检索失败率。
三、按文档类型选策略(决策表)
| 文档类型 | 推荐策略 | 块大小 | 重叠 |
|---|---|---|---|
| 技术文档/API手册 | 结构感知 + 父子 | 300~600 token | 10% |
| 法律合同 | 结构感知(按条款) | 500~800 | 0 |
| 通用文章/百科 | 递归 | 400~600 | 15% |
| 会议纪要/FAQ | 语义分块 | 200~400 | 20% |
| 表格/报表 | 按行组 + 表头注入 | 视行数 | — |
四、三个常被忽略的细节
1. 给每个块加上元数据。 标题路径、来源文件、页码、时间戳。检索时可以按元数据过滤,也能在生成时给模型提供出处。
2. 保留表格和代码的完整性。 表格一旦被跨块切开就彻底失效。预处理时应把表格转成 Markdown 并作为独立块。
3. 分块要为检索反推。 问自己:“用户会用什么话提问?我的块里有没有对应这句话的语义锚点?“如果答案是否定的,就该考虑改写或增强块内容。
五、一个可复用的递归分块示例
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=75,
separators=["\n## ", "\n### ", "\n\n", "\n", "。", "!", "?", " ", ""],
)
chunks = splitter.split_text(doc)
注意中文场景要显式把中文标点加进 separators,否则会退化成按空格硬切。
结语
RAG 效果的提升,往往不靠换模型,而靠把上游的分块做对。先做对分块,再谈检索优化,最后才是生成调优。 如果你的知识库今天答非所问,别急着骂模型,先去看看你的 chunk 长什么样。