【无标题】

文本切分三大方法:固定窗口 / 语义切分 / 递归字符切分

适用场景:RAG 系统中的 Chunking 阶段。切分质量直接决定召回率与生成质量。


一、固定窗口切分(Fixed-Size Chunking)

1.1 原理

按预设的固定长度(如 500 token)对文本进行硬切,不考虑语义边界。最简单的切分方式。

1.2 实现方式

复制代码
原文: "今天天气真好。我们去公园散步,看到了许多花朵盛开。"
窗口大小 = 10(字符)

切分结果:
Chunk1: "今天天气真好。我们去公"
Chunk2: "园散步,看到了许多花"
Chunk3: "朵盛开。"

1.3 优缺点

优点 缺点
实现极简,性能高 破坏语义完整性,句子可能被截断
切分结果可预测,便于调试 上下文割裂,影响 Embedding 质量
适合对延迟敏感的场景 长距离依赖信息丢失

1.4 适用场景

  • 日志、二进制文本等无语义结构的数据
  • 对延迟极端敏感、且对召回质量要求不高的场景
  • 通常作为 baseline 对照方案

二、递归字符切分(Recursive Character Splitting) ⭐ 推荐

2.1 原理

LangChain 默认切分器。按一组优先级递减的分隔符递归切分:

  1. 先尝试用 "\n\n"(段落)切
  2. 切出的块仍太大 → 用 "\n"(行)切
  3. 还大 → 用 " "(空格)切
  4. 还大 → 用 ""(字符)硬切

2.2 核心代码逻辑

python 复制代码
def recursive_split(text, separators, chunk_size):
 for sep in separators:
 if sep in text:
 splits = text.split(sep)
 # 合并 splits,直到累积长度 >= chunk_size
 ..
 # 兜底:按字符硬切
 return hard_cut(text, chunk_size)

默认分隔符列表(LangChain):

python 复制代码
separators = ["\n\n", "\n", " ", ""]

2.3 切分示例

复制代码
原文:
"## 标题1
这是第一段内容,讲的是 A 概念。

## 标题2
这是第二段内容,讲的是 B 概念。"

chunk_size = 30

切分过程:
1. 按 "\n\n" 切 → ["## 标题1\n这是第一段内容,讲的是 A 概念。", "## 标题2\n这是第二段内容,讲的是 B 概念。"]
2. 每段都 < 30,无需继续切

2.4 优缺点

优点 缺点
保留自然语义边界(段落/句子) 仍是基于规则,无法感知深层语义
比固定窗口效果好,实现相对简单 切分粒度依赖分隔符选择
LangChain 等框架开箱即用 对结构化文档(Markdown/LaTeX)支持一般

2.5 适用场景

  • 通用文本的首选方案(新闻、博客、报告)
  • Markdown / HTML 等半结构化文档
  • 不具备 Embedding 计算条件、又想比固定窗口更好的场景

三、语义切分(Semantic Chunking)

3.1 原理

利用 Embedding 向量化句子,根据相邻句子的语义相似度动态决定切分点:

  • 计算相邻句子(s₁, s₂)的余弦相似度
  • 相似度的位置 = 主题切换点 = 切分边界
  • 相似度高的句子合并为同一 chunk

3.2 核心流程

复制代码
输入文本
 ↓
按句子切分(s₁, s₂, s₃, .., sₙ)
 ↓
对每个句子 Embedding 得到 (v₁, v₂, .., vₙ)
 ↓
计算相邻相似度 sim(vᵢ, vᵢ₊₁)
 ↓
根据阈值或百分位切分
 ↓
合并语义相近的句子为 chunk

3.3 关键参数

  • percentile_threshold:相似度低于此百分位的位置作为切分点(如 90 表示取相似度最低的 10% 位置切分)
  • min_chunk_size:最小 chunk 大小,避免切出过短片段

3.4 代码示例(LangChain)

python 复制代码
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

text_splitter = SemanticChunker(
 OpenAIEmbeddings(),
 breakpoint_threshold_type="percentile",
 breakpoint_threshold_amount=90
)
chunks = text_splitter.split_text(text)

3.5 优缺点

优点 缺点
切分点 = 主题切换点,语义最连贯 计算成本高:每个句子都要 Embedding
召回质量最优 切分速度慢,不适合大文档
chunk 内部语义高度内聚 依赖 Embedding 模型质量
chunk 大小不可控,可能过长或过短

3.6 适用场景

  • 长文档(论文、法律文书、技术手册)
  • 对召回精度要求极高的场景
  • 预算允许且对延迟不敏感的场景

四、三者对比总结

维度 固定窗口 递归字符 语义切分
切分质量 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
切分速度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐
实现复杂度 极低
语义保持 较好
chunk 大小可控 完全可控 可控 不可控
计算成本 几乎为零 高(Embedding)
框架支持 全部 LangChain 等 LangChain Experimental
典型场景 基线/日志 通用首选 高精度检索

五、面试回答模板

问:RAG 中如何做文本切分?有哪几种方法?
:常见三种切分方式,按推荐度排序:

  1. 递归字符切分(最常用):按 "\n\n → \n → 空格 → 字符" 的优先级递归切分,既能保持语义边界,又比纯固定窗口效果好,LangChain 默认方案。
  2. 语义切分(精度最高):用 Embedding 计算句子间相似度,在主题切换点切分,召回质量最好,但计算成本高,适合对精度要求高且预算允许的场景。
  3. 固定窗口切分(基线方案):按固定长度硬切,实现简单但会破坏语义,一般作为 baseline 或对延迟极端敏感时使用。

实际项目中,我会先用递归字符切分作为默认 ,再根据召回评估结果决定是否升级到语义切分。同时配合 chunk overlap(通常 10%-20%)缓解跨 chunk 上下文断裂问题。


六、延伸考点(配合 checklist 一起看)

  • Chunk 大小选择:512 / 1024 token 是常见起点,需要根据 Embedding 模型的最长输入限制和下游 LLM 的上下文窗口共同决定。
  • Overlap 的意义:避免关键信息正好落在切分边界,典型取 chunk 大小的 10%-20%。
  • 不同文档类型的策略:
  • PDF:按页 + 段落结构(MarkdownHeaderTextSplitter)
  • Markdown:按标题层级切分
  • 代码:按 AST(抽象语法树)切分,避免函数被截断