深度解析 RAG 文本分块:从基础字符到语义感知的五种演进策略
文本分块的核心逻辑
在构建大语言模型(LLM)驱动的检索增强生成(RAG)系统时,文本分块(Chunking)是决定索引质量的关键环节。之所以需要分块,主要源于两个约束:首先,LLM 的上下文窗口存在物理限制;其次,检索过长的无关信息会引入噪声,干扰模型的理解。分块的目标是将原始文档转化为语义完整的独立单元,以便在检索阶段能够精确命中用户需求。
文本分块通常涉及两个核心参数:
- 块大小 (Chunk Size):定义每个分段容纳的字符数或 Token 数。
- 块重叠 (Chunk Overlap):在相邻分段之间保留部分重复内容,以确保上下文在边界处不丢失。
第一级:基础字符分块(Character Splitting)
字符分块是最原始的方案,它不考虑语义或文档结构,仅根据固定的字符数强制切分字符串。
from langchain.text_splitter import CharacterTextSplitter
# 示例:通过固定长度进行物理切分
raw_content = "RAG systems require efficient data processing. Chunking is the foundational step for indexing."
char_processor = CharacterTextSplitter(
chunk_size=25,
chunk_overlap=5,
separator="",
strip_whitespace=True
)
segments = char_processor.create_documents([raw_content])
# 结果可能导致单词中途被截断,例如 "process" 被拆分为两个 chunk
这种方法的局限性显而易见:它极易破坏单词的完整性及句子的语义连贯性。
第二级:递归字符分块(Recursive Character Splitting)
为了优化分块效果,递归分块器采用了一组优先级递减的分隔符(通常为 \n\n, \n, " ", "")。它首先尝试在段落层面切分,如果分段依然过大,则在行、单词甚至字符层面逐步细化。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_data = """
Developing AI agents requires a deep understanding of memory management.
Traditional RAG relies on vector databases.
However, modern approaches focus on agentic reasoning and tool-use capabilities.
These systems are more dynamic and interactive.
"""
recursive_tool = RecursiveCharacterTextSplitter(
chunk_size=60,
chunk_overlap=10
)
refined_chunks = recursive_tool.create_documents([text_data])
这种方法是目前纯文本处理的最佳实践,因为它能在维持块大小的同时,尽可能保留段落和句子的结构完整性。
第三级:格式感知分块(Specialized Splitting)
对于 Markdown、代码或 PDF 等结构化程度较高的文档,通用的递归切分往往会丢失层级信息。此时需要根据特定的语法规则进行分块。
代码分块(以 Python 为例)
代码分块器能识别类、函数等语法单元,确保代码逻辑不被切断。
from langchain.text_splitter import PythonCodeTextSplitter
source_code = """
class DataAnalyzer:
def __init__(self, dataset):
self.dataset = dataset
def compute_mean(self):
return sum(self.dataset) / len(self.dataset)
def initialize_system():
print("System online")
"""
py_splitter = PythonCodeTextSplitter(chunk_size=80, chunk_overlap=0)
code_docs = py_splitter.create_documents([source_code])
多模态 PDF 处理
针对 PDF 这种复杂格式,除了提取文本,还需要处理表格和图片:
- 表格处理:建议使用解析库(如 Unstructured)将表格转换为 HTML 或 Markdown 格式,这样更符合 LLM 的预训练语料分布,利于理解。
- 图片处理:通常利用多模态模型(如 GPT-4o 或 CLIP)为图片生成描述性的摘要(Caption),随后对该摘要进行向量索引。
第四级:语义分块(Semantic Chunking)
语义分块不再依赖硬编码的分隔符,而是基于文本内容的向量相似度。其逻辑是:如果相邻两个句子在 Embedding 空间中的距离超过了某个阈值,则认为它们属于不同的语义主题,应当进行切分。
核心步骤如下:
- 将文档拆分为单句。
- 为每个句子生成 Embedding。
- 计算相邻句子(或滑动窗口内的句群)之间的余弦距离。
- 在距离突变的"极值点"设置切分边界。
# 伪代码:语义切分逻辑参考
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# 1. 获取句子向量
embeddings = model.encode(sentence_list)
# 2. 计算相邻向量的差异值
diff_scores = []
for i in range(len(embeddings) - 1):
sim = cosine_similarity([embeddings[i]], [embeddings[i+1]])[0][0]
diff_scores.append(1 - sim)
# 3. 基于分位数(如 95% 阈值)确定断点
threshold = np.percentile(diff_scores, 95)
breakpoints = [i for i, score in enumerate(diff_scores) if score > threshold]
第五级:智能代理分块(Agentic Chunking)
这是最进阶的方法,模拟人类阅读文档的逻辑,利用 LLM 作为"代理"来决策。其流程通常基于"原子命题(Propositions)"概念:
- 命题提取:利用 LLM 将段落分解为一系列独立的、事实性的原子短句(Proposition),消除代词歧义(例如将"他"替换为具体的姓名)。
- 动态聚类:代理逐个审视命题。如果命题与现有分块主题一致,则加入;否则新建一个块。
- 动态摘要:每当新命题加入,LLM 会动态更新该块的标题和摘要,作为后续决策的上下文参考。
这种方式虽然计算成本较高,但能构建出最具语义连贯性和检索精准度的索引。通过将文档原子化并由模型进行二次逻辑编排,能够极大提升 RAG 在复杂长文场景下的回答准确率。