正则分块 (Regex)
正则分块是一种基于自定义正则表达式模式进行切分的灵活分块策略。它提供了最大的自由度,适合处理特殊格式的文档或需要自定义切分规则的场景。
核心概念
正则分块的核心思想是:使用正则表达式定义分隔符,按模式快速切分文本。
与其他策略的区别:
- vs 智能分块: 不分析文档结构,纯模式匹配
- vs 标题分块: 不限于标题,可以是任意模式
- vs 父子分块: 单层结构,处理速度最快
工作原理
1. 模式匹配切分
正则分块的工作流程非常直接:
文本内容 → 正则表达式匹配 → 在匹配位置切分 → 生成块列表
示例:
原始文本:
段落 A 的内容。
段落 B 的内容。
段落 C 的内容。
使用模式 \n\n+ (两个或多个换行符):
import re
text = "段落 A 的内容。\n\n段落 B 的内容。\n\n段落 C 的内容。"
pattern = r"\n\n+"
chunks = re.split(pattern, text)
# 结果: ["段落 A 的内容。", "段落 B 的内容。", "段落 C 的内容。"]
2. 默认模式
默认正则模式: \n\n+
这个模式匹配两个或多个连续的换行符,相当于按段落分隔。
为什么选择这个作为默认?
- ✅ 在大多数文档中,段落之间用空行分隔
- ✅ 简单直观,容易理解
- ✅ 适用于 Markdown、纯文本等多种格式
效果:
段落 1
段落 2
段落 3
→ 切分为 3 个块
3. 切分后的大小控制
正则分块在模式切分后,还会进行大小控制:
步骤 1: 按正则模式切分
raw_chunks = re.split(pattern, text)
步骤 2: 应用 token 限制
final_chunks = []
for chunk in raw_chunks:
tokens = count_tokens(chunk)
# 情况 1: 块太小,合并到前一个块
if tokens < min_chunk_tokens:
if final_chunks:
final_chunks[-1] += "\n\n" + chunk
else:
final_chunks.append(chunk)
# 情况 2: 块大小合适,直接添加
elif tokens <= chunk_token_num:
final_chunks.append(chunk)
# 情况 3: 块太大,进一步切分
else:
# 按 chunk_token_num 进一步切分
sub_chunks = split_large_chunk(chunk, chunk_token_num)
final_chunks.extend(sub_chunks)
示例:
假设 chunk_token_num=256, min_chunk_tokens=10, pattern=\n\n+
原始文本:
块 A: 500 tokens
块 B: 8 tokens (太小)
块 C: 200 tokens
块 D: 350 tokens (太大)
处理后:
块 1: 250 tokens (从块 A 切分)
块 2: 258 tokens (块 A 剩余 + 块 B 合并)
块 3: 200 tokens (块 C 保持)
块 4: 250 tokens (从块 D 切分)
块 5: 100 tokens (块 D 剩余)
常用正则模式
1. 段落分隔 (默认)
模式: \n\n+
匹配: 两个或多个换行符
适用场景:
- 纯文本文件
- Markdown 文档
- 文章和报告
示例:
第一段内容。
第二段内容。
第三段内容。
→ 切分为 3 段
2. 句子分隔 (中文)
模式: [。!?]+
匹配: 中文句号、感叹号、问号
适用场景:
- 需要很细的分块粒度
- 短文本检索
- 问答数据集
示例:
这是第一句。这是第二句!这是第三句?
→ 切分为 3 块: ["这是第一句", "这是第二句", "这是第三句"]
注意: 这会产生很多小块,通常需要配合较大的 chunk_token_num 使用。
3. 句子分隔 (英文)
模式: [.!?]+\s+
匹配: 英文句号/感叹号/问号 + 空格
适用场景:
- 英文文档
- 细粒度分块
示例:
This is sentence one. This is sentence two! This is sentence three?
→ 切分为 3 块
4. 章节标记分隔
模式: \n---+\n
匹配: 单独一行的多个连字符(Markdown 分隔线)
适用场景:
- 使用
---作为章节分隔的文档 - 自定义格式文档
示例:
第一章内容
---
第二章内容
---
第三章内容
→ 切分为 3 块
5. Markdown 标题分隔
模式: \n#{1,6}\s+
匹配: 行首的 Markdown 标题(1-6 个 #)
适用场景:
- 需要按标题切分,但不想用标题分块策略
- 更灵活的标题切分
示例:
# 标题 1
内容 A
## 标题 2
内容 B
### 标题 3
内容 C
→ 切分为 3 块(在每个标题处切分)
注意: 与标题分块不同,这里是简单的模式匹配,不考虑标题层级。
6. 编号章节分隔
模式: \n\d+\.\s+
匹配: 行首的数字编号(如 "1. ", "2. ")
适用场景:
- 有编号的章节或列表
- 合同条款
示例:
1. 第一条款
内容...
2. 第二条款
内容...
3. 第三条款
内容...
→ 切分为 3 块
7. 日志文件分隔
模式: \n\d{4}-\d{2}-\d{2}
匹配: 日期格式(YYYY-MM-DD)
适用场景:
- 日志文件
- 按日期组织的文档
示例:
2024-01-15 系统启动...
详细日志...
2024-01-16 系统更新...
详细日志...
→ 按日期切分
8. 多个分隔符组合
模式: (\n\n+|---+|\n#{1,6}\s+)
匹配: 空行 OR 分隔线 OR 标题
适用场景:
- 复杂文档,有多种分隔方式
- 提高切分灵活性
示例:
段落 A
段落 B
---
# 新章节
段落 C
→ 在空行、分隔线、标题处都会切分
配置参数
完整配置示例
{
"regex_pattern": "\\n\\n+", // 正则表达式模式
"chunk_token_num": 256, // 目标块大小
"min_chunk_tokens": 10, // 最小块大小
"enable_vision_enhancement": false, // 是否启用图片理解
"vision_description_format": "[图片描述]: {desc}",
"vision_batch_size": 3
}
参数详解
regex_pattern (默认: \\n\\n+)
作用: 定义切分文本的正则表达式模式
注意: 在 JSON 配置中,需要转义反斜杠,例如:
{
"regex_pattern": "\\n\\n+" // 注意双反斜杠
}
在 Python 中等价于:
regex_pattern = r"\n\n+" // 原始字符串,单反斜杠
测试正则表达式:
可以使用在线工具测试:
chunk_token_num (默认: 256)
作用: 目标块大小,用于进一步切分过大的块
推荐值: 256-512 tokens
说明:
- 如果正则切分后的块超过这个大小,会进一步切分
- 如果块小于这个大小,直接使用
min_chunk_tokens (默认: 10)
作用: 最小块大小,过小的块会被合并或丢弃
推荐值: 10-50 tokens
说明:
- 避免产生无意义的小块
- 小块会被合并到前一个块
产品使用指南
1. 在 KnowFlow 中配置
步骤 1: 选择正则分块
- 创建或编辑知识库
- 在"分块方法"中选择"正则分块(Regex)"
- 点击"配置"
步骤 2: 设置正则模式
界面示意:
分块方法: 正则分块
正则表达式: [输入框]
默认: \n\n+
说明: 使用正则表达式定义分隔符,在匹配位置切分文本
常用模式:
• \n\n+ 段落分隔(默认)
• [。!?]+ 中文句子分隔
• \n---+\n 分隔线
• \n#+\s+ Markdown 标题
块大小: 256 tokens
最小块大小: 10 tokens
步骤 3: 测试模式
重要: 在应用到所有文档前,先测试正则模式!
- 上传一个样本文档
- 查看"分块预览"
- 检查切分效果
- 调整模式并重新测试
2. 正则表达式编写技巧
基础语法
字符匹配:
. 匹配任意字符(除换行符)
\n 匹配换行符
\s 匹配空白字符(空格、制表符等)
\d 匹配数字
\w 匹配字母、数字、下划线
量词:
+ 匹配 1 次或多次
* 匹配 0 次或多次
? 匹配 0 次或 1 次
{n} 匹配恰好 n 次
{n,m} 匹配 n 到 m 次
{n,} 匹配至少 n 次
字符类:
[abc] 匹配 a 或 b 或 c
[^abc] 匹配除 a、b、c 之外的字符
[a-z] 匹配小写字母
[0-9] 匹配数字
分组和选择:
(abc) 分组
a|b 匹配 a 或 b
常见错误
❌ 错误 1: 忘记转义特殊字符
{
"regex_pattern": "." // 这会匹配任意字符!
}
应该:
{
"regex_pattern": "\\." // 匹配句号
}
❌ 错误 2: 忘记 JSON 中的双反斜杠
{
"regex_pattern": "\n\n+" // 错误!JSON 中 \n 会被转义
}
应该:
{
"regex_pattern": "\\n\\n+" // 正确
}
❌ 错误 3: 模式过于宽泛
{
"regex_pattern": "\\n+" // 会在每个换行符处切分,产生很多小块
}
更好:
{
"regex_pattern": "\\n\\n+" // 只在空行处切分
}
调试技巧
技巧 1: 使用在线工具测试
在 https://regex101.com/ 测试你的正则表达式:
- 选择 Python 语法
- 粘贴样本文本
- 输入正则表达式
- 查看匹配结果
技巧 2: 从简单开始,逐步完善
第一步: \n\n (匹配双换行)
第二步: \n\n+ (匹配两个或多个换行)
第三步: (\n\n+|---) (匹配空行或分隔线)
技巧 3: 查看分块预览
在 KnowFlow 中上传样本文档,查看实际切分效果,根据结果调整。
3. 最佳实践
推荐配置模板
纯文本文档:
{
"regex_pattern": "\\n\\n+",
"chunk_token_num": 256,
"min_chunk_tokens": 10
}
日志文件(按日期):
{
"regex_pattern": "\\n\\d{4}-\\d{2}-\\d{2}",
"chunk_token_num": 512,
"min_chunk_tokens": 20
}
有编号的条款文档:
{
"regex_pattern": "\\n\\d+\\.",
"chunk_token_num": 256,
"min_chunk_tokens": 50
}
使用分隔线的文档:
{
"regex_pattern": "\\n-{3,}\\n",
"chunk_token_num": 512,
"min_chunk_tokens": 20
}
混合分隔符:
{
"regex_pattern": "(\\n\\n+|\\n-{3,}\\n|\\n#{1,6}\\s+)",
"chunk_token_num": 256,
"min_chunk_tokens": 10
}
性能优化
优化 1: 简化正则表达式
❌ 复杂:
(\n\n+|\n\r\n+|\r\n\r\n+|\n {2,}\n)
✅ 简单:
\n\s*\n
优化 2: 避免回溯
❌ 可能导致回溯:
.*abc.*
✅ 明确匹配:
[^\n]*abc[^\n]*
优化 3: 增大块大小
如果文档很大,增大 chunk_token_num 可以减少块数量,提高处理速度:
{
"chunk_token_num": 512 // 从 256 增加到 512
}
使用场景
适合的场景
✅ 日志文件
- 按时间戳切分
- 按日志级别切分
- 示例: 服务器日志、应用日志
✅ 数据文件
- CSV、TSV 等分隔符文件
- 自定义格式文件
- 示例: 导出数据、批量记录
✅ 特殊格式文档
- 非标准 Markdown
- 纯文本文件
- 自定义标记语言
✅ 快速原型开发
- 快速测试不同切分方式
- 不需要复杂的语义分析
✅ 简单规则切分
- 分隔规则明确且一致
- 不需要考虑语义完整性
不适合的场景
❌ 需要语义完整性
- 技术文档(使用智能分块)
- 结构化手册(使用标题分块)
❌ 复杂文档结构
- 嵌套列表、多级标题
- 建议使用智能分块或标题分块
❌ 需要保持表格/代码完整
- 正则分块可能会切断表格和代码
- 建议使用智能分块
❌ 不熟悉正则表达式
- 学习曲线较陡
- 建议使用智能分块(默认)
与其他策略的对比
| 特性 | 正则分块 | 智能分块 | 标题分块 |
|---|---|---|---|
| 灵活性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 处理速度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 语义完整性 | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 学习成本 | ⭐⭐⭐ | ⭐ | ⭐ |
| 配置复杂度 | ⭐⭐⭐ | ⭐ | ⭐ |
| 适用范围 | 特殊场景 | 通用 | 结构化文档 |
何时选择正则分块?
选择正则分块:
- ✅ 文档有明确的分隔模式
- ✅ 分隔规则简单一致
- ✅ 需要最快的处理速度
- ✅ 需要完全自定义切分规则
选择其他策略:
- ❌ 文档结构复杂
- ❌ 需要保证语义完整性
- ❌ 不熟悉正则表达式
技术原理(简要说明)
算法流程
1. PDF 解析
↓
2. 提取纯文本
↓
3. 应用正则表达式切分
├─ re.split(pattern, text)
├─ 获得初始块列表
↓
4. 后处理
├─ 过滤空块
├─ 合并过小的块
├─ 拆分过大的块
└─ 应用 token 限制
↓
5. 向量化和存储
Python 实现示例
import re
import tiktoken
def regex_chunking(text, pattern, chunk_size, min_size):
# 1. 按正则模式切分
raw_chunks = re.split(pattern, text)
# 2. 过滤空块
raw_chunks = [c.strip() for c in raw_chunks if c.strip()]
# 3. 应用大小控制
encoding = tiktoken.get_encoding("cl100k_base")
final_chunks = []
current_chunk = ""
for chunk in raw_chunks:
tokens = len(encoding.encode(chunk))
# 块太大,单独拆分
if tokens > chunk_size:
if current_chunk:
final_chunks.append(current_chunk)
current_chunk = ""
# 进一步切分大块
sub_chunks = split_by_size(chunk, chunk_size)
final_chunks.extend(sub_chunks)
# 块太小,合并
elif tokens < min_size:
current_chunk += "\n\n" + chunk if current_chunk else chunk
# 大小合适
else:
if current_chunk:
final_chunks.append(current_chunk)
current_chunk = chunk
# 添加最后一个块
if current_chunk:
final_chunks.append(current_chunk)
return final_chunks
性能特点
- 时间复杂度: O(n),n 为文本长度
- 空间复杂度: O(n)
- 处理速度: 最快(不需要解析文档结构)
常见问题
1. 如何测试我的正则表达式?
答: 使用在线工具:
- 访问 https://regex101.com/
- 选择 Python 语法
- 粘贴样本文本
- 测试你的正则表达式
2. 为什么我的模式没有匹配到任何内容?
答: 常见原因:
- 转义问题: JSON 中忘记双反斜杠
- 换行符不匹配: 文档使用
\r\n(Windows),你使用\n - 模式太严格: 尝试放宽匹配条件
解决:
{
"regex_pattern": "\\n+|\\r\\n+" // 同时匹配 \n 和 \r\n
}
3. 切分后的块大小差异很大怎么办?
答: 这是正常的。正则分块优先遵循模式,不保证块大小均匀。
如果需要更均匀的块:
- 增大
chunk_token_num - 提高
min_chunk_tokens - 或改用智能分块
4. 可以使用复杂的正则表达式吗?
答: 可以,但建议:
- 先用简单模式测试
- 确保模式不会导致性能问题
- 避免过度回溯的模式
5. 正则分块会保留匹配的分隔符吗?
答: 不会。分隔符会被移除,只保留分隔符之间的内容。
示例:
文本: "A---B---C"
模式: "---"
结果: ["A", "B", "C"] // 分隔符 "---" 被移除
6. 如何在段落间保留空行?
答: 使用捕获组:
{
"regex_pattern": "(\\n\\n+)" // 使用括号捕获
}
但通常不需要,因为分块后空行不重要。
高级用法
1. 条件切分
需求: 只在特定标题处切分,忽略其他标题
模式: \n##\s+(?:安装|配置|使用)
说明:
##\s+匹配二级标题(?:安装|配置|使用)只匹配这三个标题
2. 多语言文档
需求: 同时处理中英文句子分隔
模式: [。!?]+|[.!?]+\s+
说明:
[。!?]+中文标点[.!?]+\s+英文标点 + 空格
3. 保留分隔符
需求: 切分后保留标题
方法: 使用前向/后向断言
(?=\n#+\s+)
说明:
(?=...)前向断言,匹配但不消耗- 切分后,标题会保留在块的开头
4. 负向匹配
需求: 在空行处切分,但忽略代码块中的空行
模式: 复杂,建议使用智能分块
正则分块不太适合这种需要上下文理解的场景。