数据分块:让 RAG 检索更精准的 Chunk 策略

数据分块:让 RAG 检索更精准的 Chunk 策略

RAG 检索的不是整篇文档,而是文档的片段——Chunk(块)。块切得好不好,直接决定检索能不能召回正确的内容。

为什么需要分块

问题一:大模型上下文窗口限制

大模型的上下文窗口有限,无法一次性处理整篇长文档。假设一篇 50 页的 PDF,全部塞进去会直接爆token。

问题二:检索精度——大海捞针

即使窗口够大,把所有文本都塞给模型会让它”走神”,找不到重点。

解决方案:先检索出最相关的几个文本块,只把这几个块喂给模型。

关键参数

chunkSize(块大小)

  • 太大:检索不精准,容易混入不相关内容,上下文被稀释
  • 太小:语义不完整,上下文丢失,关键信息被切断

推荐范围:200 ~ 1000 字符

overlap(重叠量)

相邻两个块之间共享的文本长度,用于保持上下文连贯性。

推荐值:chunkSize 的 10%~25%

单位选择

  • 字符:肉眼看到的每个符号,简单但不考虑语义
  • Token:大模型处理的最小单位(中文 1~2 个 token/字),更精确但需要 tokenizer

主流分块策略

1. 固定大小分块(Fixed Size)

最简单的硬切方式,每隔固定字符数切一刀。

优点:实现简单,性能好
缺点:完全忽略文本结构,容易切断语义

1
2
3
4
5
原文: "退货政策第一章。第一段描述基本原则。第二段说明适用范围。第三段规定操作流程。"
↓ 固定每50字符切
块1: "退货政策第一章。第一段描述基本原则。第二段说"
块2: "明适用范围。第三段规定操作流程。"
❌ "退货政策"被切断,"说明"和"适用范围"分离

2. 重叠分块(Overlapping)

相邻块之间保留重叠区域,缓解边界断裂。

优点:有效缓解语义断裂
缺点:overlap 会导致存储量增加(相同内容重复存储)

1
2
3
块1: "退货政策第一章...第二段说明适用"
块2: "明适用范围...第三段规定操作流程"
↑────────────重叠────────────↑

3. 递归分块(Recursive)⭐ 最常用

先尝试用最大分隔符切,逐步细化到更小的分隔符。

分隔符优先级:["\n\n", "\n", "。", ",", " ", ""]

优点:兼顾语义完整性和块大小控制
缺点:需要根据语言调整分隔符(中文和英文分隔符不同)

4. 语义分块(Semantic)

用 Embedding 模型判断语义相似度,在话题转换处切割。

优点:分块质量最高,每个块主题高度内聚
缺点:需要调用模型,有额外成本,速度较慢

5. 混合分块(Hybrid)

组合多种策略,取长补短。例如先用递归分块,再用语义分块合并过小的块。

分块策略选择表

文档类型 推荐策略
产品手册/知识库 递归分块
FAQ/问答对 递归分块
合同/法律文档 语义分块
日志文件 固定大小分块
代码文件 专用代码分块器
多类型混合 混合分块

调参经验值

参数 推荐范围
chunkSize 200 ~ 1000 字符
overlap chunkSize 的 10%~25%

调参思路:从 chunkSize=500overlap=50 开始,根据实际效果微调。

可视化工具

ChunkViz — 分块效果对比工具,可以输入文本和参数,直观看到不同策略的切分结果。

实战:Python 实现递归分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import re

def recursive_chunk(text: str, separators: list[str] = None,
chunk_size: int = 500, overlap: int = 50) -> list[str]:
if separators is None:
separators = ["\n\n", "\n", "。", ",", " ", ""]

chunks = []
start = 0

while start < len(text):
end = start + chunk_size

# 尝试在分隔符处切割
for sep in separators:
if sep: # 非空分隔符
pos = text.rfind(sep, start, end)
if pos > start:
end = pos + len(sep)
break

chunk = text[start:end].strip()
if chunk:
chunks.append(chunk)

start = end - overlap # 重叠移动

return chunks

# 使用
text = """
退货政策第一章
第一条:为保障消费者权益,本店所有商品支持七天无理由退货。
第二条:退货商品需保持完好,不影响二次销售。
物流说明
配送时间:下单后2-3个工作日送达。
偏远地区延长1-2天。
"""
chunks = recursive_chunk(text, chunk_size=100, overlap=20)
for i, c in enumerate(chunks):
print(f"块{i+1}: {c}\n")

相关阅读: