Tutorial 3: 文本分块策略
为什么需要分块?
大语言模型有上下文长度限制,且过长的文本会影响检索效果。
问题:
┌─────────────────────────────────────────────────────────────┐
│ 一篇 10000 字的文档 │
│ │
│ · 超出 LLM 上下文限制 │
│ · 检索时整篇匹配,精度低 │
│ · 包含太多无关信息 │
└─────────────────────────────────────────────────────────────┘
解决方案:
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Chunk 1│ │ Chunk 2│ │ Chunk 3│ │ Chunk 4│ ...
│ 500字 │ │ 500字 │ │ 500字 │ │ 500字 │
└────────┘ └────────┘ └────────┘ └────────┘
· 每个块独立索引
· 精确检索相关段落
· 适配 LLM 上下文
分块的关键参数
chunk_size: 每个块的最大长度
chunk_overlap: 相邻块之间的重叠长度
分隔符: 在哪里切分文本
chunk_size = 100, chunk_overlap = 20
原文: "这是一段很长的文本,需要被分割成多个小块来处理。每个块都有固定的大小。"
Chunk 1: "这是一段很长的文本,需要被分割成多个小块来处理。"
Chunk 2: "成多个小块来处理。每个块都有固定的大小。"
↑
重叠部分
基本分块方法
1. 按字符分块
from langchain.text_splitter import CharacterTextSplitter
text = """
人工智能(AI)是计算机科学的一个分支,它企图了解智能的实质,
并生产出一种新的能以人类智能相似的方式做出反应的智能机器。
机器学习是人工智能的一个子领域,它使计算机能够从数据中学习,
而不需要被明确编程。深度学习是机器学习的一个子集,使用多层神经网络。
"""
# 按字符分块
splitter = CharacterTextSplitter(
separator="\n\n", # 首选分隔符
chunk_size=100, # 每块最大100字符
chunk_overlap=20, # 重叠20字符
length_function=len, # 长度计算函数
)
chunks = splitter.split_text(text)
for i, chunk in enumerate(chunks):
print(f"Chunk {i+1} ({len(chunk)} 字符):")
print(f" {chunk[:50]}...")
print()
2. 递归字符分块(推荐)
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 递归分块:按优先级尝试不同分隔符
splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "。", ",", " ", ""], # 分隔符优先级
chunk_size=200,
chunk_overlap=30,
length_function=len,
)
chunks = splitter.split_text(text)
print(f"分成了 {len(chunks)} 个块")
for i, chunk in enumerate(chunks):
print(f"\nChunk {i+1}:")
print(chunk)
3. 按 Token 分块
from langchain.text_splitter import TokenTextSplitter
# 按 Token 分块(更精确控制 LLM 输入长度)
splitter = TokenTextSplitter(
chunk_size=100, # 100 tokens
chunk_overlap=20, # 20 tokens 重叠
)
chunks = splitter.split_text(text)
语义分块
基于语义边界进行分块,保持内容完整性。
1. 按句子分块
import re
def split_by_sentences(text, max_sentences=5):
"""按句子分块"""
# 中文句子分割
sentences = re.split(r'([。!?])', text)
# 重组句子(保留标点)
full_sentences = []
for i in range(0, len(sentences)-1, 2):
if i+1 < len(sentences):
full_sentences.append(sentences[i] + sentences[i+1])
else:
full_sentences.append(sentences[i])
# 按固定句子数分块
chunks = []
for i in range(0, len(full_sentences), max_sentences):
chunk = ''.join(full_sentences[i:i+max_sentences])
if chunk.strip():
chunks.append(chunk)
return chunks
chunks = split_by_sentences(text, max_sentences=3)
2. 按段落分块
def split_by_paragraphs(text, max_paragraphs=2):
"""按段落分块"""
paragraphs = text.split('\n\n')
paragraphs = [p.strip() for p in paragraphs if p.strip()]
chunks = []
for i in range(0, len(paragraphs), max_paragraphs):
chunk = '\n\n'.join(paragraphs[i:i+max_paragraphs])
chunks.append(chunk)
return chunks
3. 基于嵌入的语义分块
from sentence_transformers import SentenceTransformer
import numpy as np
def semantic_chunking(text, similarity_threshold=0.7):
"""基于语义相似度的分块"""
# 先按句子分割
sentences = re.split(r'([。!?])', text)
full_sentences = []
for i in range(0, len(sentences)-1, 2):
if i+1 < len(sentences):
full_sentences.append(sentences[i] + sentences[i+1])
if not full_sentences:
return [text]
# 计算句子嵌入
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(full_sentences)
# 基于相似度分块
chunks = []
current_chunk = [full_sentences[0]]
for i in range(1, len(full_sentences)):
# 计算与当前块最后一句的相似度
sim = np.dot(embeddings[i], embeddings[i-1]) / (
np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[i-1])
)
if sim >= similarity_threshold:
current_chunk.append(full_sentences[i])
else:
chunks.append(''.join(current_chunk))
current_chunk = [full_sentences[i]]
if current_chunk:
chunks.append(''.join(current_chunk))
return chunks
特定格式分块
1. Markdown 分块
from langchain.text_splitter import MarkdownTextSplitter
markdown_text = """
# 第一章 人工智能简介
人工智能是一门研究如何使计算机能够完成智能任务的学科。
## 1.1 什么是人工智能
人工智能(AI)是指由人制造的机器所表现出来的智能。
## 1.2 AI 的历史
AI 的概念最早在 1956 年达特茅斯会议上提出。
# 第二章 机器学习
机器学习是 AI 的一个重要分支。
"""
splitter = MarkdownTextSplitter(
chunk_size=200,
chunk_overlap=20
)
chunks = splitter.split_text(markdown_text)
for i, chunk in enumerate(chunks):
print(f"Chunk {i+1}:")
print(chunk)
print("-" * 40)
2. 代码分块
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
Language
)
python_code = '''
def hello_world():
"""打印 Hello World"""
print("Hello, World!")
class Calculator:
"""简单计算器"""
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
'''
# Python 代码分块
splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=200,
chunk_overlap=20
)
chunks = splitter.split_text(python_code)
3. HTML 分块
from langchain.text_splitter import HTMLHeaderTextSplitter
html_content = """
<html>
<body>
<h1>人工智能</h1>
<p>人工智能是计算机科学的一个分支。</p>
<h2>机器学习</h2>
<p>机器学习是 AI 的子领域。</p>
<h2>深度学习</h2>
<p>深度学习使用神经网络。</p>
</body>
</html>
"""
headers_to_split_on = [
("h1", "Header 1"),
("h2", "Header 2"),
]
splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
chunks = splitter.split_text(html_content)
实战:智能分块器
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
from typing import List
import re
class SmartChunker:
"""智能文本分块器"""
def __init__(
self,
chunk_size: int = 500,
chunk_overlap: int = 50,
min_chunk_size: int = 100
):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.min_chunk_size = min_chunk_size
# 中文分隔符
self.chinese_separators = ["\n\n", "\n", "。", "!", "?", ";", ",", " "]
# 英文分隔符
self.english_separators = ["\n\n", "\n", ". ", "! ", "? ", "; ", ", ", " "]
def _detect_language(self, text: str) -> str:
"""检测文本语言"""
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
total_chars = len(text.replace(" ", ""))
if total_chars == 0:
return "english"
return "chinese" if chinese_chars / total_chars > 0.3 else "english"
def _get_separators(self, language: str) -> List[str]:
"""获取分隔符"""
return self.chinese_separators if language == "chinese" else self.english_separators
def chunk_text(self, text: str) -> List[str]:
"""分块文本"""
language = self._detect_language(text)
separators = self._get_separators(language)
splitter = RecursiveCharacterTextSplitter(
separators=separators,
chunk_size=self.chunk_size,
chunk_overlap=self.chunk_overlap,
length_function=len
)
chunks = splitter.split_text(text)
# 过滤太短的块
chunks = [c for c in chunks if len(c) >= self.min_chunk_size]
return chunks
def chunk_documents(self, documents: List[Document]) -> List[Document]:
"""分块文档列表"""
chunked_docs = []
for doc in documents:
chunks = self.chunk_text(doc.page_content)
for i, chunk in enumerate(chunks):
chunked_doc = Document(
page_content=chunk,
metadata={
**doc.metadata,
"chunk_index": i,
"total_chunks": len(chunks),
"chunk_size": len(chunk)
}
)
chunked_docs.append(chunked_doc)
return chunked_docs
# 使用智能分块器
chunker = SmartChunker(chunk_size=300, chunk_overlap=30)
# 测试中文
chinese_text = """
人工智能是计算机科学的一个重要分支,它致力于研究和开发能够模拟、延伸和扩展人类智能的理论、方法、技术及应用系统。
机器学习是人工智能的核心技术之一,它使计算机系统能够从数据中自动学习和改进,而无需进行明确的编程。
深度学习是机器学习的一个子集,它使用多层神经网络来学习数据的复杂表示和特征。
"""
chunks = chunker.chunk_text(chinese_text)
print(f"中文文本分成 {len(chunks)} 个块")
for i, chunk in enumerate(chunks):
print(f"\nChunk {i+1} ({len(chunk)} 字符):")
print(chunk)
分块策略选择指南
场景 |
推荐策略 |
chunk_size |
overlap |
|---|---|---|---|
通用问答 |
递归字符分块 |
500-1000 |
50-100 |
代码检索 |
语言感知分块 |
1000-2000 |
100-200 |
文档摘要 |
段落分块 |
1000-2000 |
100 |
精确检索 |
句子分块 |
200-500 |
20-50 |
长文档 |
语义分块 |
500-1000 |
50-100 |
关键概念总结
概念 |
解释 |
|---|---|
Chunk Size |
每个文本块的最大长度 |
Overlap |
相邻块之间的重叠部分 |
Separator |
用于分割文本的分隔符 |
递归分块 |
按优先级尝试不同分隔符 |
语义分块 |
基于语义边界进行分块 |
下一步
在下一个教程中,我们将学习向量嵌入技术。