智能分块 (Smart)
智能分块是 KnowFlow 的默认分块策略,它基于 Markdown 抽象语法树(AST)进行智能切分,在保持语义完整性的同时控制块的大小。适用于大多数文档场景。
核心概念
智能分块的核心思想是:识别文档的语义单元,智能合并和切分,在块大小和语义完整性之间取得平衡。
与其他策略的区别:
- vs 标题分块: 不仅考虑标题,还考虑段落、列表、表格等语义单元
- vs 父子分块: 只生成一个层级,处理更快
- vs 正则分块: 理解文档结构,不是简单的模式匹配
工作原理
1. Markdown AST 解析
智能分块首先将 Markdown 文档解析成抽象语法树(AST),识别各种语义单元。
文档示例:
## 系统架构
系统采用三层架构设计。
### 主要组件
系统包含以下组件:
1. 前端展示层
2. 业务逻辑层
3. 数据存储层
| 组件 | 技术栈 |
|------|--------|
| 前端 | React |
| 后端 | Spring Boot |
```python
def hello():
print("Hello World")
**AST 结构:**
Document ├─ Heading (level=2): "系统架构" ├─ Paragraph: "系统采用三层架构设计。" ├─ Heading (level=3): "主要组件" ├─ Paragraph: "系统包含以下组件:" ├─ List (ordered) │ ├─ ListItem: "前端展示层" │ ├─ ListItem: "业务逻辑层" │ └─ ListItem: "数据存储层" ├─ Table │ ├─ Header: ["组件", "技术栈"] │ ├─ Row: ["前端", "React"] │ └─ Row: ["后端", "Spring Boot"] └─ CodeBlock (language=python): "def hello()..."
### 2. 语义单元识别
智能分块识别以下语义单元:
#### 2.1 标题 (Heading)
```markdown
# H1 一级标题
## H2 二级标题
### H3 三级标题
处理规则:
- 标题本身很少作为独立的块
- 标题与其后的内容组合成块
- 标题是重要的切分边界提示
2.2 段落 (Paragraph)
这是一个段落,包含完整的语义。
这是另一个段落。
处理规则:
- 绝不在段落中间切分
- 段落是最基本的语义完整单元
- 多个小段落可以合并成一个块
2.3 列表 (List)
有序列表:
1. 第一项
2. 第二项
3. 第三项
无序列表:
- 项目 A
- 项目 B
- 项目 C
嵌套列表:
1. 父项 1
- 子项 1.1
- 子项 1.2
2. 父项 2
处理规则:
- 尽量保持列表完整
- 如果列表很长,可以在列表项之间切分
- 绝不在列表项内部切分
2.4 表格 (Table)
| 列 1 | 列 2 | 列 3 |
|------|------|------|
| 数据 | 数据 | 数据 |
| 数据 | 数据 | 数据 |
处理规则:
- 表格必须保持完整,绝不切分
- 表格及其标题保持在同一个块
- 表格前的说明文字尽量与表格一起
2.5 代码块 (Code Block)
```python
def example():
return "code"
**处理规则:**
- **代码块必须保持完整**
- 代码块不能被截断
- 代码块及其说明保持在一起
#### 2.6 引用块 (Blockquote)
```markdown
> 这是一段引用文字
> 可以跨多行
处理规则:
- 引用块保持完整
- 不在引用块内部切分
3. 智能合并算法
智能分块使用贪心算法进行块的合并:
算法流程:
current_chunk = []
current_size = 0
target_size = chunk_token_num # 例如 256
for block in ast_blocks:
block_size = calculate_tokens(block)
# 情况 1: 当前块为空,直接添加
if current_size == 0:
current_chunk.append(block)
current_size += block_size
# 情况 2: 添加后不超过目标大小,合并
elif current_size + block_size <= target_size:
current_chunk.append(block)
current_size += block_size
# 情况 3: 添加后超过目标大小
else:
# 如果当前块已有内容,保存当前块,开始新块
if current_size >= min_chunk_tokens:
save_chunk(current_chunk)
current_chunk = [block]
current_size = block_size
# 如果当前块太小,强行添加这个 block
else:
current_chunk.append(block)
current_size += block_size
# 保存最后一个块
if current_chunk:
save_chunk(current_chunk)
示例:
假设 chunk_token_num = 300, min_chunk_tokens = 50
AST Blocks Token 数 操作 当前块大小
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Heading: "系统架构" 10 → 添加到块 1 10
Paragraph A 80 → 添加到块 1 90
Heading: "主要组件" 10 → 添加到块 1 100
Paragraph B 60 → 添加到块 1 160
List (3 items) 90 → 添加到块 1 250
Table 120 → 超过 300,保存块 1
→ 开始块 2 120
Paragraph C 70 → 添加到块 2 190
CodeBlock 80 → 添加到块 2 270
Paragraph D 50 → 超过 300,保存块 2
→ 开始块 3 50
Heading: "总结" 8 → 添加到块 3 58
Paragraph E 100 → 添加到块 3 158
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
结果:
块 1: 250 tokens (Heading + Paragraph A + Heading + Paragraph B + List)
块 2: 270 tokens (Table + Paragraph C + CodeBlock)
块 3: 158 tokens (Paragraph D + Heading + Paragraph E)
4. 特殊元素处理
表格的特殊处理
规则: 表格绝对不能被切分,即使超过 chunk_token_num。
## 数据统计
以下是详细的数据统计表格:
| 项目 | Q1 | Q2 | Q3 | Q4 |
|------|----|----|----|----|
| ... (假设表格有 400 tokens) ... |
如果 chunk_token_num = 256:
- ✅ 将整个表格(400 tokens)作为一个块
- ❌ 不会将表格拆分成两个块
前置说明文字处理:
- 如果表格前有说明文字,尽量与表格放在同一块
- 如果说明文字+表格超过
chunk_token_num * 1.5,可能分开
代码块的特殊处理
def complex_function():
# 假设这个函数有 500 tokens
...
规则:
- 代码块不能被截断
- 如果代码块超过
chunk_token_num,整个作为一个块 - 代码块前的说明文字尽量保持在一起
列表的智能切分
短列表(完整保留):
系统支持以下功能:
1. 用户管理
2. 权限控制
3. 数据分析
→ 整个列表保持在一个块中
长列表(智能切分):
详细功能列表:
1. 功能 A (包含详细说明,50 tokens)
2. 功能 B (包含详细说明,60 tokens)
3. 功能 C (包含详细说明,55 tokens)
4. 功能 D (包含详细说明,50 tokens)
5. 功能 E (包含详细说明,65 tokens)
...
如果 chunk_token_num = 256:
- 块 1: 标题 + 功能 1-3 (约 250 tokens)
- 块 2: 功能 4-5 (约 115 tokens)
关键: 在列表项之间切分,不在列表项内部切分。
5. 块大小控制
智能分块通过以下机制控制块大小:
目标大小 (chunk_token_num):
- 这是期望的块大小
- 算法会尽量接近这个值
- 但不是硬性限制
最小大小 (min_chunk_tokens):
- 小于此值的块会被合并或丢弃
- 避免产生无意义的小块
实际大小范围:
通常: [chunk_token_num * 0.7, chunk_token_num * 1.3]
特殊情况: 可能超出此范围(大表格、长代码块)
示例:
chunk_token_num = 256
min_chunk_tokens = 10
实际块大小分布:
块 1: 245 tokens ✓ 接近目标
块 2: 310 tokens ✓ 略超,但可接受(包含大表格)
块 3: 189 tokens ✓ 略小,但可接受
块 4: 8 tokens ✗ 太小,会被合并到块 3 或丢弃
块 5: 500 tokens ✓ 很大,但包含完整代码块,不能切分
配置参数
完整配置示例
{
"chunk_token_num": 256, // 目标块大小
"min_chunk_tokens": 10, // 最小块大小
"enable_heading_in_content": false, // 是否在块中包含标题层级
"enable_vision_enhancement": false, // 是否启用图片理解
"vision_description_format": "[图片描述]: {desc}",
"vision_batch_size": 3
}
参数详解
chunk_token_num (默认: 256)
作用: 每个块的目标 token 数量
推荐值:
-
短问答场景: 128-256 tokens
- 问题简单,不需要太多上下文
- 例如: FAQ,名词解释
-
通用场景: 256-512 tokens (推荐)
- 平衡检索精度和上下文完整性
- 适合大多数技术文档
-
长上下文场景: 512-1024 tokens
- 需要更多背景信息
- 例如: 教程,分析报告
如何选择:
检查文档内容密度:
- 段落短,列表多 → 较小值(128-256)
- 段落长,解释详细 → 较大值(512-1024)
- 混合内容 → 中等值(256-512)
注意: 这是目标值,不是强制值。实际块大小可能因语义单元而有所不同。
min_chunk_tokens (默认: 10)
作用: 过滤过小的文本块
推荐值:
-
严格过滤: 50-100 tokens
- 只保留有意义的内容块
- 适合高质量文档
-
宽松过滤: 10-30 tokens (推荐)
- 保留更多信息
- 适合一般文档
-
不过滤: 0-5 tokens
- 保留所有内容,包括很短的注释
- 适合简洁的技术文档
示例:
min_chunk_tokens = 50
块 A: 48 tokens → 被丢弃或合并到相邻块
块 B: 52 tokens → 保留
enable_heading_in_content (默认: false)
作用: 将标题层级路径添加到块内容开头
启用前:
系统采用三层架构设计,包括前端、业务层和数据层。
启用后:
# 用户手册 > ## 第三章 系统架构 > ### 3.1 总体设计 > 系统采用三层架构设计,包括前端、业务层和数据层。
何时启用:
- ✅ 文档层级深(3 层以上)
- ✅ 多文档混合检索(区分不同文档)
- ✅ 块需要独立理解
- ❌ 标题信息冗余
- ❌ 想节省 token
产品使用指南
1. 在 KnowFlow 中配置
步骤 1: 选择智能分块
- 创建或编辑知识库
- 在"分块方法"中选择"智能分块(Smart)" (默认选项)
- 点击"配置"进行参数调整
步骤 2: 配置基础参数
块大小配置:
块大小: 256 tokens (推荐)
最小块大小: 10 tokens (推荐)
如何选择块大小:
场景 A: FAQ 知识库
块大小: 128 tokens
最小块大小: 20 tokens
原因: 问答简短,不需要大块
场景 B: 技术文档
块大小: 512 tokens
最小块大小: 50 tokens
原因: 技术说明详细,需要完整上下文
场景 C: 混合内容
块大小: 256 tokens
最小块大小: 10 tokens
原因: 通用设置,适应各种内容
步骤 3: 高级选项
高级选项:
☐ 在内容中包含父标题
→ 仅在文档层级深时启用
☐ 启用图片理解
→ 文档包含重要图片时启用
图片描述格式: [图片描述]: {desc}
图片批量大小: 3
步骤 4: 上传文档并验证
- 上传测试文档
- 查看"分块预览"
- 检查块的大小和内容
- 根据效果调整参数
2. 验证分块效果
检查清单
✓ 块大小是否合理?
- 大部分块在目标大小附近(±30%)
- 没有过多的极大或极小块
- 特殊元素(表格、代码)完整
✓ 语义是否完整?
- 段落没有被截断
- 列表项完整
- 表格完整
- 代码块完整
✓ 块内容是否有意义?
- 每个块都包含完整的信息
- 没有孤立的标题
- 没有无意义的碎片
常见问题处理
问题 1: 有些块特别大(>1000 tokens)
原因: 包含大表格或长代码块
解决方案:
选项 A: 接受现状
大块通常是因为保持表格/代码完整性,这是正常的
选项 B: 减小块大小
{
"chunk_token_num": 512 // 从 256 增加到 512,给大元素更多空间
}
选项 C: 调整文档结构
# 修改前: 一个超大表格
# 修改后: 拆分成多个小表格
## 基础数据
| ... 小表格 ... |
## 详细数据
| ... 另一个小表格 ... |
问题 2: 块大小不均匀
原因: 文档内容密度差异大(有的地方文字多,有的地方列表多)
解决方案:
这是正常现象。智能分块会尽量保持语义完整性,块大小差异是可接受的。
如果差异过大:
{
"chunk_token_num": 384, // 增加目标大小,容纳更多内容
"min_chunk_tokens": 50 // 提高最小值,过滤碎片
}
问题 3: 有很多小块(<50 tokens)
原因: 文档有很多短段落或注释
解决方案:
提高最小块大小:
{
"min_chunk_tokens": 50 // 从 10 提高到 50
}
或者检查文档质量,移除无意义的碎片内容。
问题 4: 表格被意外切分
原因: 这不应该发生,可能是解析错误
解决方案:
- 检查 Markdown 表格格式是否正确
- 尝试不同的 PDF 解析器(MinerU/DOTS)
- 联系技术支持
3. 最佳实践
推荐配置模板
FAQ 知识库:
{
"chunk_token_num": 128,
"min_chunk_tokens": 20,
"enable_heading_in_content": false
}
技术文档(通用):
{
"chunk_token_num": 256,
"min_chunk_tokens": 10,
"enable_heading_in_content": false
}
技术教程(长上下文):
{
"chunk_token_num": 512,
"min_chunk_tokens": 50,
"enable_heading_in_content": true
}
代码文档:
{
"chunk_token_num": 384,
"min_chunk_tokens": 20,
"enable_heading_in_content": false
}
多文档混合检索:
{
"chunk_token_num": 256,
"min_chunk_tokens": 10,
"enable_heading_in_content": true // 帮助区分不同文档
}
文档结构建议
✓ 适合智能分块的文档:
## 功能介绍
系统提供以下核心功能:
1. **用户管理**: 支持用户注册、登录、权限管理等功能。
2. **数据分析**: 提供多维度的数据分析和可视化。
3. **报表生成**: 自动生成各类业务报表。
### 详细说明
用户管理模块采用 RBAC 权限模型...
| 角色 | 权限 |
|------|------|
| 管理员 | 全部权限 |
| 普通用户 | 查看权限 |
✗ 不太适合的文档:
功能A功能B功能C... (缺少结构)
或者:
巨大的单一段落,没有任何标题、列表或表格,所有内容都挤在一起...(缺少语义分隔)
使用场景
适合的场景
✅ 通用技术文档
- 包含文字、列表、表格、代码的混合内容
- 示例: 用户手册、API 文档、技术博客
✅ 没有明确标题结构的文档
- 扁平结构或标题层级不规范
- 智能分块会自动识别段落边界
✅ 代码文档和教程
- 需要保持代码块完整性
- 智能分块不会截断代码
✅ 快速开始的默认选择
- 不确定用哪种策略时,先用智能分块
- 效果通常不错,适用范围广
不适合的场景
❌ 需要严格按章节切分
- 用户需要按标题导航
- 建议使用标题分块
❌ 需要同时提供精确检索和完整上下文
- 例如法律文档、学术论文
- 建议使用父子分块
❌ 特殊格式的文本文件
- 日志文件、数据文件
- 建议使用正则分块
技术原理(简要说明)
AST 解析器
KnowFlow 使用 markdown-it-py 库解析 Markdown:
from markdown_it import MarkdownIt
md = MarkdownIt()
tokens = md.parse(markdown_text)
# tokens 包含文档的 AST 结构
Token 计数
使用 tiktoken 库计算 token 数量:
import tiktoken
encoding = tiktoken.get_encoding("cl100k_base") # GPT-4 编码
tokens = encoding.encode(text)
token_count = len(tokens)
算法复杂度
- 时间复杂度: O(n),n 为文档长度
- 空间复杂度: O(n)
- 处理速度: 中等(比标题分块慢,比父子分块快)
常见问题
1. 智能分块和标题分块有什么区别?
| 特性 | 智能分块 | 标题分块 |
|---|---|---|
| 切分依据 | 多种语义单元 | 仅标题 |
| 块大小控制 | 是 | 否 |
| 是否需要标题 | 否 | 是 |
| 适用范围 | 广 | 结构化文档 |
2. 为什么有的块超过了 chunk_token_num?
答: 这是正常的。智能分块优先保证语义完整性:
- 表格不会被切分
- 代码块不会被截断
- 段落不会被打断
3. 如何让块大小更均匀?
答:
- 调整
chunk_token_num为更大的值 - 提高
min_chunk_tokens - 如果需要绝对均匀,考虑使用正则分块(但会牺牲语义完整性)
4. 智能分块会识别 HTML 标签吗?
答: 会。如果 Markdown 中包含 HTML,智能分块会尽量保持 HTML 标签的完整性。
5. 列表很长时会如何处理?
答:
- 短列表(<5 项): 保持完整
- 长列表: 在列表项之间切分,但不会在列表项内部切分
6. 为什么推荐智能分块作为默认策略?
答:
- ✅ 适用范围最广
- ✅ 不需要文档有特定结构
- ✅ 自动平衡块大小和语义完整性
- ✅ 处理速度适中
- ✅ 配置简单