RAG02:切分策略
从按章节、语义、代码块到业务对象切分,理解如何将文档切分为合适的检索单元。
文档切分是 RAG 的核心环节之一。合理的切分策略能将长文档拆分为合适的检索单元,既保证语义完整性,又提高检索精度。
切分策略的重要性
切分不当的问题
| 问题 | 表现 | 影响 |
|---|---|---|
| 切分过大 | 每个片段包含太多内容 | 检索精度低,噪音多 |
| 切分过小 | 每个片段内容不完整 | 语义断裂,上下文丢失 |
| 切分不均 | 片段大小差异大 | 检索效果不稳定 |
| 切分不当 | 破坏语义完整性 | 答案质量差 |
切分策略的影响
| 切分方式 | 优点 | 缺点 |
|---|---|---|
| 按章节 | 语义完整 | 片段可能过大 |
| 按固定长度 | 简单可控 | 可能破坏语义 |
| 按语义 | 语义完整 | 实现复杂 |
| 按代码块 | 适合代码 | 不适合普通文本 |
常见切分策略
按章节切分
按章节切分是最直观的方式:
文档结构:
第1章:引言
1.1 背景
1.2 目的
第2章:系统设计
2.1 架构设计
2.2 模块划分
第3章:实现细节
3.1 核心算法
3.2 代码实现
切分结果:
片段1:第1章完整内容
片段2:第2章完整内容
片段3:第3章完整内容
适用场景:
- 结构化文档(书籍、报告)
- 章节内容相对独立
- 需要保持章节完整性
按固定长度切分
按固定长度切分是最简单的方式:
def split_by_fixed_length(text, chunk_size=500, overlap=50):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start = end - overlap
return chunks
参数说明:
| 参数 | 说明 | 推荐值 |
|---|---|---|
| chunk_size | 片段大小(字符数) | 500-1000 |
| overlap | 重叠大小(字符数) | 50-100 |
适用场景:
- 非结构化文本
- 简单场景
- 需要快速实现
按语义切分
按语义切分是最智能的方式:
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
separators=["\n\n", "\n", "。", "!", "?", ",", "、"]
)
chunks = text_splitter.split_text(text)
分隔符优先级:
| 优先级 | 分隔符 | 说明 |
|---|---|---|
| 1 | \n\n | 段落分隔 |
| 2 | \n | 换行分隔 |
| 3 | 。!? | 句子分隔 |
| 4 | ,、 | 逗号分隔 |
适用场景:
- 需要保持语义完整
- 复杂文档
- 对检索精度要求高
按代码块切分
按代码块切分适合技术文档:
import re
def split_by_code_blocks(text):
# 匹配代码块
code_pattern = r'```(\w*)\n(.*?)```'
code_blocks = re.findall(code_pattern, text, re.DOTALL)
# 匹配内联代码
inline_pattern = r'`([^`]+)`'
inline_codes = re.findall(inline_pattern, text)
return {
'code_blocks': code_blocks,
'inline_codes': inline_codes,
'text': re.sub(code_pattern, '[代码块]', text)
}
适用场景:
- 技术文档
- API 文档
- 代码示例
按业务对象切分
按业务对象切分适合业务文档:
业务文档:
产品名称:电商平台
功能模块:用户认证
API:POST /api/auth/login
切分结果:
片段1:产品信息(名称、版本、描述)
片段2:功能模块(用户认证)
片段3:API 定义(登录接口)
片段4:数据模型(用户实体)
片段5:业务流程(登录流程)
适用场景:
- 业务文档
- API 文档
- 产品文档
切分策略的优化
重叠设置
重叠设置确保相邻片段之间有一定的上下文重叠:
原始文本:"这是一段测试文本,用于演示切分策略的效果。"
切分(chunk_size=10, overlap=3):
片段1:"这是一段测试文本"
片段2:"测试文本,用于演示"
片段3:"用于演示切分策略"
片段4:"切分策略的效果。"
重叠的作用:
- 避免语义断裂
- 保持上下文连续性
- 提高检索召回率
元数据添加
为每个片段添加元数据:
| 元数据 | 说明 | 示例 |
|---|---|---|
| source | 文档来源 | ”产品文档” |
| title | 文档标题 | ”用户认证模块” |
| section | 章节信息 | ”2.1 架构设计” |
| type | 内容类型 | ”代码”、“文本”、“表格” |
| page | 页码 | 第5页 |
元数据的作用:
- 提高检索精度
- 支持过滤和排序
- 便于溯源
语义增强
为每个片段添加语义增强信息:
def enhance_chunk(chunk, metadata):
# 添加标题信息
enhanced = f"标题:{metadata['title']}\n"
# 添加章节信息
if metadata.get('section'):
enhanced += f"章节:{metadata['section']}\n"
# 添加内容类型
enhanced += f"类型:{metadata['type']}\n"
# 添加内容
enhanced += f"内容:{chunk}\n"
return enhanced
语义增强的作用:
- 提高检索精度
- 增加片段的语义信息
- 帮助模型理解上下文
切分策略的评估
评估指标
| 指标 | 说明 | 目标值 |
|---|---|---|
| 平均片段长度 | 平均每个片段的字符数 | 500-1000 |
| 片段长度标准差 | 片段长度的一致性 | < 200 |
| 语义完整性 | 片段是否保持语义完整 | > 90% |
| 检索召回率 | 检索时的召回率 | > 85% |
| 检索精确率 | 检索时的精确率 | > 80% |
评估方法
1. 准备测试文档
2. 使用不同切分策略
3. 进行检索测试
4. 计算评估指标
5. 选择最优策略
6. 持续监控和调整
切分策略的常见问题
问题1:语义断裂
表现:切分后语义不完整
解决方案:
- 使用语义切分
- 增加重叠大小
- 优化分隔符
问题2:片段过大
表现:片段包含太多内容
解决方案:
- 减小 chunk_size
- 使用更细粒度的分隔符
- 按子章节切分
问题3:片段过小
表现:片段内容不完整
解决方案:
- 增大 chunk_size
- 使用更粗粒度的分隔符
- 合并小片段
问题4:切分不均
表现:片段大小差异大
解决方案:
- 使用动态切分策略
- 设置最小和最大片段大小
- 合并过小的片段
项目判断清单
- 需要保持章节完整性 → 使用按章节切分
- 需要快速实现 → 使用按固定长度切分
- 需要保持语义完整 → 使用按语义切分
- 需要处理技术文档 → 使用按代码块切分
- 需要处理业务文档 → 使用按业务对象切分
- 语义断裂 → 增加重叠或使用语义切分
- 片段过大 → 减小 chunk_size
- 片段过小 → 增大 chunk_size
- 需要长期维护 → 建立切分策略评估和优化机制