分块策略:chunk_size / overlap 选择、语义分块

一句话:分块是 RAG 的”第一公里”,决定了检索质量的天花板——模型再强,喂进去的”碎肉”不对,照样答非所问。


一、为什么必须分块?——“自助餐装盘”类比

想象你去吃自助餐,有一整头烤牛(完整文档)。你不能把整头牛塞嘴里(LLM 上下文窗口有限),也不能随便剁成肉泥(语义全断),而是要切成大小合适的牛排块(chunk),每块都带点肥瘦相间的完整口感(上下文保留),这样吃起来才爽(检索精准)。

分块的三个硬约束:

  1. 模型上下文窗口限制:Embedding 模型通常只吃 512~8192 tokens,LLM 也有输入上限
  2. 检索精度要求:向量检索是”局部匹配”,chunk 太大则关键信息被稀释,太小则上下文断裂
  3. 计算成本控制:chunk 越多 = 向量存储越大 = 检索越慢 = API 花钱越多

Chunking 是在上下文完整性、检索精度、计算成本三者之间做工程取舍。


2.1 chunk_size(块大小)——“牛排切多大”

chunk_size 是每个文本块的长度(通常以 token 或字符为单位)。它是 RAG 系统中最核心的超参数之一,直接决定检索的召回率和准确率。

太大的问题

问题通俗解释实际后果
噪声稀释一块牛排里混了太多肥肉(无关内容)关键信息被向量空间”平均化”,检索精度下降
上下文窗口浪费给 LLM 的 prompt 被废话占满生成容易跑偏、啰嗦,甚至幻觉
计算开销大每块更大 = Embedding 更慢成本飙升

⚠️ 真实案例:财报中”净利润 1.2 亿”这个关键数字,如果 chunk_size=2048,它就被淹没在 2000 token 的冗长描述里,向量检索根本定位不到。

太小的问题

问题通俗解释实际后果
语义腰斩”用户登录失败”被切成”用户登录” + “失败”检索到了但 LLM 看不懂完整意思
上下文断裂前提和结论分到不同块LLM 只看到结论不知道为什么
chunk 数量爆炸10 万字文档切成 200 token 的块 = 500+ 块向量库膨胀,检索变慢

黄金区间与经验法则

文档类型推荐 chunk_size理由
通用文档/百科/说明书512~1024 tokens信息密度适中,中等块兼顾上下文和精度
法律/合同/强结构化文档256~512 tokens条款独立性强,小块即可完整表达一条法规
长文章/故事/报告1024~2048 tokens叙事连贯性强,需要更大上下文
问答类/FAQ400~600 tokens问答对天然边界清晰
技术文档/代码200~300 tokens函数/配置步骤通常较短

💡 工程金句:“不要为了 5% 的召回率提升,付出 10 倍的 Embedding 成本。“

三板斧验证法

  1. 模型边界法:chunk_size ≤ Embedding 模型上限(如 512)× 90%(留 10% Buffer)
  2. 知识单位法:统计业务文档中独立知识点的平均 token 数(如法律条文”第 3 条”平均 200 tokens)
  3. 黄金文档测试:在 256 / 512 / 1024 三种尺寸下测试,检查答案是否总在 Top-3

动态评估法(进阶)

51CTO 实测文章通过 70 组不同 chunk_size × overlap 的组合,用 Ragas 评估三个指标:

  • 上下文召回率:最佳组合 chunk_size=768, overlap=0.6,得分 0.78
  • 上下文相关性:最佳组合 chunk_size=512, overlap=0.5,得分 0.8
  • 答案正确性:最佳组合 chunk_size=768, overlap=0.6,得分 0.7

关键发现:中等 chunk_size(512~896)在所有指标上平均表现最好,过大或过小都会导致性能下降。

查询类型也会影响最优 chunk_size

查询类型推荐 chunk_size原因
事实型查询(“公司的注册地址”)256~512 tokens答案集中在一两句话,小块精准命中
复杂分析查询(“分析 Q3 营收增长原因”)1024+ tokens需要跨越多段落的上下文推理

📌 AI21 实证研究:不存在通用最优 chunk_size,同一语料库中不同查询的最优粒度差异可达 5 倍。


2.2 chunk_overlap(重叠窗口)——“牛排之间留点拼接缝”

overlap 是相邻两个 chunk 之间重复的内容长度。它的存在是为了防止关键信息恰好落在切分点上被”腰斩”。

为什么需要 overlap?

举个残酷的例子:

原文:"用户数据加密采用 AES-256 算法,密钥存放在 HSM 中"
 
如果 chunk_size=10, overlap=0:
  Chunk 1: "用户数据加密采用"
  Chunk 2: "AES-256 算法,密钥存放在 HSM 中"
 
用户问:"数据用什么加密?" → Chunk 1 被检索到,但答案被切断了
用户问:"密钥存在哪?" → Chunk 2 被检索到,但缺少"数据加密"的上下文
 
有了 overlap=5:
  Chunk 1: "用户数据加密采用 AES-256"
  Chunk 2: "采用 AES-256 算法,密钥存放在 HSM 中"
  → 两个 chunk 都包含完整关键信息

💡 一句话理解:overlap 相当于给 LLM 多一次检索命中的机会——关键内容在多个 chunk 里出现,保证上下文语义完整。

经验值

来源推荐 overlap 比例对应数值(chunk_size=512)
工程通用经验chunk_size 的 10%~25%50~100 tokens
问答类场景50~80 tokens(固定值)50~80 tokens
动态评估最优chunk_size 的 50%~60%256~307 tokens(偏高)

overlap 通常设为 chunk_size 的 10%~20%。太小起不到防切断作用,太大(超过 30%)会导致大量内容重复,引发 Top-K 塌缩和存储膨胀。

overlap 太大的副作用

副作用解释后果
Top-K 塌缩检索结果全是重复段落5 个结果都是”系统错误”开头的 100 字,多样性为零
存储膨胀20% overlap = 向量库体积 +20%成本增加
噪声干扰重复内容干扰模型判断检索精度反降

工程补救方案

  1. Jaccard 相似度去重:相似度 >0.8 的 chunk 自动合并
  2. MMR(最大边际相关性):检索时强制结果多样性,避免返回高度相似的 chunk

三、分块策略全景图——“从蛮力到智能”

flowchart TD
    A[原始文档] --> B{文档有清晰结构?}
    B -->|是 Markdown/标题/代码块| C[结构感知分块<br/>按标题/章节切分]
    B -->|否| D[Recursive 递归切分<br/>按分隔符层级切分]
    C --> E[设置 chunk_size + overlap]
    D --> E
    E --> F{检索质量达标?}
    F -->|是| G[上线]
    F -->|否| H{文档长度 ≤ 8K?}
    H -->|是| I[Late Chunking<br/>先嵌入后切分]
    H -->|否| J{预算充足?}
    J -->|是| K[Contextual Retrieval<br/>LLM 生成上下文]
    J -->|否| L[语义分块<br/>句子相似度断点]
    I --> M[用 Ragas 评估]
    K --> M
    L --> M
    M --> F

策略层级递进(从简单到复杂):

  1. 朴素型(Fixed-Size):按固定 token 数切分 → 简单粗暴,可能腰斩句子 → 适合快速验证
  2. 启发型(Recursive):按分隔符层级递归切分(nn → n → 句号 → 空格)→ ✅ 工业界 Baseline
  3. 结构感知型(Structure-Aware):按 Markdown 标题/代码块/表格切分 → 保留文档结构
  4. 父子索引(Parent-Child):小块检索,大块返回 → 兼顾检索精度和上下文完整性
  5. 语义型(Semantic):按语义相似度找断点 → 上下文保留好但成本高
  6. Late Chunking:先嵌入后切分 → 2025 前沿方案
  7. Contextual Retrieval:LLM 为每块生成上下文说明 → 2025 前沿方案

策略对比表

策略适用场景工程成本核心风险
朴素型(Fixed-length)快速验证/PoC语义腰斩
启发型(Recursive)工业界 Baseline依赖分隔符质量
结构感知型Markdown/代码/技术文档需解析文档结构
父子索引(Parent-Child)长文档/产品手册中高需额外存储父级 chunk
语义型(Semantic)技术文档/复杂文本成本爆炸、延迟高
Late Chunking长文档(8K tokens 以内)依赖长上下文 Embedding 模型
Contextual Retrieval超长文档/复杂语境很高每块调一次 LLM,成本可观

Baseline 先行——永远从 Recursive Character + 512 Tokens + 15% Overlap 开始,工业界 90% 的问题可以解决。


4.1 核心思想——“按意思断句,而不是按字数断句”

传统分块按固定长度或分隔符切,就像用尺子量着切牛排——不管纹理走向,到点就切。语义分块则是顺着肉的纹理切——哪里语义发生了突变,就在哪里下刀。

4.2 实现原理

处理流程(5 步):

  1. 把文档按句子切分(用 NLTK / spaCy / LangChain 的 sentence splitter)
  2. 对每个句子生成 Embedding 向量
  3. 计算相邻句子向量的余弦相似度
  4. 当相似度低于阈值(如 0.6)时,认为语义发生了”突变”,在这里切分
  5. 相似度高的相邻句子归入同一个 chunk

判断逻辑:

句子1 → Embedding1
句子2 → Embedding2 → cos_sim(emb1, emb2) = 0.85 → 连贯,归入同一 chunk
句子3 → Embedding3 → cos_sim(emb2, emb3) = 0.45 → 突变!在这里断开
句子4 → Embedding4 → cos_sim(emb3, emb4) = 0.78 → 连贯,归入新 chunk

4.3 代码思路

from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
 
def semantic_chunking(text, similarity_threshold=0.7, min_chunk_size=50):
    # 1. 按句子切分
    sentences = split_into_sentences(text)
    # 2. 对每个句子生成 Embedding
    model = SentenceTransformer('all-MiniLM-L6-v2')
    embeddings = model.encode(sentences)
    # 3. 计算相邻句子的余弦相似度
    chunks = []
    current_chunk = [sentences[0]]
    for i in range(1, len(sentences)):
        sim = cosine_similarity([embeddings[i-1]], [embeddings[i]])[0][0]
        if sim < similarity_threshold:
            # 语义突变 → 断开
            chunks.append(' '.join(current_chunk))
            current_chunk = [sentences[i]]
        else:
            # 语义连贯 → 继续累积
            current_chunk.append(sentences[i])
    if current_chunk:
        chunks.append(' '.join(current_chunk))
    return chunks

4.4 LangChain 中的语义分块

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
 
text_splitter = SemanticChunker(
    OpenAIEmbeddings(),
    breakpoint_threshold_type="percentile",  # 或 "standard_deviation"
    breakpoint_threshold_amount=95,
)
chunks = text_splitter.split_text(document_text)

断点判定方式(breakpoint_threshold_type):

  • percentile:按相似度分布的百分位数判定(默认 95%)
  • standard_deviation:按标准差判定
  • interquartile:按四分位距判定

4.5 语义分块的优缺点

维度评价
优点上下文保留极好;chunk 边界天然贴合语义;减少”腰斩”问题
缺点需要额外的 Embedding 计算,成本高;对 Embedding 模型质量敏感;chunk 大小不可控;延迟高
适用技术文档、学术论文等语义边界清晰的场景
不适用FAQ(天然有边界)、实时系统(延迟要求高)

⚠️ 工程警示:“智能切分并非银弹!需离线批处理、计算开销大,且对 Embedding 模型稳定性高度敏感。“


5.1 Late Chunking(延迟切分)——“先看书再撕页”

传统分块(Early Chunking)的问题:先撕书再分别阅读 → 每页都不知道其他页写了什么。

Late Chunking 反其道而行:先把整本书读完(整体 Embedding),再在向量空间里”撕页”(切分向量)。

对比流程:

传统 Early Chunking:
  文档 → 先切分 → 逐块 Embedding → 每块向量只含局部语义 ❌ 上下文丢失
 
Late Chunking:
  文档 → 整篇 Embedding(需长上下文模型)→ 在向量空间切分 → 每块向量含全局语义 ✅

核心步骤:

  1. 整体编码:将完整文档一次性输入支持长上下文的 Embedding 模型(如 Jina AI 支持 8192 tokens),获取每个 token 的向量表示
  2. 延迟切分:在向量序列上按句子/段落边界切分
  3. 片段池化:对每个片段的 token 向量做平均池化,生成最终 chunk 向量

效果:每个 chunk 的向量都”看过”全文,携带全局上下文信息。实验表明,原本与查询”Berlin”余弦相似度只有 0.708 的片段,Late Chunking 后提升到 0.825。

限制:依赖长上下文 Embedding 模型,文档超过模型窗口时仍需拆分。

5.2 Contextual Retrieval(上下文检索)——“给每块贴个说明书”

Anthropic 在 2024 年提出,核心思想:用 LLM 为每个 chunk 生成一段上下文说明,拼接到 chunk 前面再 Embedding。

举例:

  • 原始 chunk:“公司收入较上季度增长了 3%”
  • LLM 生成的上下文:“本段摘自 ACME 公司的 2023 年第二季度财报,上季度营收为 3.14 亿美元。”
  • 最终 chunk:“本段摘自 ACME 公司的 2023 年第二季度财报…公司收入较上季度增长了 3%”

效果惊人:引入 Contextual Retrieval 后,检索失败率下降约 49%,配合 ReRank 总体检索错误率减少 67%。

成本优化:利用上下文缓存(如 Claude 的 prompt caching),批量生成上下文的成本可降至原来的 10% 以内。

5.3 两种策略对比

维度Late ChunkingContextual Retrieval
核心思路先嵌入后切分先切分再补上下文
上下文来源Embedding 模型的注意力机制LLM 生成的说明文本
适用文档长度≤ Embedding 模型窗口(如 8K)任意长度
额外成本一次长文本 Embedding每块一次 LLM 调用
效果提升相似度提升 ~12%检索失败率下降 ~49%
成熟度较新,依赖长上下文模型Anthropic 已验证,工程可落地

6.1 决策流程

逐步决策逻辑:

  1. 文档有清晰结构(Markdown/标题/代码块)?→ → 结构感知分块(按标题/段落切分)
  2. 是快速验证/PoC?→ → 朴素分块(Recursive + 512 tokens)
  3. 检索质量不达标?→ → 保持当前方案
  4. 检索质量不达标?→ → 文档 ≤ 8K tokens?→ → Late Chunking(先嵌入后切分)
  5. 文档 > 8K tokens?→ 预算充足?→ → Contextual Retrieval(LLM 生成上下文)
  6. 预算不足?→ 语义分块(句子相似度断点)
  7. 所有方案 → 设置 chunk_size + overlap → 用 Ragas/TruLens 评估 → 指标达标则上线

6.2 参数选择速查表

场景chunk_sizeoverlap分块策略理由
快速验证51250~100Recursive工业界 Baseline,覆盖 90% 场景
FAQ/客服400~60050~80句子分块问答对天然有边界
法律合同256~51250~100结构感知按条款切,保持法律效力
技术文档200~30030~60结构感知按函数/配置步骤切
长篇报告1024~2048100~200父子索引需要大上下文做推理
学术论文512~102450~100层级分块按章节/段落分层
超长文档(高级)512~102450~100Contextual RetrievalLLM 补充上下文

6.3 元数据——“不是附加项,而是系统粘合剂”

💡 “没有元数据的 chunk 是失去户口的流浪汉,召回后难以二次定位。”

每个 chunk 必须携带:

  • 来源信息:文件名、章节标题、页码
  • 时间信息:文档更新时间(用于时效性过滤)
  • 类型信息:文档类型(法律/技术/FAQ)
  • 层级路径:如 产品手册 > 安装指南 > 步骤三

元数据的核心价值——召回前过滤:

  • 金融场景:查询”2025 年 Q1 业绩” → 过滤 update_time > 2025-01-01,直接排除 80% 无关文档
  • 法律场景:查询”《个人信息保护法》第 12 条” → 过滤 doc_type=law + section=12


八、总结:工程取舍金字塔

四层递进策略:

  1. 第 1 层:Baseline 先行 — Recursive + 512 + 15% overlap(覆盖 90% 场景)
  2. 第 2 层:结构优于算法 — 能用 Markdown 标题切就别盲切(省 50% 调参时间)
  3. 第 3 层:度量驱动 — 用 Ragas/TruLens 建立评估(别凭感觉调参)
  4. 第 4 层:针对性优化 — 语义分块 / Late Chunking / Contextual Retrieval

🎯 最后的忠告:RAG 的”智商”不是由 Embedding 模型决定的,而是由你如何切分文本决定的。别让”智能切分”成为工程成本的黑洞——在业务场景、精度、成本之间,找到那个”刚刚好”的 chunk。


九、参考资料

  • ChunkSize 和 ChunkOverlap 动态评估法 — 51CTO,2025-08
  • Chunk 不是越聪明越好:RAG 工程实战中的取舍艺术 — CSDN,2026-01
  • 15种 Chunking 方法详解 — CSDN,2026-05
  • -RAG 的优化 — CSDN,2026-03
  • Late Chunking vs Contextual Retrieval — 51CTO,2025-09
  • RAG 检索优化(Chunking / 压缩 / 混合检索) — 博客园,2025-12

技术学习路线图 > RAG

▶ 对应原理:14-文档解析与分块策略

速记卡(面试闪卡)

Q1:一句话讲清「分块策略:chunk_size / overlap 选择、语义分块」到底是什么? A:分块是 RAG 的第一公里,在上下文完整、检索精度、成本三者间切出大小刚好的块。

Q2:一、为什么分块 —— 怎么理解? A:像吃自助牛不能整头塞、也不能剁成肉泥,要把文档切成带口感的牛排块,检索才精准(Chunk 分块)。

Q3:二、chunk_size 与 overlap —— 怎么理解? A:太大噪声稀释、太小语义腰斩;overlap 是块间留拼接缝,防关键信息恰好落在切点上被切断(Overlap 重叠窗口)。

Q4:三、分块策略全景 —— 怎么理解? A:从朴素固定到递归(工业 Baseline),再到结构感知、父子索引、语义分块层层递进(Recursive Chunking 递归切分)。

Q5:四、语义分块与前沿 —— 怎么理解? A:语义分块顺纹理切、相似度突变才下刀;Late Chunking 先嵌入后切、Contextual 给每块贴说明书(Semantic Chunking 语义分块)。

Q6:核心速记主线有哪些?

  • 三硬约束:上下文窗口、检索精度、成本
  • Baseline:Recursive + 512 token + 15% overlap
  • 语义分块按余弦相似度断点,成本高
  • 元数据是系统粘合剂,召回前先过滤

口诀 A:整牛别想一口吞,切成牛排才入唇; 太大稀释小断魂,overlap 护缝留痕; 递归打底工业稳,语义顺纹下刀准; Late 先嵌后切分,上下文贴说明文。

相关链接


技术学习路线图 > RAG

相关链接