支持私有化部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


RAG落地实战之文本切分4种策略全解析

发布日期:2025-04-25 19:09:55 浏览次数: 1530 作者:AI悠悠
推荐语

RAG技术在文本处理中的应用,提升检索和生成效率。

核心内容:
1. RAG技术流程及文本切分的重要性
2. 文本切分策略的三要素及不同策略对比
3. 推荐的切分策略组合及切分器详细介绍

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

? 一、什么是 RAG?

RAG 是一种结合了检索(Retrieval)和生成(Generation)的 AI 技术。它的流程通常包括以下几个阶段:

  • 文本切分(Text Splitting)

  • 向量化编码(Embedding)

  • 存入向量数据库(如 FAISS / Chroma / Milvus 等)

  • 检索相似文段

  • 生成回答


? 二、为什么要进行文本切分?

大模型(如 GPT)并不能直接检索整篇文档。我们必须先把文档切分成合适大小的段落(chunk),再对每个段落进行嵌入(embedding)。切得太细,会失去上下文;切得太粗,会导致嵌入不准确或超过上下文窗口。

文本切分在 RAG 中非常关键,因为切不好可能会:

  • ✂️ 打断语义单位(比如一个句子切了一半)

  • ? 降低召回准确率

  • ? 让 LLM 理解不完整,输出变差


文本切分的目标是:

  •  将大文档划分为若干小块(chunk),便于嵌入(Embedding)与后续的检索(Retrieval)。

  • 切分得合理,检索更精准。


? 三、文本切分策略

分块三要素:

要素
说明
推荐值
块大小
每段文字的长度
200-500字
块重叠
相邻块重复内容
10%-20%
切分依据
按句子/段落/语义划分
语义分割最优


分块策略对比表:

策略类型
优点
缺点
适用场景
固定大小
实现简单
可能切断完整语义
技术文档
按段落分割
保持逻辑完整性
段落长度差异大
文学小说
语义分割
确保内容完整性
计算资源消耗较大
专业领域文档


LlamaIndex 提供了多个内置的 TextSplitter 类,来应对不同语言与结构的文档。

常用切分器:  

TextSplitter 类型
适用情况
中文支持
SentenceSplitter
按语句分割(适合自然语言)
✅ 很适合中文
TokenTextSplitter
按 Token 数量分块
✅ 精确控制 LLM 输入
SentenceWindowNodeParser
句子窗口法(重叠段)
✅ 适合上下文连续的文档

SemanticSplitterNodeParser


基于语义切分(用小模型判断)
✅ 高级但稍慢

✅ 四、推荐切分策略组合

  • 简单文档:用 SentenceSplitter + 固定 chunk size

  • 上下文要求高:用 SentenceWindowNodeParser句子窗口,保持上下文连续

  • 高质量 QA:用 SemanticSplitterNodeParser,语义分块(需额外安装小模型)


? 五、切分器详细介绍

1、语句切分SentenceSplitter

解析文本时优先考虑完整的句子;此类会尝试将句子和段落保持在一起。

参数:

名称 类型 描述 默认
chunk_size int

每个块的token大小。

1024
chunk_overlap int

分割时每个块的 token 重叠。

200
separator str

分割单词的默认分隔符

' '空格


separator 非常关键,多语言常见句尾标点:

  • 中午断在 “。!?\n”

  • 英语断在 “.!?\n”

  • 西语断在 “¡¿”

paragraph_separator str

段落之间的分隔符。

'\n\n\n'
secondary_chunking_regex

str|

None

用于拆分句子的备份正则表达式。

'[^,.;。?!]+[,.;。?!]?|[,.;。?!]'

 安装依赖

pip install llama-index llama-index-embeddings-huggingface

 split_text_demo.py

from llama_index.core import SimpleDirectoryReaderfrom llama_index.core.node_parser import SentenceSplitter# 1. 加载文档documents = SimpleDirectoryReader(input_files=[r"D:\Test\LLMTrain\day20_llamaindex\data\ai.txt"]).load_data()#print(f"原始文档数量:{len(documents)}")# 2. 创建 SentenceSplittersentence_splitter = SentenceSplitter(    chunk_size=100,    chunk_overlap=10,    separator="。!?!?.\n¡¿",  # 适配中文,英语,西班牙语三种语言的分隔符                )# 3. 分割文档nodes = sentence_splitter.get_nodes_from_documents(documents)print(f"生成节点数: {len(nodes)}")print("分块示例的前3部分的长度:", [len(n.text) for n in nodes[:3]])print("\n? 分割结果示例:")for i, node in enumerate(nodes[:3]):    print(f"\nChunk {i + 1}:\n{node}")

关于chunk_overlap

chunk_overlap 表示相邻两个文本块之间“重叠”的字符数或 token 数。

它的作用是:

  • 保留上下文连续性(避免重要信息断裂)

  • 提升检索和生成质量(比如多轮问答)

有时即便设置了值,但分隔的语句并没有重叠部分。因为是优先按照“语句边界”来切分。

  • 它不是严格按字符数定长分块的

  • 所以 chunk_overlap 是“尽量重叠语句”,而不是精确控制字符重叠


2、固定分块切分

TokenTextSplitter 根据固定的Token数量切分,目前使用场景较少。

参数:

姓名 类型 描述 默认
chunk_size int

每个块的token块大小。

1024
chunk_overlap int

分割时每个块的 token 重叠。

20
separator str

分割单词的默认分隔符

' '
backup_separators List

用于分割的附加分隔符。

<dynamic>
keep_whitespaces bool

是否保留块中的前导/尾随空格。

False

token_text_splitter_demo.py

#使用固定节点切分from llama_index.core import SimpleDirectoryReaderfrom llama_index.core.node_parser import TokenTextSplitter
documents = SimpleDirectoryReader(input_files=[r"D:\Test\LLMTrain\day20_llamaindex\data\ai.txt"]).load_data()
fixed_splitter = TokenTextSplitter(chunk_size=256, chunk_overlap=20)fixed_nodes = fixed_splitter.get_nodes_from_documents(documents)print("固定分块示例:", [len(n.text) for n in fixed_nodes[:3]])print(print("首个节点内容:\n", fixed_nodes[0].text))print("===========")print(print("第二个节点内容:\n", fixed_nodes[1].text))

3、句子窗口切分

SentenceWindowNodeParser 是 LlamaIndex 提供的一个高级文本解析器,专为 RAG(Retrieval-Augmented Generation)场景设计。它的核心作用是将文档按句子拆分,并为每个句子节点附加其前后若干句子的上下文信息,从而在检索和生成阶段提供更丰富的语义背景。 

核心功能

  • 句子级切分:将文档拆分为独立的句子,每个句子作为一个节点(Node)。

  • 上下文窗口:为每个句子节点附加其前后若干句子的内容,形成一个“句子窗口”,以提供上下文信息。

  • 元数据存储:上下文窗口信息存储在节点的元数据中,便于后续检索和生成阶段使用。

参数:

姓名
类型
描述
默认
sentence_splitter Optional[Callable]

将文本拆分成句子

<function split_by_sentence_tokenizer.<locals>.<lambda> at 0x7b5051030a40>
include_metadata bool

是否在节点中包含元数据

必需的
include_prev_next_rel bool

是否包含上一个/下一个关系

必需的
window_size int
指定每个句子节点前后包含的句子数量
3
window_metadata_key str
存储上下文窗口信息的元数据键名
'window'
original_text_metadata_key str

用于存储原始句子的元数据键。

'original_text'


from llama_index.core  import Documentfrom llama_index.core import SimpleDirectoryReaderfrom llama_index.core.node_parser import SentenceWindowNodeParser
text = "hello. how are you? I am fine! aaa;ee. bb,cc"
# 定义句子解析器node_parser = SentenceWindowNodeParser.from_defaults(    window_size=3,    window_metadata_key="window",    original_text_metadata_key="original_text",)#print(node_parser)#documents = SimpleDirectoryReader(input_files=[r"D:\Test\LLMTrain\day20_llamaindex\data\ai.txt"]).load_data()
nodes = node_parser.get_nodes_from_documents([Document(text=text)])
print([x.text for x in nodes])
print("-"*20)
print("第1个节点的元数据",nodes[0].metadata)
print("-"*20)print("最后1个节点的元数据",nodes[4].metadata)

结果分析:

  • 可看到英文字符串 "hello. how are you? I am fine! aaa;ee. bb,cc" 被拆分成了5个句子,SentenceWindowNodeParser根据句尾的标点符号句号(.), 问候(?),感叹号(!)来识别和切割句子。

  • 当文档被切割以后,窗口数据和文档数据都会被存储在节点的元数据中并以自定义的window_metadata_key和original_text_metadata_key来表示。

  • 我们查看第一个文档的元数据,第一个文档也就是原始文档的第一个句子,因此窗口数据中只包含了当前句子和后续两条句子共3个句子。

  • 节点的最后一共文档,因为是最后一共文档因此它的窗口数据中只包含了当前句子的前三条句子和当前句子一共4个句子。


注意:

LlamaIndex中的SentenceWindowNodeParser只能识别半角的英文标点符号,这将导致无法切割中文的文档。

解决方法:将中文文档中的全角符号(句号。、问号?、感叹号!)全部替换成对应的半角标点符号。且后面再多加一空格,这样就可以切割中文了。 

from llama_index.core  import Documentfrom llama_index.core import SimpleDirectoryReaderfrom llama_index.core.node_parser import SentenceWindowNodeParser
text = "你好,很高兴认识你。已经10点了,可我还不想起床!下雪啦!你的作业完成了吗?"text = text.replace('。''. ')text = text.replace('!''! ')text = text.replace('?''? ')
# 定义句子解析器node_parser = SentenceWindowNodeParser.from_defaults(    window_size=3,    window_metadata_key="window",    original_text_metadata_key="original_text",)#print(node_parser)#documents = SimpleDirectoryReader(input_files=[r"D:\Test\LLMTrain\day20_llamaindex\data\ai.txt"]).load_data()
nodes = node_parser.get_nodes_from_documents([Document(text=text)])
print([x.text for x in nodes])
print("-"*20)
print("第1个节点的元数据",nodes[0].metadata)
print("-"*20)print("最后第2个节点的元数据",nodes[2].metadata)

4、语义切分Semantic splitter

 参数说明

名称
    类型 
   描述
    默认
buffer_size
    int
模型每次考虑的句子数量
    1
embed_model
    BaseEmbedding 
   (BaseEmbedding):要使用的嵌入模型    
必需的
sentence_splitter
    Optional[Callable] 
   将文本拆分成句子    
<function split_by_sentence_tokenizer.<locals>.<lambda> at 0x7b5051032660>
include_metadata
 bool 
 是否在节点中包含元数据  
必需的
include_prev_next_rel
bool
是否包含上一个/下一个关系    
必需的
breakpoint_percentile_threshold
 int 
一组句子与下一组句子之间余弦差异的百分位数,该百分位数必须超过该百分位数才能形成节点。该数字越小,生成的节点越多
95

buffer_size的作用:

SemanticSplitterNodeParser 的主要功能是根据语义相似性将文档划分为多个节点(Nodes),每个节点包含一组语义相关的句子。在此过程中,buffer_size 决定了在计算语义相似性时,模型每次考虑的句子数量

例如,设置 buffer_size=3 表示模型每次将连续的 3 个句子作为一个单元进行语义相似性评估。这有助于确定是否应在这些句子之间插入断点,从而形成新的节点。

参数设置建议

  • 较小的 buffer_size(如 1 或 2):适用于内容变化频繁或结构松散的文档,有助于更精细地捕捉语义变化。

  • 较大的 buffer_size(如 5 或 10):适用于结构紧凑、语义连贯的文档,有助于减少不必要的分割。

from llama_index.core.node_parser import SemanticSplitterNodeParserfrom llama_index.embeddings.huggingface import HuggingFaceEmbeddingfrom llama_index.core import SimpleDirectoryReader
embed_model = HuggingFaceEmbedding(model_name=r"D:\Test\LLMTrain\testllm\llm\BAAI\bge-m3")
documents = SimpleDirectoryReader(input_files=[r"D:\Test\LLMTrain\day20_llamaindex\data\ai.txt"]).load_data()

parser = SemanticSplitterNodeParser(    embed_model=embed_model,    buffer_size=2)
nodes = parser.get_nodes_from_documents(documents)
print(f"? 共生成 {len(nodes)} 个语义块")print(f"\n示例块:\n{nodes[0].get_content()}")

? 六、总结建议

目标
推荐 TextSplitter
兼容中英文、快速好用
SentenceSplitter ✅
需要上下文连续性
FixedWindowSplitter ✅
高质量问答 + 多语义融合
SemanticSplitterNodeParser(高级)


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询