在构建基于大语言模型(LLM)的知识库应用时,文本分块(Chunking) 是决定检索精度最关键的环节之一。本文将深入探讨为什么要进行分块,并详细介绍四种主流的分块策略及其代码实现。
一、 为什么“分块”至关重要?
分块是将长文档拆分为更小、更易处理的片段的过程。其核心目标是:在检索时准确找到最相关的内容。
- 语义独立性:理想的块应尽可能在不依赖上下文的情况下表达完整意思,方便模型理解。
- 粒度平衡:块太小,有效信息不足,模型难以理解;块太大,包含噪音多,检索相关性下降。
- 预处理:在分块前,通常需要进行文本清洗、去除停用词等操作。
二、 四种主流分块策略实战
1. 基于标点符号切分(句子级)
通过识别自然语言中的标点符号(如 。 ! ?)进行切分,最大限度保持了语义的完整。
import re
def split_text_by_punctuation(text):
# 匹配中英文结尾标点
pattern = r"[。!?。]+"
segments = re.split(pattern, text)
return [s.strip() for s in segments if s.strip()]
# 适用场景:对话系统、对短句逻辑敏感的场景。
2. 固定字符数切分
不考虑内容含义,强制按照设定的字符长度进行硬切分。
def split_text_by_fixed_length(text, length):
return [text[i:i + length] for i in range(0, len(text), length)]
# 优缺点:实现极其简单,但会造成主题和语义的严重断裂。
# 适用场景:日志处理、固定格式的代码块、传感器流数据。
3. 带重叠窗口的固定长度切分
这是对固定长度切分的改良,通过在相邻的块之间保留一部分重叠(Overlap),确保跨块的上下文信息不会丢失。
def split_text_by_fixed_length_with_overlap(text, length, overlap):
step = length - overlap
return [text[i:i + length] for i in range(0, len(text) - overlap, step)]
# 意义:如果某个语义被切断,重叠部分能确保它在下一个 Chunk 中以完整形式出现。
4. 递归字符切分(推荐方案)
这是目前 RAG 应用中最常用的策略(如 LangChain 的默认实现)。它通过一组分隔符(如 \n\n, \n, ``)递归地尝试拆分,直到块大小达标。
LangChain 实现示例:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", " ", ""], # 优先级由高到低
chunk_size=60,
chunk_overlap=12,
length_function=len
)
text = "..." # 您的长文本
splits = splitter.split_text(text)
三、 策略效果对比
以以下文本为例,对比不同策略的效果。
春节的脚步越来越近,大街小巷都布满了节日的气氛。商店门口挂满了红灯笼和春联,家家户户都在忙着打扫卫生,准备迎接新的一年。 小明回到家乡,感受到了浓浓的过年氛围。他在街上走着,看到小朋友们手持烟花棒,欢笑声此起彼伏。 夜幕降临,整个城市亮起了五彩缤纷的灯光,映照着人们脸上的喜悦与期待。老人们聚在一起,回忆过去,展望未来。 而年轻人则在夜市享受美食,放松心情。这是一个充满希望和喜悦的时刻,每个人都在以自己的方式庆祝这个特殊的节日。
1、基于标点符号切分(句子级)
Chunk 1: 春节的脚步越来越近,大街小巷都布满了节日的气氛
Chunk 2: 商店门口挂满了红灯笼和春联,家家户户都在忙着打扫卫生,准备迎接新的一年
Chunk 3: 小明回到家乡,感受到了浓浓的过年氛围
Chunk 4: 他在街上走着,看到小朋友们手持烟花棒,欢笑声此起彼伏
Chunk 5: 夜幕降临,整个城市亮起了五彩缤纷的灯光,映照着人们脸上的喜悦与期待
Chunk 6: 老人们聚在一起,回忆过去,展望未来
Chunk 7: 而年轻人则在夜市享受美食,放松心情
Chunk 8: 这是一个充满希望和喜悦的时刻,每个人都在以自己的方式庆祝这个特殊的节日
2、固定字符数切分
Chunk 1: 春节的脚步越来越近,大街小巷都布满了节日的气氛。商店门口挂满了红灯笼和春联,家家户户都在忙着打扫卫生,准备迎接新的一年。小明回到家乡,感受到了浓浓的过年氛围。他在街上走着,看到小朋友们手持烟花棒,欢笑
Chunk 2: 声此起彼伏。夜幕降临,整个城市亮起了五彩缤纷的灯光,映照着人们脸上的喜悦与期待。老人们聚在一起,回忆过去,展望未来。而年轻人则在夜市享受美食,放松心情。这是一个充满希望和喜悦的时刻,每个人都在以自己的
Chunk 3: 方式庆祝这个特殊的节日。
3、带重叠窗口的固定长度切分
Chunk 1: 春节的脚步越来越近,大街小巷都布满了节日的气氛。商店门口挂满了红灯笼和春联,家家户户都在忙着打扫卫生,准备迎接新的一年。小明回到家乡,感受到了浓浓的过年氛围。他在街上走着,看到小朋友们手持烟花棒,欢笑
Chunk 2: 了浓浓的过年氛围。他在街上走着,看到小朋友们手持烟花棒,欢笑声此起彼伏。夜幕降临,整个城市亮起了五彩缤纷的灯光,映照着人们脸上的喜悦与期待。老人们聚在一起,回忆过去,展望未来。而年轻人则在夜市享受美食
Chunk 3: 老人们聚在一起,回忆过去,展望未来。而年轻人则在夜市享受美食,放松心情。这是一个充满希望和喜悦的时刻,每个人都在以自己的方式庆祝这个特殊的节日。
4、递归字符切分(推荐方案)
Chunk 1: 春节的脚步越来越近,大街小巷都布满了节日的气氛。商店门口挂满了红灯笼和春联,家家户户都在忙着打扫卫生,准备迎接新的一年。
Chunk 2: 卫生,准备迎接新的一年。小明回到家乡,感受到了浓浓的过年氛围。他在街上走着,看到小朋友们手持烟花棒,欢笑声此起彼伏。夜幕
Chunk 3: 棒,欢笑声此起彼伏。夜幕降临,整个城市亮起了五彩缤纷的灯光,映照着人们脸上的喜悦与期待。老人们聚在一起,回忆过去,展望未
Chunk 4: 在一起,回忆过去,展望未来。而年轻人则在夜市享受美食,放松心情。这是一个充满希望和喜悦的时刻,每个人都在以自己的方式庆祝
Chunk 5: 个人都在以自己的方式庆祝这个特殊的节日。
四、 策略对比总结
| 策略 | 灵活性 | 语义保持 | 复杂度 | 推荐使用场景 |
|---|---|---|---|---|
| 句子切分 | 中 | 极高 | 低 | 结构化良好的文学或新闻文本 |
| 固定字符 | 低 | 差 | 极低 | 日志、固定格式数据 |
| 重叠窗口 | 中 | 中 | 低 | 追求检索召回率的通用 RAG |
| 递归切分 | 高 | 高 | 中 | 首选策略,适用于复杂文档 |
五、 结语
分块没有“一劳永逸”的万能公式。对于开发者而言,最有效的做法是:
- 分析数据源(是规整的报告还是杂乱的网页?)。
- 选择合适的 Splitter(优先尝试递归切分)。
- 利用可视化工具(如 LangChain Text Splitter Streamlit App)实时调整
chunk_size和overlap。