ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding: 0px 1em;color: rgb(63, 63, 63);">RAG系统文本切分算法选型指南简介ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">在构建企业级检索增强生成(RAG)系统时,文本切分算法的选择至关重要。切分策略直接影响检索的质量和生成结果的准确性。本文档将介绍常用的文本切分方法,分析其优缺点,并结合不同检索器给出最佳实践方案。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">RAG系统通常包含两个主要组件: ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;color: rgb(63, 63, 63);" class="list-paddingleft-1">ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;text-indent: -1em;display: block;margin: 0.2em 8px;color: rgb(63, 63, 63);">1.ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: inherit;color: rgb(15, 76, 129);">索引阶段:加载数据、切分文档、存储向量ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;text-indent: -1em;display: block;margin: 0.2em 8px;color: rgb(63, 63, 63);">2.ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: inherit;color: rgb(15, 76, 129);">检索和生成阶段:基于用户输入检索相关内容,利用LLM生成回答ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">其中,文本切分是索引阶段的关键步骤,它将大型文档分割成更小的块,以便于检索和处理。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding: 0px 0.2em;color: rgb(255, 255, 255);background: rgb(15, 76, 129);">文本切分算法概述文本切分算法的主要目标是将长文本分割成语义连贯的小块,使其既能保持上下文完整性,又能有效地用于检索和传递给模型。理想的切分算法应该能够: 常用文本切分方法基于字符的切分描述基于字符的切分是最简单的切分方法,它根据字符数量将文本切分成固定大小的块。 LangChain示例fromlangchain_text_splittersimportRecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, # 每个块的字符数 chunk_overlap=200, # 相邻块之间的重叠字符数 separators=["\n\n","\n"," ",""] # 优先按照这些分隔符切分 ) docs = text_splitter.split_documents(documents)
优点缺点最佳实践- • 结合使用
BM25Retriever或简单的向量检索 - • 对于较短的文档,建议使用较小的
chunk_size(500-1000)
基于令牌的切分描述基于令牌的切分根据LLM的令牌(token)计数来切分文本,这更符合模型处理文本的方式。 LangChain示例fromlangchain_text_splittersimportTokenTextSplitter
text_splitter = TokenTextSplitter( chunk_size=500, # 每个块的最大令牌数 chunk_overlap=50 # 相邻块之间的重叠令牌数 ) docs = text_splitter.split_documents(documents)
优点缺点最佳实践- • 结合
VectorStoreRetriever使用 - • 对于GPT模型,可以设置chunk_size为1024-2048
基于语义的切分描述基于语义的切分通过计算文本嵌入并识别语义边界来切分文本,确保每个块的语义连贯性。 不同breakpoint_threshold_type的功能与作用SemanticChunker提供了四种不同的阈值类型(breakpoint_threshold_type),用于确定文本的切分点: 1. 百分位数方法(percentile) - •功能:基于文本嵌入向量之间的距离分布,使用百分位数作为划分边界的阈值
- •原理:计算相邻句子嵌入向量之间的距离,找出距离分布中特定百分位(如95%)的值作为阈值,超过该阈值的位置被视为语义边界
- •适用场景:适合结构相对规律、语义转换明显的一般文本
- •参数调整:通过调整
breakpoint_threshold_amount(0-100之间的值)控制切分的粒度,值越高切分越少,默认为95.0
2. 标准差方法(standard_deviation) - •功能:使用距离分布的平均值加上一定倍数的标准差作为切分阈值
- •原理:计算所有相邻句子嵌入向量距离的平均值和标准差,将"平均值+n倍标准差"作为阈值
- •适用场景:适合语义变化程度差异较大的文档,如包含多个不同主题的论文或报告
- •参数调整:通过
breakpoint_threshold_amount设置标准差的倍数,默认为1.0 - •特点:对异常值(语义突变点)更敏感,能够适应文档内部语义变化程度的差异
3. 四分位距方法(interquartile) - •功能:使用四分位距(IQR)来确定语义边界的阈值
- •原理:计算距离分布的第一四分位数(Q1)和第三四分位数(Q3),将"Q3 + n倍IQR"作为阈值,其中IQR = Q3 - Q1
- •适用场景:适合语义高度相关但需要在自然边界处切分的文档,如专业论文、技术文档
- •参数调整:通过
breakpoint_threshold_amount调整IQR的倍数,默认为1.5 - •特点:对极端值不敏感,提供更稳健的切分方式,减少噪声影响
4. 梯度方法(gradient) - •功能:结合梯度分析和百分位数方法,特别适用于语义高度相关的专业领域文档
- •原理:计算相邻向量距离的变化率(梯度),然后对梯度分布应用异常检测,找出语义变化显著的点
- •适用场景:特别适合法律、医学等专业领域文档,这类文档往往语义高度相关、术语密集
- •参数调整:与百分位数方法类似,通过
breakpoint_threshold_amount设置百分位阈值,默认为95.0 - •特点:能够在语义高度相关的文本中识别出细微的主题转换,使分布更宽,边界更容易识别
LangChain示例百分位数方法: fromlangchain_text_splittersimportSemanticChunker fromlangchain_openaiimportOpenAIEmbeddings
text_splitter = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="percentile", # 使用百分位数方法 breakpoint_threshold_amount=95.0# 95百分位数 ) docs = text_splitter.create_documents([text])
标准差方法: text_splitter = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="standard_deviation", # 使用标准差方法 breakpoint_threshold_amount=1.5# 1.5倍标准差 ) docs = text_splitter.create_documents([text])
四分位距方法: text_splitter = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="interquartile", # 使用四分位距方法 breakpoint_threshold_amount=1.5# 1.5倍四分位距 ) docs = text_splitter.create_documents([text])
梯度方法: text_splitter = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="gradient", # 使用梯度方法 breakpoint_threshold_amount=95.0# 95百分位数 ) docs = text_splitter.create_documents([text])
优点缺点最佳实践- • 梯度方法适合专业领域文档(如法律、医学、学术论文)
- • 考虑使用
HyperRetriever或SelfQueryRetriever增强语义检索能力
特定格式文档切分描述针对特定格式的文档(如Markdown、HTML、代码等)的专用切分器,能够理解文档结构。 LangChain示例Markdown切分: fromlangchain_text_splittersimportMarkdownHeaderTextSplitter
headers_to_split_on = [ ("#","Header 1"), ("##","Header 2"), ("###","Header 3"), ] markdown_splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on ) md_docs = markdown_splitter.split_text(markdown_text)
HTML切分: fromlangchain_text_splittersimportHTMLHeaderTextSplitter
html_splitter = HTMLHeaderTextSplitter(tags_to_split_on=["h1","h2","h3"]) html_docs = html_splitter.split_text(html_text)
代码切分: fromlangchain_text_splittersimportRecursiveCharacterTextSplitter
python_splitter = RecursiveCharacterTextSplitter.from_language( language="python", chunk_size=1000, chunk_overlap=200 ) code_docs = python_splitter.split_text(code_text)
优点缺点最佳实践- • 结合使用
ParentDocumentRetriever保持文档层次结构 - • 对于Markdown文档,按标题分割可提高检索精度
- • 对于HTML文档,可以结合CSS选择器进行更精细的控制
- • 使用元数据存储文档结构信息,便于检索时重建上下文
最佳实践方案混合策略在企业级RAG系统中,可以考虑采用混合策略,结合多种切分方法的优势: - 1.分层切分:先使用结构化切分(如按标题、段落),再使用细粒度切分(如基于令牌)
- 2.动态切分:根据文档类型和长度自动选择适合的切分算法
- 3.并行切分:同时使用多种切分方法,在检索时合并结果
文档类型与切分方法匹配- •长文本文档(如论文、报告):语义切分 + 分层检索
- •结构化文档(如Markdown、HTML):特定格式切分器 + 层次检索
检索器选择与优化- •向量存储检索器:适合语义相似性查询,与语义切分配合使用
- •BM25检索器:适合关键词查询,与基于字符的切分配合使用
- •自查询检索器:允许文档自描述,与语义切分配合良好
- •父文档检索器:保持文档结构,与特定格式切分器配合使用
结论文本切分是RAG系统的关键环节,直接影响检索质量和生成结果。在企业级应用中,应根据具体需求、文档特性和资源限制选择合适的切分策略。 理想的切分方案应该: 建议在实际部署前进行充分测试,并根据反馈持续优化切分策略。 额外内容文本切分预处理文本切分可以先通过一些预处理来获得更好的检索效果 - 1. 预设问题:通过大模型对切分后的文本进行扩充,补充1-2个预设问题来增加chunk召回概率,例如text切分成10个chunk,每个chunk通过大模型生成1-2个问题,然后更新chunk为问题+chunk文本
- 2. 保存chunk位置信息,检索到chunk后取出上下n个chunk一起参与模型问答,例如搜索到chunk3,将chunk1,chunk2,chunk4,chunk5一起填入context
- 3. 存储chunk与原文关系,检索到chunk后使用原文,剔除chunk
- 4. context拼接:由于大模型注意力机制,prompt的头尾两部分数据被关注权重比中间高,因此检索到的chunk按分数从高到低排序后,使用头尾交替法插入prompt,例如排序12345,插入prompt后顺序13542
|