引言
文本分块(Chunking)是RAG系统中被低估却至关重要的环节。Embedding模型再好、Reranker再精确,如果分块策略不当,检索质量也会大打折扣。
分块的目标看似简单——将长文本切成适合检索和模型处理的片段——但其中的权衡远比想象中复杂:
- 块太小:语义被切断,检索到的块缺少完整上下文
- 块太大:噪声过多,超出LLM的上下文窗口,检索精度下降
- 切错位置:在关键语义处截断,导致检索结果无法理解
┌────────────────────────────────────────────────────────────────────┐
│ 分块策略对RAG质量的影响 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ 文本内容: "RAG系统由检索器、生成器和知识库三部分组成。" │
│ │
│ ❌ 分块不佳: │
│ → Chunk 1: "RAG系统由检索器、生成器" │
│ → Chunk 2: "和知识库三部分组成。" │
│ (检索"生成器"得到Chunk 1,但完整的语义被切断了) │
│ │
│ ✅ 分块合理: │
│ → Chunk: "RAG系统由检索器、生成器和知识库三部分组成。" │
│ (语义完整,检索即可获得完整信息) │
│ │
└────────────────────────────────────────────────────────────────────┘
本文将深入剖析6种主流分块策略的实现原理、代码示例和选型指南,帮助你在不同场景下找到最优的分块方案。
1. 固定大小分块(Fixed-Size Chunking)
最简单的分块方式:按固定的字符或Token数量切割文本。
实现原理
固定大小分块 + 重叠机制示意
原始文本: "A B C D E F G H I J K L M N O P"
chunk_size = 6, chunk_overlap = 2
Chunk 1: A B C D E F
| 重叠
Chunk 2: E F G H I J
| 重叠
Chunk 3: I J K L M N
| 重叠
Chunk 4: M N O P
代码实现
from langchain.text_splitter import TokenTextSplitter
# 按Token数分块(推荐,比字符数更准确)
token_splitter = TokenTextSplitter(
chunk_size=512, # 每块512个token
chunk_overlap=128, # 128个token的重叠
encoding_name="cl100k_base", # OpenAI的编码器
)
text = "..." * 1000
chunks = token_splitter.split_text(text)
print(f"分块数: {len(chunks)}")
print(f"第一块长度: {len(chunks[0])} tokens")
from llama_index.core.node_parser import SimpleNodeParser
# LlamaIndex的固定大小分块
parser = SimpleNodeParser.from_defaults(
chunk_size=1024,
chunk_overlap=200,
)
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
nodes = parser.get_nodes_from_documents(documents)
print(f"生成节点数: {len(nodes)}")
优缺点
| 方面 | 说明 |
|---|---|
| ✅ 优点 | 实现简单、计算开销小、块大小均匀 |
| ❌ 缺点 | 无视语义边界,可能在句子中间截断、丢失文档结构信息 |
适用场景
- 快速原型验证
- 文本结构简单且统一(如日志文件、固定格式报告)
- 对检索精度要求不高的场景
2. 递归字符分块(RecursiveCharacterTextSplitter)
LangChain最推荐的默认分块策略。它优先尝试按段落(双换行)分割,如果块太大则逐级回退到更细的分隔符。
实现原理
RecursiveCharacterTextSplitter 分割流程
输入文本
│
▼
尝试用 ["\n\n"] 分割
│
├── 所有子块 < chunk_size?──► 完成
│
▼ 否
尝试用 ["\n"] 分割过大的块
│
├── 所有子块 < chunk_size?──► 完成
│
▼ 否
尝试用 ["。"] 分割(中文句号)
│
├── 所有子块 < chunk_size?──► 完成
│
▼ 否
尝试用 [". "] 分割(英文句点+空格)
│
├── 所有子块 < chunk_size?──► 完成
│
▼ 否
尝试用 [" "] 分割(空格,最后手段)
│
▼
按字符分割(保证每个块不超过 chunk_size)
代码实现
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader
# 加载文档
loader = PyPDFLoader("report.pdf")
docs = loader.load()
# 配置递归分割器——中文优化版
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=[
"\n\n", # 段落分隔(最高优先级)
"\n", # 行分隔
"。", # 中文句号
"!", # 中文感叹号
"?", # 中文问号
".\n", # 英文句号+换行
". ", # 英文句号+空格
" ", # 空格
"", # 字符级分割(最低优先级)
],
length_function=len,
keep_separator=True, # 保留分隔符在块尾
)
chunks = text_splitter.split_documents(docs)
print(f"文档数: {len(docs)}, 分块数: {len(chunks)}")
from llama_index.core.node_parser import SentenceSplitter
# LlamaIndex的SentenceSplitter(类似递归分割)
parser = SentenceSplitter(
chunk_size=512,
chunk_overlap=64,
paragraph_separator="\n\n",
secondary_chunking_regex="[^,.;。!?]+[,.;。!?]?",
)
nodes = parser.get_nodes_from_documents(documents)
for i, node in enumerate(nodes[:3]):
print(f"Node {i}: {len(node.text)} chars")
print(f" 来源: {node.metadata.get('file_name')}")
选用建议
# 根据文档类型调整 separators
# Markdown文档:保留标题层级
markdown_splitter = RecursiveCharacterTextSplitter(
chunk_size=1024,
chunk_overlap=128,
separators=[
"\n## ", # H2标题
"\n### ", # H3标题
"\n#### ", # H4标题
"\n\n", # 段落
"\n", # 行
"。", # 句号
" ", # 空格
"",
],
)
# 代码文档:保留代码块结构
code_splitter = RecursiveCharacterTextSplitter(
chunk_size=1024,
chunk_overlap=64,
separators=[
"\ndef ", # 函数定义
"\nclass ", # 类定义
"\n\n", # 空行
"\n", # 行
" ", # 空格
"",
],
is_separator_regex=True,
)
3. 语义分块(Semantic Chunking)
利用Embedding模型计算句子间的语义相似度,在语义边界处切割。这是目前最先进的分块策略,适合对检索质量要求极高的场景。
实现原理
语义分块流程
原始文本 → 句子级分割 → 计算相邻句子相似度 →
│
▼
┌─────────────────────┐
│ 相似度变化分析 │
│ │
│ 得分 │ │
│ 1.0├─┬─┬── │
│ 0.8│ │ │ ┌─ │
│ 0.6│ │ │ │ ┌─── │
│ 0.4│ │ │ │ │ ┌── │
│ └─┬─┬─┬─┬─┬──── │
│ │ │ │ │ │ │
│ 句子序列 │
└────────┬────────────┘
│
相似度骤降点 ←─────┘
│
▼
在语义边界处切分
│
▼
语义完整的文本块
代码实现
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
# OpenAI方式
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
semantic_splitter = SemanticChunker(
embeddings=embeddings,
breakpoint_threshold_type="percentile", # 或 "standard_deviation" / "interquartile"
breakpoint_threshold_amount=80, # 百分位数阈值,越高分块越少
)
text = open("long_article.txt").read()
chunks = semantic_splitter.split_text(text)
print(f"语义分块数: {len(chunks)}")
# 手动实现语义分块(更精细控制)
from sentence_transformers import SentenceTransformer
import numpy as np
class SemanticChunkerManual:
def __init__(
self,
model_name: str = "BAAI/bge-small-zh-v1.5",
threshold: float = 0.75,
min_chunk_size: int = 100,
max_chunk_size: int = 1000,
):
self.model = SentenceTransformer(model_name)
self.threshold = threshold
self.min_chunk_size = min_chunk_size
self.max_chunk_size = max_chunk_size
def split_text(self, text: str) -> list[str]:
# 1. 先按句子分割
sentences = self._split_sentences(text)
if len(sentences) <= 1:
return [text]
# 2. 计算句子Embedding
embeddings = self.model.encode(sentences)
# 3. 计算相邻句子余弦相似度
similarities = []
for i in range(len(embeddings) - 1):
sim = np.dot(embeddings[i], embeddings[i+1])
sim /= (np.linalg.norm(embeddings[i]) *
np.linalg.norm(embeddings[i+1]))
similarities.append(sim)
# 4. 在相似度骤降点处切割
chunks = []
current_chunk = []
current_size = 0
for i, sent in enumerate(sentences):
current_chunk.append(sent)
current_size += len(sent)
is_last = (i == len(sentences) - 1)
is_too_big = current_size >= self.max_chunk_size
is_semantic_break = (
i < len(similarities) and
similarities[i] < self.threshold and
current_size >= self.min_chunk_size
)
if is_last or is_too_big or is_semantic_break:
chunks.append("".join(current_chunk))
current_chunk = []
current_size = 0
return chunks
def _split_sentences(self, text: str) -> list[str]:
"""中文句子分割"""
import re
# 按中文句号/问号/感叹号/换行分割
sentences = re.split(r'(?<=[。!?\n])', text)
return [s.strip() for s in sentences if s.strip()]
优缺点
| 方面 | 说明 |
|---|---|
| ✅ 优点 | 语义完整度高、检索精度最优、自适应文本结构 |
| ❌ 缺点 | 需要额外Embedding计算、速度较慢(约2-5x)、依赖模型质量 |
适用场景
- 长文档(书籍、研究报告、法律文书)
- 对检索质量要求极高的生产系统
- 文本结构复杂、语义跨度大的文档
4. 基于文档结构的分块
利用文档本身的层级结构(标题、章节、列表等)进行分块,保持原文的逻辑框架。
实现原理
文档结构分块示例
# 用户手册
## 第一章:安装指南
### 1.1 系统要求
内容...
### 1.2 安装步骤
内容...
## 第二章:使用说明
### 2.1 基本操作
内容...
### 2.2 高级功能
内容...
↓ 基于标题层级分块
Chunk 1: "第一章:安装指南 > 1.1 系统要求\n内容..."
Chunk 2: "第一章:安装指南 > 1.2 安装步骤\n内容..."
Chunk 3: "第二章:使用说明 > 2.1 基本操作\n内容..."
Chunk 4: "第二章:使用说明 > 2.2 高级功能\n内容..."
代码实现
from langchain.text_splitter import MarkdownHeaderTextSplitter
# Markdown文档按标题分块
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "h1"),
("##", "h2"),
("###", "h3"),
]
)
md_text = """
# 产品文档
## 安装指南
### 系统要求
需要Python 3.9以上版本。
### 配置说明
修改config.yaml中的参数。
## API参考
### 认证
使用API Key进行身份验证。
"""
sections = markdown_splitter.split_text(md_text)
for section in sections:
print(f"\n--- 章节: {section.metadata} ---")
print(section.page_content[:80])
# HTML文档按标签分块
from langchain.text_splitter import HTMLHeaderTextSplitter
html_splitter = HTMLHeaderTextSplitter(
headers_to_split_on=[
("h1", "h1"),
("h2", "h2"),
]
)
html_text = """
<html>
<body>
<h1>产品文档</h1>
<h2>安装指南</h2>
<p>安装步骤说明...</p>
<h2>API参考</h2>
<p>API文档内容...</p>
</body>
</html>
"""
html_docs = html_splitter.split_text(html_text)
# LlamaIndex的MarkdownNodeParser
from llama_index.core.node_parser import MarkdownNodeParser
parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(documents)
for node in nodes:
print(f"Node: {node.text[:50]}...")
print(f"Metadata: {node.metadata}")
优缺点
| 方面 | 说明 |
|---|---|
| ✅ 优点 | 保留文档逻辑结构、元数据丰富、便于溯源 |
| ❌ 缺点 | 对非结构化文档无效、标题缺失时效果差、块大小不均匀 |
适用场景
- Markdown/HTML文档(技术文档、Wiki、博客)
- 学术论文(有明确章节)
- 法律合同和规范文档
5. 基于递归合并的分块(Recursive Chunk Merging)
在递归分割的基础上,将过小的相邻块合并,达到最小块大小要求。
实现原理
递归合并流程
输入文本
│
▼
递归分割(按照 RecursiveCharacterTextSplitter 逻辑)
│
▼
┌────────────────────────────────────────────┐
│ Chunk 1 (300 chars) ← 太小 │
│ Chunk 2 (250 chars) ← 太小 │
│ Chunk 3 (800 chars) ← 合适 │
│ Chunk 4 (200 chars) ← 太小 │
│ Chunk 5 (900 chars) ← 合适 │
└────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ Chunk 1+2 (550 chars) ← 合并后合适 │
│ Chunk 3 (800 chars) ← 保持 │
│ Chunk 4+5 (1100 chars) ← 合并后合适 │
└────────────────────────────────────────────┘
代码实现
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.text_splitter import MarkdownHeaderTextSplitter
from langchain.schema import Document
from typing import List
class MergingChunkSplitter:
"""递归合并分块器"""
def __init__(
self,
min_chunk_size: int = 300,
max_chunk_size: int = 1500,
separators: list = None,
):
self.min_chunk_size = min_chunk_size
self.max_chunk_size = max_chunk_size
self.separators = separators or ["\n\n", "\n", "。", ". ", " ", ""]
def split_documents(self, documents: List[Document]) -> List[Document]:
chunks = []
for doc in documents:
# 先递归分割
splitter = RecursiveCharacterTextSplitter(
chunk_size=self.max_chunk_size,
chunk_overlap=0,
separators=self.separators,
)
raw_chunks = splitter.split_text(doc.text)
# 合并过小的块
merged = self._merge_chunks(raw_chunks)
for chunk_text in merged:
chunks.append(Document(
page_content=chunk_text,
metadata={**doc.metadata, "merged": len(raw_chunks) > len(merged)}
))
return chunks
def _merge_chunks(self, chunks: List[str]) -> List[str]:
merged = []
buffer = ""
for chunk in chunks:
if not buffer:
buffer = chunk
else:
# 如果合并后不超过最大大小,则合并
candidate = buffer + "\n" + chunk
if len(candidate) <= self.max_chunk_size:
buffer = candidate
else:
# 如果buffer太小,强制合并
if len(buffer) < self.min_chunk_size:
buffer = candidate
else:
merged.append(buffer)
buffer = chunk
if buffer:
merged.append(buffer)
return merged
# 使用示例
splitter = MergingChunkSplitter(
min_chunk_size=300,
max_chunk_size=1200,
)
result = splitter.split_documents(docs)
print(f"分块前: 自动分割, 分块后: {len(result)}")
6. 基于Agent的智能分块
使用LLM智能识别文档中的语义边界。这是最灵活但也最昂贵的方式。
实现原理
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import Document
from typing import List
class LLMBasedChunker:
"""基于LLM的智能分块"""
def __init__(self, model: str = "gpt-4o-mini"):
self.llm = ChatOpenAI(model=model, temperature=0)
self.chunk_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个文档分块专家。请分析以下文本,将其分割成语义完整的块。
每个块应包含一个独立且完整的主题。输出格式为JSON数组:
[
"第一个块的内容...",
"第二个块的内容..."
]
分块原则:
1. 每个块必须有独立的主题和完整语义
2. 不要切断段落中间
3. 尽量让每个块在200-800个汉字之间
4. 列表和代码块不要分开
5. 如果文档太短,返回单个块"""),
("human", "{text}"),
])
def split(self, text: str) -> List[str]:
if len(text) < 1000:
return [text] # 短文档不分块
try:
response = self.llm.invoke(
self.chunk_prompt.format(text=text[:8000])
)
import json
chunks = json.loads(response.content)
return chunks if isinstance(chunks, list) else [text]
except Exception as e:
print(f"LLM分块失败: {e}, 回退到递归分割")
# 回退方案
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
return splitter.split_text(text)
# 使用示例
chunker = LLMBasedChunker()
text = open("complex_document.txt").read()
chunks = chunker.split(text)
print(f"LLM智能分块数: {len(chunks)}")
应用场景
- 高度复杂的文档结构
- 需要理解和保留领域知识的分块
- 对分块质量要求极高,且预算充足的场景
7. 分块策略对比总览
| 策略 | 语义完整性 | 实现复杂度 | 计算开销 | 执行速度 | 适用文档类型 |
|---|---|---|---|---|---|
| 固定大小 | ★★★☆☆ | ★☆☆☆☆ | ★☆☆☆☆ | 极快 | 任意(但效果差) |
| 递归字符 | ★★★★☆ | ★★☆☆☆ | ★★☆☆☆ | 快 | 通用(推荐默认) |
| 语义分块 | ★★★★★ | ★★★★★ | ★★★★★ | 慢 | 长文档、复杂文档 |
| 文档结构 | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ | 快 | Markdown/HTML |
| 递归合并 | ★★★★☆ | ★★★☆☆ | ★★☆☆☆ | 快 | 通用(块大小优化) |
| LLM智能 | ★★★★★ | ★★★★★ | ★★★★★★ | 极慢 | 复杂文档、预算充足 |
8. 块大小优化:关键权衡
块大小与检索质量的关系
检索质量 vs 块大小(典型曲线)
检索质量
↑
1.0 │ ╱╲
│ ╱ ╲
0.8 │ ╱ ╲
│ ╱ ╲
0.6 │ ╱ ╲
│╱ ╲
0.4 │ ╲
│ ╲
0.2 │ ╲
│ ╲
0.0 └──────────────────────────────────→ 块大小 (tokens)
0 256 512 768 1024 1536 2048
↑ ↑ ↑
| | |
代码片段 通用最佳 长文档
问答 配置 摘要
按文档类型推荐块大小
| 文档类型 | 推荐块大小 | 推荐重叠 | 说明 |
|---|---|---|---|
| 代码片段/问答 | 128-256 | 16-32 | 每个块聚焦单个概念 |
| 新闻文章/博客 | 256-512 | 32-64 | 段落级粒度 |
| 技术文档/教程 | 512-1024 | 64-128 | 章节级粒度 |
| 学术论文 | 512-1024 | 64-128 | 按小节分割 |
| 法律合同/政策 | 1024-2048 | 128-256 | 需要较长上下文 |
| 书籍/手册 | 1024-2048 | 128-256 | 按章节分割 |
| 对话/聊天记录 | 256-512 | 32-64 | 按对话轮次分割 |
块大小优化的实验方法
import numpy as np
from langchain.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
def evaluate_chunk_sizes(
documents: list,
test_queries: list,
chunk_sizes: list = [256, 512, 768, 1024, 1536],
chunk_overlap_ratio: float = 0.2,
):
"""评估不同块大小的检索质量"""
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
results = {}
for chunk_size in chunk_sizes:
overlap = int(chunk_size * chunk_overlap_ratio)
# 分块
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
)
chunks = splitter.split_documents(documents)
# 建索引
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 评估
relevance_scores = []
for query in test_queries:
retrieved = retriever.get_relevant_documents(query)
# 简单的评估:计算检索到的块与查询的语义相似度
query_emb = embeddings.embed_query(query)
chunk_emb = embeddings.embed_documents([d.page_content for d in retrieved])
scores = [
np.dot(query_emb, ce) / (np.linalg.norm(query_emb) * np.linalg.norm(ce))
for ce in chunk_emb
]
relevance_scores.append(np.mean(scores))
results[chunk_size] = {
"mean_relevance": np.mean(relevance_scores),
"num_chunks": len(chunks),
"avg_chunk_len": np.mean([len(c.page_content) for c in chunks]),
}
print(f"chunk_size={chunk_size}: "
f"相关性={np.mean(relevance_scores):.4f}, "
f"分块数={len(chunks)}")
return results
# 运行评估
results = evaluate_chunk_sizes(
documents=docs,
test_queries=[
"RAG系统的核心组件有哪些?",
"如何优化检索精度?",
"文本分块的最佳实践",
],
)
9. 最佳实践与常见陷阱
最佳实践
-
从递归字符分割器开始:大多数场景下,
RecursiveCharacterTextSplitter是平衡质量和复杂度的最佳选择。 -
根据文档类型定制 separators:中文文档必须包含中文标点(。!?)作为分隔符。
-
重叠很重要:建议重叠比例在10%-25%之间,避免关键信息落在块边界。
-
块大小不超过 Embedding 模型最大输入长度:如 text-embedding-3-small 最大 8191 tokens,bge-small-zh 最大 512 tokens。
-
保留元数据:将源文件名、页码、章节标题等作为每个块的元数据。
# 最佳实践示例
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
def chunk_with_metadata(
text: str,
metadata: dict,
chunk_size: int = 512,
chunk_overlap: int = 64,
) -> list[Document]:
"""带元数据的智能分块"""
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=["\n\n", "\n", "。", "!", "?", ". ", " ", ""],
)
chunks = splitter.split_text(text)
documents = []
for i, chunk_text in enumerate(chunks):
doc = Document(
page_content=chunk_text,
metadata={
**metadata,
"chunk_index": i,
"total_chunks": len(chunks),
"chunk_size": len(chunk_text),
}
)
documents.append(doc)
return documents
常见陷阱
| 陷阱 | 问题 | 解决方案 |
|---|---|---|
| ❌ 忽略重叠 | 关键信息被切断,搜索不到 | 设置10%-25%的重叠率 |
| ❌ 块太大 | 超过模型上下文窗口 | 控制在LLM最大输入长度的30%以内 |
| ❌ 中文用英文分隔符 | 中文句号未被识别 | 必须包含。!?等中文分隔符 |
| ❌ 所有文档用同一策略 | 不同类型文档结构差异大 | 按文档类型定制策略 |
| ❌ 忽略Embedding限制 | 块超过模型最大输入 | 确认模型的max_seq_length |
| ❌ 不保留元数据 | 无法追溯来源 | 记录源文件、页码、标题等 |
10. 总结
文本分块是RAG系统中技术含量高、影响深远的环节。没有”放之四海而皆准”的最佳策略,关键在于理解不同策略的适用场景和权衡:
- 快速原型 → 固定大小分块
- 通用生产系统 → 递归字符分块(
RecursiveCharacterTextSplitter) - 长文档/高精度需求 → 语义分块
- 结构化文档 → 基于文档结构的分块
- 块大小不均匀问题 → 递归合并分块
- 复杂文档/预算充足 → LLM智能分块
选择分块策略后,务必通过实验验证效果:构建评估集、测试不同块大小、监控检索质量指标。只有经过严格实验验证的分块策略,才能支撑起一个高质量的RAG系统。
# 最终推荐:生产级分块配置
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
# 1. 分块配置(中文优化)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=["\n\n", "\n", "。", "!", "?", ". ", " ", ""],
keep_separator=True,
)
# 2. 加载和分块
documents = text_splitter.split_documents(raw_docs)
# 3. 向量化(确保模型支持您的chunk_size)
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5", # 最大512 tokens
encode_kwargs={"normalize_embeddings": True},
)
# 4. 构建索引
vectorstore = Chroma.from_documents(
documents=documents,
embedding=embeddings,
collection_metadata={"hnsw:space": "cosine"},
)
记住:分块策略的选择永远服务于检索质量。多实验、多评估,找到最适合您数据的分块方案。