Document Chunking
Document Chunking 是将长文档切分成小块(Chunk)的过程。块太大浪费 token,块太小丢失上下文,分块策略直接影响 RAG 质量。
#type / concept
#status / evergreen
#tech / ai
[!info] related notes
- 所属 MOC: RAG Engineering MOC
- 相关: Chunk Metadata, Embedding Model
Document Chunking
一句话定义
Document Chunking 是将长文档切分成小块(Chunk)的过程。LLM 的上下文窗口有限,不可能把整本书塞进去。需要切成小块,只检索最相关的块。
核心原理
分块策略
| 策略 | 原理 | 优缺点 |
|---|---|---|
| 固定长度 | 每 N 个字符切一块 | 简单但可能切断句子 |
| 按句子 | 按句号切分 | 保持语义完整 |
| 按段落 | 按空行切分 | 保持段落完整 |
| 按语义 | 用 Embedding 判断语义边界 | 最智能但最慢 |
| 递归分割 | 先按大分隔符,再按小分隔符 | LangChain 推荐 |
递归分割实现
class RecursiveChunker:
def __init__(self, chunk_size: int = 500, chunk_overlap: int = 50):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.separators = ["\n\n", "\n", "。", ".", " "]
def chunk(self, text: str) -> list[str]:
return self._split(text, self.separators)
def _split(self, text: str, separators: list[str]) -> list[str]:
if len(text) <= self.chunk_size:
return [text]
# 找到合适的分隔符
separator = separators[0]
for sep in separators:
if sep in text:
separator = sep
break
# 按分隔符切分
splits = text.split(separator)
# 合并小块
chunks = []
current = ""
for split in splits:
if len(current) + len(split) + len(separator) <= self.chunk_size:
current += (separator if current else "") + split
else:
if current:
chunks.append(current)
current = split
if current:
chunks.append(current)
# 添加重叠
if self.chunk_overlap > 0:
chunks = self._add_overlap(chunks)
return chunks
Chunk 大小选择
| 场景 | 推荐大小 | 说明 |
|---|---|---|
| 问答 | 200-500 字 | 精确回答 |
| 摘要 | 500-1000 字 | 需要更多上下文 |
| 代码 | 按函数/类 | 保持代码完整 |
常见坑
- 块太大: 浪费 token,检索不精确
- 块太小: 丢失上下文,信息不完整
- 切断句子: 固定长度切分可能切断句子中间
- 不做重叠: 相邻块之间没有重叠,边界信息丢失