Skip to content

02.2 - 文本分块策略

定位: RAG 检索质量的第一个关键控制点——合理的分块策略决定了"能不能检索到正确的信息" 面试高频度: ⭐⭐⭐⭐ 考查方式: 分块参数选择、不同策略的场景适配、分块对检索质量的影响

一、这是什么?为什么需要它?

是什么

文本分块(Chunking)是将加载后的长文档切分为若干较小的文本片段(Chunks),每个 Chunk 将被独立向量化并存储,作为检索的基本单元。

原始文档(3000字):
  第1章...第2章...第3章...第4章...

分块后:
  Chunk 1: 第1章内容...        <- 每个 Chunk 独立向量化
  Chunk 2: 第2章内容...        <- 检索时按 Chunk 匹配
  Chunk 3: 第3章内容...
  Chunk 4: 第4章内容...

为什么需要分块?

LLM 和 Embedding 模型都有"输入长度限制"。

限制含义典型值
Embedding 模型最大长度超过则截断512 tokens(如 BGE)
LLM 上下文窗口只能处理这么多信息4K-128K tokens
检索精度块太大/太小都影响匹配128-1024 tokens

不分块的后果

公司对远程工作的政策是什么?

不分块 -> 整本 500 页员工手册作为一个向量
        -> "远程工作"的语义被淹没在整体语义中
        -> 检索得分很低

分块后 -> 远程工作政策作为独立 Chunk(约半页)
        -> 语义集中 -> 检索得分很高
        -> 精准命中!

二、原理拆解

2.1 分块的核心权衡

            +-- 更精准 --+          +-- 更完整 --+
            |                            |
         小块                           大块
  +------------------+           +------------------+
  | 128 tokens        |  <---->     | 1024+ tokens     |
  +------------------+           +------------------+
            |                            |
  + 语义单一、精确          + 上下文完整、信息丰富
  - 缺乏上下文              - 噪声多、关键词易稀释
  - 碎片化、需要更多K        - 超过模型限制时被截断
参数小块 (128-256)中块 (512)大块 (1024-2048)
语义纯度低(可能含多个主题)
上下文完整性低(截断段落)
Top-K 覆盖率需要更多 K适中少量 K 即可
检索召回(精确查询)中高中低
检索召回(模糊主题)
存储成本块数多适中块数少

2.2 五种主流分块策略

策略 1:固定大小分块

最简单的分块方式,按固定 token 数切分:

[文档开始]
[0-256 tokens] [128-384 tokens] [256-512 tokens] ...
  Chunk 1       Chunk 2          Chunk 3
      重叠 128 tokens  <-- overlap 防止截断语义

核心参数

参数作用推荐值
chunk_size每个块的 token 数256-1024
chunk_overlap块之间重叠的 token 数10-20% of chunk_size

为什么需要重叠?

句子A。句子B。句子C。句子D。
  |--- Chunk 1 ---|
               |--- Chunk 2 ---|

如果不重叠:"句子B。"可能被 @截断
如果有重叠: 句子B在 Chunk 1 和 2 中都是完整的

策略 2:递归字符分块(推荐)

按分隔符优先级递归切分,保留自然文本边界:

python
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,
    separators=["\n\n", "\n", "。", ".", " ", ""]
    # 优先级:段落 > 换行 > 句号 > 空格 > 字符
)

分块层级

文本
  |-- 段落 (\n\n)         <- 最优分割点
      |-- 句子 (。|.!?)   <- 次优
          |-- 短语 (,|,) <- 再次
              |-- 单词    <- 最后手段
原始文本:
"第3章 远程工作政策\n\n公司允许员工每周远程工作最多3天。
需提前在系统申请。\n\n第4章 休假制度..."

分块过程:
第1步:按 \n\n 分割 -> ["第3章 远程工作政策\n公司允许...",
                       "第4章 休假制度..."]

第1块仍 > 500 tokens -> 第2步:按 \n 分割
  -> ["第3章 远程工作政策", "公司允许员工每周远程工作最多3天。需提前在系统申请。"]

第1块仍 > 500 tokens -> 第3步:按 。分割
  -> 最终 Chunk

策略 3:语义分块

基于语义边界(而非字符数)切分,智能识别内容变化点:

原理:
  1. 滑动窗口计算相邻句子的 Embedding 相似度
  2. 当相似度突变 -> 认为是主题切换点
  3. 在切换点切分,保证每个 Chunk 语义完整

效果:
  "第3章: 远程工作..."   [高相似度:同一主题]
  "第3章: 远程工作..."
  "公司允许每周3天..."    [低相似度:主题切换]
  --- 在这里切分 ---    [语义边界]
  "第4章: 休假制度..."

工具

python
# 使用 LangChain 的语义分块(实验性)
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

semantic_splitter = SemanticChunker(
    embeddings=OpenAIEmbeddings(),
    breakpoint_threshold_type="percentile"
    # percentile / standard_deviation / interquartile
)
chunks = semantic_splitter.split_documents(documents)
方法优点缺点适用场景
语义分块保留完整语义单元计算成本高写作质量高的文档
递归分块效率高、效果稳定可能截断句子通用(推荐首选)
固定分块极简单、速度快语义断裂快速原型

策略 4:特定文档分块

针对特定文档结构设计的分块策略:

Markdown 分块:
  # 标题1                -> Chunk 1 的起点
  ## 子标题1.1            -> Chunk 2 的起点
  正文内容...
  ## 子标题1.2            -> Chunk 3 的起点

代码分块:
  def function1():        -> Chunk 1(按函数)
  def function2():        -> Chunk 2

JSON 分块:
  {                       -> 顶级 key 作为分割点
    "employees": [...]   -> Chunk 1
    "policies": {...}     -> Chunk 2
  }

HTML 分块(网页):
  <article>               -> 按语义标签分割
  <section>
  <p>

策略 5:Agentic / LLM 分块

用 LLM 决定如何分块。最智能但最慢、最贵:

python
prompt = """
请将以下文本切分为语义完整的片段,每个片段应该
围绕一个独立的主题。返回 JSON 数组。

文本:{text}
"""
response = llm.invoke(prompt)
chunks = json.loads(response.content)
策略速度质量成本推荐场景
固定大小分块最高最低快速原型
递归字符分块中高通用首选
语义分块中(需Embedding)高质量文档
特定文档分块结构化文档
Agentic分块最高最高质量要求

三、图解全景

分块策略选择指南

你的文档类型?
  |
  +-- 长文本/小说/文章?
  |     -> 递归字符分块(chunk_size=500, overlap=100)
  |
  +-- Markdown/LaTeX 等结构化文本?
  |     -> MarkdownHeaderTextSplitter(按标题分块)
  |
  +-- 代码文件?
  |     -> 按函数/类分块(RecursiveCharacter with code separators)
  |
  +-- JSON/HTML/XML?
  |     -> 特定解析器 + RecursiveCharacter
  |
  +-- 高价值文档(准确率要求极高)?
  |     -> 语义分块/Semantic Chunking
  |
  +-- 不知道 -> 递归字符分块(最通用的默认选择)

Chunk 参数速查表

chunk_sizechunk_overlap适合场景说明
12816代码、关键词搜索最精确,但上下文少
25632FAQ、短文本精度和上下文的平衡
512100通用(推荐起点)大多数场景的最佳起点
768150技术文档、论文需要更多上下文时
1024200长文分析、总结大块,适合主题级检索

四、实战验证

4.1 分块策略对比实验

python
# 分块效果对比:同一文档,不同策略

from langchain.text_splitter import (
    RecursiveCharacterTextSplitter,
    CharacterTextSplitter,
)
from langchain_community.document_loaders import TextLoader

# 加载测试文档
loader = TextLoader("test_doc.txt")
doc = loader.load()[0]

# 策略 1:固定大小分块
fixed_splitter = CharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,
    separator=" ",
)
fixed_chunks = fixed_splitter.split_documents([doc])

# 策略 2:递归字符分块(推荐)
recursive_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,
)
recursive_chunks = recursive_splitter.split_documents([doc])

# 检查分块结果
for i, chunk in enumerate(recursive_chunks[:3]):
    print(f"=== Chunk {i+1} ===")
    print(f"长度: {len(chunk.page_content)} 字符")
    print(f"内容预览: {chunk.page_content[:100]}...")
    print()

4.2 分块参数的影响实验

bash
# 用 RAGAS 评估不同分块策略对检索质量的影响

# 实验设计:
# 同一知识库,同一测试集,仅改变分块策略
# 观察 Faithfulness 和 Relevancy 分数

# 典型实验结果:
# +----------------+----------+-----------+
# | 分块策略        | Faith.   | Relev.    |
# +----------------+----------+-----------+
# | 256, overlap=0  | 0.72     | 0.65      |  <- 块太小,上下文不足
# | 512, overlap=50 | 0.78     | 0.71      |  <- 好一些
# | 512, overlap=128| 0.83     | 0.76      |  <- 最佳平衡
# | 1024, overlap=200| 0.81    | 0.73      |  <- 块太大,噪声增加
# | 语义分块         | 0.86     | 0.79      |  <- 最好但最贵
# +----------------+----------+-----------+

五、面试视角

问题答案要点
chunk_size 和 chunk_overlap 如何确定?chunk_size 取决于 Embedding 模型的 max_input (典型 512) 和文档的复杂度。chunk_overlap 取 chunk_size 的 10-20% 防止截断。起点:500/100
为什么递归分块是推荐的首选策略?因为它按自然分割符(段落>句子>短语)递归切分,在保证效率的同时最大程度保留语义完整性,是质量和速度的最佳平衡
分块大小如何影响检索质量?太小:上下文不足、碎片化;太大:噪声多、语义稀释;最优块能让每个块围绕一个主题,检索时击中率高
代码文件分块有什么特殊考虑?代码应按函数/类/模块分割,而不是按行数;注释和文档字符串应与代码一起保留;使用适合代码的分隔符(函数定义、类定义)
分块对 RAG 延迟的影响?块数多 -> 存储大 -> 检索慢;块太大 -> 生成时 tokens 多 -> 生成慢。需在检索和生成之间平衡

📚 相关链接

  • **文档加载与解析** — 分块之前的文档加载
  • **Embedding模型与向量化** — 分块之后向量化
  • **检索与重排序** — 检索策略与分块的关系
  • <- 返回 **检索系统索引**

Knowledge4J — Java 知识库