Appearance
四、文档切分
4.1 为什么要切分
获取 Document 对象后,需要将其切分成 Chunk(文本块),以 Chunk 为基本单位进行存储和检索。
| 问题 | 说明 |
|---|---|
| Token 限制 | LLM 的上下文窗口有限,无法一次性处理整篇长文档 |
| 检索不精确 | 如果不切分,检索时返回整篇文档,噪声过多,LLM 容易产生幻觉 |
| 向量化成本高 | 整篇文档做 Embedding,计算和存储成本都很高 |
| 语义模糊 | 嵌入模型对短文本的语义表达更精确,长文本的向量表示会"稀释"关键信息 |
4.2 切分的核心考量
既然要切分,那么怎么切、在哪儿切就成了关键问题。切分需要平衡三个维度:
| 考量维度 | 说明 | 过犹不及的后果 |
|---|---|---|
| 长度控制 | 每个块不能太大,也不能太小 | 太大:检索噪声多;太小:语义不完整 |
| 语义完整 | 尽量不让句子被切断,保持意思连贯 | 断句会导致上下文断裂,影响理解 |
| 边界合理 | 在自然分隔处切分,而非强行截断 | 强行截断会丢失重要信息 |
基于这些考量,LangChain 提供了多种切分策略供选择。
4.3 切分策略对比
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 固定长度切分 | 按固定字符/Token数切分 | 简单直接,块大小均匀 | 可能在不当位置断句 |
| 语义切分 | 对相邻句子嵌入,找语义变化大的位置作为切分点 | 语义完整性最好 | 速度慢,块大小不均衡 |
| 递归多分隔符切分 | 依次尝试多个分隔符切分,优先用大分隔符,逐步降级到小分隔符 | 保持语义完整,块大小可控 | 需要调整分隔符顺序 |
4.4 递归字符切分器
为什么选择它? 固定长度切分太粗暴,语义切分太慢且块大小不可控。递归字符切分器介于两者之间——既保证语义完整性,又能控制块大小,是实际应用中最常用的折中方案。
4.4.1 工作原理
核心思想:定义分隔符优先级列表(如 ["\n\n", "\n", " ", ""]),按优先级依次尝试切分:
- 先用最高优先级分隔符(如
\n\n)切分整段文本 - 检查每个切分块是否超过
chunk_size - 若超限,对该块降级使用下一级分隔符继续切分
- 递归直到所有块都不超限,或降级到字符级切分(
"")
分隔符优先级的语义含义:
| 分隔符 | 语义边界 | 说明 |
|---|---|---|
\n\n | 段落边界 | 最优先保持段落完整 |
\n | 行边界 | 段落超限时,尝试保持行完整 |
| 词边界 | 行超限时,尝试保持词完整 |
"" | 字符级 | 最后手段,强制按字符截断 |

关键结论:
- 分隔符优先级越高,切分越粗粒度,语义完整性越好
- 当分隔符无法匹配时,自动降级到下一级
4.4.2 重叠的作用
相邻块之间保留一部分重叠内容,防止切分边界处丢失关键信息。

4.4.3 代码示例
python
# pip install langchain-text-splitters
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import UnstructuredWordDocumentLoader
# 加载文档
docs = UnstructuredWordDocumentLoader(
file_path="assets/sample.docx",
mode="single"
).load()
# 切分为文本块
chunks = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "。", "!", "?", "……", ",", ""], # 分隔符优先级列表
chunk_size=400, # 每个块的最大长度
chunk_overlap=50, # 相邻块重叠长度
length_function=len, # 长度计算函数
).split_documents(docs)
# 统计块数量
print(f"切分得到 {len(chunks)} 个文本块\n")
# 可选:查看前几个块内容和元数据
for i, chunk in enumerate(chunks[:5]): # 只看前 5 个
print(f"=== Chunk {i} ===")
print(chunk.page_content)
print("metadata:", chunk.metadata)
print("============\n")切分后,每个 Chunk 仍然是一段文本。为了让计算机能够衡量"语义上有多相似",我们需要把文本转化为数值向量——这就是下一步"嵌入"要做的事。