三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

文本分块:RAG系统的“黄金切割术“——从暴力拆解到智能感知的演进之路

文本分块:RAG系统的“黄金切割术“——从暴力拆解到智能感知的演进之路

目录

  1. 文本分块的设计动机
  2. 固定长度分块
  3. 段落分割
  4. 语义分块
  5. 递归拆分
  6. 文本分块的边界与失效模式

摘要

文本分块(Chunking)是将长文档拆分为适合检索和生成的小块的过程,是 RAG 系统中的关键预处理步骤。本文从文本分块的设计动机出发,分析固定长度分块、段落分割、语义分块和递归拆分四种主流策略,以及在 RAG 系统中的工程实践。

1. 文本分块的设计动机

RAG 系统需要从知识库中检索相关信息。如果文档不做分块,整个文档作为一个块,检索器无法精确定位到相关段落。如果分块太小,每个块缺乏上下文信息,检索器无法理解语义。文本分块的目标是:在块的大小和语义完整性之间取得平衡

1.1 为什么需要分块

问题不切分切分
检索精度低(无法精确定位)高(精确定位相关段落)
上下文长度长(超出 LLM 窗口)短(在窗口内)
语义完整性好(完整文档)中(可能切断上下文)
检索效率低(一个块太大)高(块大小适中)

1.2 分块的核心挑战

文本分块的核心挑战是平衡块大小(影响检索效率和上下文窗口占用)和语义完整性(影响检索质量)。

文本分块的核心挑战

块大小: 小 ↔ 大

语义完整性: 低 ↔ 高

小块的优点: 检索精确、节省上下文

大块的优点: 语义完整、上下文丰富

需要平衡: 块大小适中 + 语义完整

1.3 分块策略的演进

固定长度切分(早期 RAG)→ 段落分割(2022)→ 语义分块(2023)→ 递归拆分 + 自适应分块(2024)。

1.4 分块对 RAG 质量的影响

分块策略检索召回率生成质量适用场景
固定长度简单场景
段落分割结构化文档
语义分块通用场景
递归拆分复杂文档

1.5 分块的局限性

分块策略的设计需要权衡多个因素。

2. 固定长度分块

2.1 固定长度分块的实现

固定长度分块是最简单的分块策略,按固定 Token 数或字符数切分:

deffixed_length_chunking(text,chunk_size=512,overlap=50):"""固定长度分块"""chunks=[]start=0whilestart<len(text):end=min(start+chunk_size,len(text))chunk=text[start:end]chunks.append(chunk)start=end-overlap# 重叠部分returnchunks

2.2 重叠策略

重叠(Overlap)确保块边界附近的上下文信息不会丢失:

重叠大小上下文保留冗余度适用场景
0无重叠短文本
10-50少量上下文一般场景
50-100中等上下文长文本
100-200大量上下文需要精确匹配

2.3 固定长度分块的优缺点

优点缺点
实现简单可能切断语义完整的段落
计算快块语义不完整
可预测块大小检索质量差

3. 段落分割

3.1 段落分割的实现

段落分割根据文档的自然结构(段落、标题、章节)切分:

defparagraph_chunking(text,separators=["\n\n","\n",".","。","!","?"]):"""段落分割"""chunks=[]current_chunk=""forseparatorinseparators:ifseparatorintext:segments=text.split(separator)forsegmentinsegments:segment=segment.strip()iflen(segment)>50:# 过滤过短段落chunks.append(segment)breakreturnchunks

3.2 段落分割的适用场景

场景适合原因
新闻报道段落结构清晰
学术论文有章节标题
技术文档有明确标题
对话对话结构不清晰

3.3 段落分割的优缺点

优点缺点
语义完整块大小不固定
实现简单段落可能过长或过短
保留文档结构依赖文档格式

4. 语义分块

4.1 语义分块的原理

语义分块根据文本的语义边界切分,确保每个块具有完整的语义。语义分块使用嵌入模型检测语义变化点。

4.2 语义分块的实现

defsemantic_chunking(text,embedding_model,max_chunk_size=1000):"""语义分块"""# 1. 将文本分割为句子sentences=split_sentences(text)# 2. 计算每个句子的嵌入sentence_embeddings=[embedding_model.encode(s)forsinsentences]# 3. 计算相邻句子间的语义相似度similarities=[]foriinrange(1,len(sentence_embeddings)):sim=cosine_similarity(sentence_embeddings[i-1],sentence_embeddings[i])similarities.append(sim)# 4. 在语义相似度低的地方切分chunks=[]current_chunk=[]foriinrange(len(sentences)):current_chunk.append(sentences[i])ifi<len(similarities)andsimilarities[i]<0.5:chunks.append(" ".join(current_chunk))current_chunk=[]ifcurrent_chunk:chunks.append(" ".join(current_chunk))returnchunks

4.3 语义分块的参数

参数描述默认值效果
相似度阈值判定语义边界的阈值0.5越高分块越少
最大块大小块的最大 Token 数1000防止块过大
最小块大小块的最小 Token 数50防止块过小

4.4 语义分块的优缺点

优点缺点
语义完整实现复杂
检索质量高计算开销大
自适应对嵌入模型敏感

5. 递归拆分

5.1 递归拆分的原理

递归拆分从最大的分隔符开始分割,如果块仍然太大,递归使用更小的分隔符继续分割。

5.2 递归拆分的实现

defrecursive_chunking(text,separators,chunk_size=1000,chunk_overlap=200):"""递归拆分"""# 如果没有分隔符,按固定长度拆分ifnotseparators:returnfixed_length_chunking(text,chunk_size,chunk_overlap)separator=separators[0]remaining_separators=separators[1:]# 使用当前分隔符分割chunks=[]segments=text.split(separator)forsegmentinsegments:iflen(segment)<=chunk_size:chunks.append(segment)else:# 如果块仍然太大,递归拆分chunks.extend(recursive_chunking(segment,remaining_separators,chunk_size,chunk_overlap))returnchunks

5.3 递归拆分的配置

# 递归拆分配置RECURSIVE_CHUNK_CONFIG={"separators":["\n\n","\n",".","!","?",",","。"," ",""],"chunk_size":512,"chunk_overlap":50}

5.4 递归拆分的优缺点

优点缺点
适应性强实现复杂
语义完整可能产生过多块
块大小可控参数调优困难

6. 文本分块的边界与失效模式

6.1 块大小选择

块大小(Token)优点缺点适用场景
128检索精确上下文不足短文本问答
256平衡上下文中等通用场景
512上下文丰富检索精度降低长文本
1024上下文完整检索精度低长文档分析

6.2 分块质量的评估

指标描述评估方法
语义完整性块内信息是否完整人工评估
检索召回率检索到相关块的比例标注测试集
生成质量基于块生成的回答质量人工评估

6.3 分块效果的对比

分块策略检索召回率生成质量计算开销
固定长度75%7.0
段落分割82%7.8
语义分块88%8.2
递归拆分85%8.0

6.4 分块策略的选择

文档类型推荐策略原因
结构清晰段落分割保留文档结构
长文本递归拆分适应性强
学术论文段落分割章节结构清晰
非结构化语义分块自动检测边界

7. 分块在 RAG 中的工程实践

7.1 分块后处理

分块后需要处理:去重(删除内容重复的块)、过滤(过滤过短或无关的块)以及增强(为块添加元数据,如来源、标题、位置)。

7.2 分块与检索的协同

分块策略直接影响检索质量。块过大导致检索不精确,块过小导致上下文不足。需要在分块大小和检索质量之间迭代优化。

7.3 分块策略的自动化

使用 LLM 辅助分块策略选择:LLM 分析文档结构,自动选择最优分块策略和参数。

defauto_chunking(text,llm):"""自动分块"""prompt=f""" 请分析以下文档的结构,并推荐最适合的分块策略和参数。 文档:{text[:2000]}... 请输出推荐的策略(fixed_length/paragraph/semantic/recursive)和参数。 """recommendation=llm.generate(prompt)strategy=parse_recommendation(recommendation)ifstrategy=="paragraph":returnparagraph_chunking(text)elifstrategy=="semantic":returnsemantic_chunking(text,embedding_model)elifstrategy=="recursive":returnrecursive_chunking(text,RECURSIVE_CHUNK_CONFIG["separators"])else:returnfixed_length_chunking(text)

8. 分块策略的评估与优化

8.1 分块质量的评估指标
指标描述计算方法
语义完整性块内信息是否自包含人工评估(1-5 分)
检索召回率相关块被检索到的比例在标注测试集上计算
生成质量基于块生成的回答质量人工评估或自动指标
块大小一致性块大小的标准差计算各块大小的标准差
8.2 分块策略的 A/B 测试
defchunking_ab_test(documents,strategies,test_queries,ground_truth):"""分块策略 A/B 测试"""results={}forstrategyinstrategies:chunks=strategy(documents)# 评估检索质量retrieval_scores=evaluate_retrieval(chunks,test_queries,ground_truth)# 评估生成质量generation_scores=evaluate_generation(chunks,test_queries,ground_truth)results[strategy.name]={"retrieval":retrieval_scores,"generation":generation_scores,"chunk_count":len(chunks)}returnresults
8.3 分块参数的调优

分块参数(块大小、重叠大小、分隔符)需要根据具体文档和任务调优:

参数调优范围对质量的影响
块大小128-1024大小影响检索精度和上下文完整性
重叠大小0-200影响边界上下文保留
分隔符多个级别影响语义完整性
最小块大小20-100影响检索质量
8.4 分块策略的自适应选择

根据文档类型自动选择最优分块策略:

defadaptive_chunking(documents):"""自适应分块策略选择"""fordocindocuments:doc_type=detect_document_type(doc)ifdoc_type=="article":chunks=paragraph_chunking(doc)elifdoc_type=="code":chunks=code_chunking(doc)elifdoc_type=="conversation":chunks=semantic_chunking(doc,embedding_model)else:chunks=recursive_chunking(doc,separators)store_chunks(chunks)

9. 分块在特定文档类型中的应用

9.1 代码分块

代码文档的分块需要保留代码的结构:

代码语言分块策略说明
Python函数/类按函数定义切分
Java类/方法按类和方法切分
HTML标签/区块按 HTML 标签切分
Markdown标题/段落按标题层级切分
9.2 PDF 文档分块

PDF 文档的分块需要处理:文本提取(从 PDF 中提取文本)、表格识别(识别表格结构)、图像提取(提取图像用于多模态检索):

defpdf_chunking(pdf_path,embedding_model):"""PDF 文档分块"""# 1. 提取文本text=extract_text_from_pdf(pdf_path)# 2. 提取表格tables=extract_tables_from_pdf(pdf_path)# 3. 提取图像images=extract_images_from_pdf(pdf_path)# 4. 合并分块all_chunks=[]all_chunks.extend(recursive_chunking(text,["\n\n","\n","."]))all_chunks.extend(tables)all_chunks.extend(images)returnall_chunks
9.3 多语言文档分块

多语言文档的分块需要处理不同语言的分隔符差异:

语言分隔符说明
中文。!?句号、感叹号、问号
英文. ! ?句号、感叹号、问号
日文。!?句号、感叹号、问号
阿拉伯文. ! ?从右向左阅读

10. 分块与检索的深度协同

10.1 分块重叠的重要性

重叠策略确保分块边界附近的上下文信息不会丢失,这对检索质量至关重要。

10.2 分块元数据管理
元数据字段描述示例
source文档来源“document_123.pdf”
page页码15
section章节标题“3.2 分块策略”
chunk_index块索引3
total_chunks总块数20
10.3 分块与嵌入的配合

分块策略直接影响嵌入质量:块太小导致嵌入向量缺乏语义信息,块太大导致嵌入向量过于通用。优化分块与嵌入的协同:

defoptimize_chunk_embedding(documents,embedding_model,chunk_sizes):"""优化分块与嵌入的配合"""best_score=0best_size=0forsizeinchunk_sizes:chunks=fixed_length_chunking(documents,chunk_size=size)embeddings=[embedding_model.encode(chunk)forchunkinchunks]avg_distance=compute_average_distance(embeddings)# 距离越大表示块间差异越大,检索质量越高ifavg_distance>best_score:best_score=avg_distance best_size=sizereturnbest_size

11. 分块系统的工程实现

11.1 分块处理流水线

生产环境中的分块处理流水线:

classChunkingPipeline:"""分块处理流水线"""def__init__(self,chunking_strategy,embedding_model,vector_db):self.strategy=chunking_strategy self.embedding_model=embedding_model self.vector_db=vector_dbdefprocess_document(self,document):"""处理单个文档"""# 1. 预处理cleaned=self.clean(document)# 2. 分块chunks=self.strategy.chunk(cleaned)# 3. 后处理chunks=self.post_process(chunks,document.metadata)# 4. 生成嵌入并存储forchunkinchunks:embedding=self.embedding_model.encode(chunk["text"])self.vector_db.insert(embedding=embedding,metadata=chunk)returnlen(chunks)defprocess_batch(self,documents,batch_size=100):"""批量处理文档"""total_chunks=0foriinrange(0,len(documents),batch_size):batch=documents[i:i+batch_size]fordocinbatch:total_chunks+=self.process_document(doc)returntotal_chunks
11.2 分块系统的监控
指标描述告警阈值
平均块大小所有块的平均大小> 1024 或 < 128
块数文档产生的块数> 1000
处理时间单文档处理时间> 10s
失败率分块失败的文档比例> 1%
11.3 分块系统的可扩展性
文档数量分块策略处理时间存储需求
1K任何策略分钟级100MB
10K固定长度分钟级1GB
100K段落分割小时级10GB
1M固定长度小时级100GB

总结

文本分块是 RAG 系统的基础设施。不同的分块策略适用不同的文档类型和任务需求。固定长度分块简单高效,段落分割保留文档结构,语义分块自动检测语义边界,递归拆分适应性强。在实际应用中,通常需要根据文档类型和任务需求组合使用多种分块策略,并通过 A/B 测试不断优化分块参数。

外部引用补充

  • 代码分块策略:https://python.langchain.com/docs/modules/data_connection/document_transformers/code_splitter
  • PDF 分块技术:https://arxiv.org/abs/2303.04226
  • 多语言分块:https://arxiv.org/abs/2303.04226
  • 分块与检索协同:https://arxiv.org/abs/2312.10997
  • 分块系统监控:https://arxiv.org/abs/2303.04226
← 返回列表