微信扫码
添加专属顾问
我要投稿
RAG技术在文本处理中的应用,提升检索和生成效率。核心内容:1. RAG技术流程及文本切分的重要性2. 文本切分策略的三要素及不同策略对比3. 推荐的切分策略组合及切分器详细介绍
? 一、什么是 RAG?
RAG 是一种结合了检索(Retrieval)和生成(Generation)的 AI 技术。它的流程通常包括以下几个阶段:
文本切分(Text Splitting)
向量化编码(Embedding)
存入向量数据库(如 FAISS / Chroma / Milvus 等)
检索相似文段
生成回答
? 二、为什么要进行文本切分?
大模型(如 GPT)并不能直接检索整篇文档。我们必须先把文档切分成合适大小的段落(chunk),再对每个段落进行嵌入(embedding)。切得太细,会失去上下文;切得太粗,会导致嵌入不准确或超过上下文窗口。
文本切分在 RAG 中非常关键,因为切不好可能会:
✂️ 打断语义单位(比如一个句子切了一半)
? 降低召回准确率
? 让 LLM 理解不完整,输出变差
文本切分的目标是:
将大文档划分为若干小块(chunk),便于嵌入(Embedding)与后续的检索(Retrieval)。
切分得合理,检索更精准。
? 三、文本切分策略
分块三要素:
分块策略对比表:
LlamaIndex 提供了多个内置的 TextSplitter 类,来应对不同语言与结构的文档。
常用切分器:
SemanticSplitterNodeParser |
✅ 四、推荐切分策略组合
简单文档:用 SentenceSplitter + 固定 chunk size
上下文要求高:用 SentenceWindowNodeParser句子窗口,保持上下文连续
高质量 QA:用 SemanticSplitterNodeParser,语义分块(需额外安装小模型)
? 五、切分器详细介绍
1、语句切分SentenceSplitter
解析文本时优先考虑完整的句子;此类会尝试将句子和段落保持在一起。
参数:
名称 | 类型 | 描述 | 默认 |
---|---|---|---|
chunk_size |
int |
每个块的token大小。 |
1024 |
chunk_overlap |
int |
分割时每个块的 token 重叠。 |
200 |
separator |
str |
分割单词的默认分隔符 |
separator 非常关键,多语言常见句尾标点:
|
paragraph_separator |
str |
段落之间的分隔符。 |
'\n\n\n' |
secondary_chunking_regex |
|
用于拆分句子的备份正则表达式。 |
'[^,.;。?!]+[,.;。?!]?|[,.;。?!]' |
安装依赖
pip install llama-index llama-index-embeddings-huggingface
split_text_demo.py
from llama_index.core import SimpleDirectoryReader
from 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. 创建 SentenceSplitter
sentence_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 SimpleDirectoryReader
from 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 Document
from llama_index.core import SimpleDirectoryReader
from 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 Document
from llama_index.core import SimpleDirectoryReader
from 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的作用:
SemanticSplitterNodeParser 的主要功能是根据语义相似性将文档划分为多个节点(Nodes),每个节点包含一组语义相关的句子。在此过程中,buffer_size 决定了在计算语义相似性时,模型每次考虑的句子数量。
例如,设置 buffer_size=3 表示模型每次将连续的 3 个句子作为一个单元进行语义相似性评估。这有助于确定是否应在这些句子之间插入断点,从而形成新的节点。
参数设置建议
较小的 buffer_size(如 1 或 2):适用于内容变化频繁或结构松散的文档,有助于更精细地捕捉语义变化。
较大的 buffer_size(如 5 或 10):适用于结构紧凑、语义连贯的文档,有助于减少不必要的分割。
from llama_index.core.node_parser import SemanticSplitterNodeParser
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from 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()}")
? 六、总结建议
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-10-27
2024-09-04
2024-07-18
2024-05-05
2024-06-20
2024-06-13
2024-07-09
2024-07-09
2024-05-19
2024-07-07
2025-04-25
2025-04-22
2025-04-22
2025-04-20
2025-04-19
2025-04-18
2025-04-16
2025-04-14