掘金 人工智能 前天 13:23
RAG 系统文本切分:从固定长度到智能检索的六种方法解析
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了RAG系统中多种文本切分策略,包括固定大小切分、语义切分、基于文档结构的切分、递归切分、句子窗口检索和自动合并检索。这些策略各有优劣,适用于不同场景,旨在提升信息检索的准确性和生成结果的质量。文章详细介绍了各策略的原理、优缺点、适用场景,并提供了相关工具和示例,帮助读者理解和应用这些文本处理技术。

💡 **固定大小切分**:简单易行,但易破坏语义结构,导致上下文信息丢失,不推荐在生产环境中使用。

💡 **语义切分**:通过分析句子间相似度进行切分,保持语义完整性,提高检索准确性,但计算成本较高。

💡 **基于文档结构的切分**:利用文档的标题、段落等结构元素进行切分,保留语义和上下文关系,适用于结构清晰的文档,如技术文档。

💡 **递归切分**:使用不同的分隔符进行递归切分,保持语义结构,适应多种文本格式,减少上下文割裂,但处理速度较慢。

💡 **句子窗口检索**:以句子为单位进行检索,并提供上下文窗口,提高检索精度和生成质量,适用于需要高精度检索的场景。

💡 **自动合并检索**:通过层次化的块结构,自动合并相关的子块,提供更完整的上下文,提高生成质量,降低幻觉风险。

一、固定大小切分(Fixed-size chunking)

优势

顾名思义且容易实现。由于直接分割可能会破坏语义流程,建议在两个连续的片段之间保持一些重叠。

劣势

缺点较多,不建议在大多数真实生产场景中使用。

二、语义切分(Semantic chunking)

原理概述

语义分块的核心思想是通过计算相邻句子的嵌入向量之间的相似度,识别语义上的断点,从而将文本划分为语义连贯的块。具体步骤包括:

    句子分割:将文本按标点符号(如句号、问号等)分割成句子。嵌入计算:使用嵌入模型(如 OpenAI Embedding)计算每个句子的向量表示。相似度计算:计算相邻句子之间的语义相似度。断点识别:当相邻句子的相似度低于设定阈值时,认为存在语义断点,进行分块。块生成:将相似度高的句子合并为一个语义块。

这种方法旨在确保每个块内部的语义连贯性,从而提高检索和生成的准确性。

优势

劣势

语义分块在保持文本语义完整性方面具有优势,适用于对语义连贯性要求高的任务,如复杂问答系统。然而,其高计算成本和实现复杂度使其在资源受限或对处理速度要求高的场景中不太适用。相比之下,递归切分方法实现简单、处理速度快,适用于结构清晰的自然语言文本。因此,选择合适的分块策略应根据具体任务需求、数据类型和可用资源综合考虑。

三、基于文档结构的切片 (Document structure-based chunking)

文档结构化分块(Document Structure-Based Chunking)是一种利用文档自身结构进行内容切分的策略。其核心思想是根据文档的自然组织形式(如标题、段落、章节、函数等)进行分块,以保留语义完整性和上下文连贯性,从而提升检索和生成的效果。

“Document structure-based chunking”(基于文档结构的切分)不是按固定长度或句子切,而是“按逻辑块”划分内容,常见于 Word、PDF、HTML 等格式的企业文档处理中,尤其适合处理说明书、规约、设计文档的场景。

原理概述

文档结构化分块的基本原理是:

    结构感知:识别文档中的结构元素(如 Markdown 的标题、HTML 的标签、代码中的函数或类等),并以这些元素作为分块的边界。语义完整:确保每个分块在语义上是完整的,避免将相关内容拆分到不同的块中。上下文保留:通过保留文档的结构信息,维护内容之间的逻辑关系,增强模型对上下文的理解。

例如:在处理包含 Markdown 格式的文档时,可以使用 LangChain 提供的 MarkdownHeaderTextSplitter 类,根据标题层级(如 #、##、###)进行分块,从而保留文档的层次结构。

原理解析

1. 结构识别

从文档中提取结构元素,包括但不限于:

这些结构在不同文档格式中的表现形式不同,例如:

2. 结构驱动的切分逻辑

常见策略:

关键不是按“多少 token”切,而是“从属于哪个结构单元”切。

3. 结构标签保留(可选)

切分后的 chunk 还可保留其结构标识,如:

{  "chunk": "本系统支持 7 层安全防护措施……",  "section": "2.3 安全架构设计"}

这便于:

举个例子:

给定这样一段 Word 文档内容:

1. 系统概述    介绍系统的设计目标与背景。2. 功能模块    2.1 用户管理        包括登录、注册、权限分配等功能。    2.2 设备管理        支持设备的接入、控制与监控。

Structure-based Chunking 结果可能是:

docx2python 示例:

示例目录:

我们将把 Word 文档解析成如下结构:

[  {    "id": "1",    "title": "1. 系统概述",    "level": 1,    "content": "……正文内容……"  },  {    "id": "1.1",    "title": "1.1 功能模块",    "level": 2,    "content": "……正文内容……"  },  {    "id": "2",    "title": "2. 技术架构",    "level": 1,    "content": "……正文内容……"  }]

完整代码:解析 Word 文档结构和正文内容

from docx2python import docx2pythonimport reimport jsondef get_level_and_id(title: str):    """    从标题行中提取编号和层级(例:1.2.3 → level 3)    """    match = re.match(r"^(\d+(\.\d+)*)(\s+|$)", title.strip())    if match:        id_str = match.group(1)        level = id_str.count(".") + 1        return id_str, level    return None, Nonedef parse_docx_structure(docx_path: str):    doc_result = docx2python(docx_path)    body = doc_result.body    parsed_chunks = []    current_chunk = None    for section in body:        for para_group in section:            for para in para_group:                text = para.strip()                if not text:                    continue                # 如果段落以编号开头,视为新段落标题                id_str, level = get_level_and_id(text)                if id_str:                    # 存储上一段内容                    if current_chunk:                        parsed_chunks.append(current_chunk)                    current_chunk = {                        "id": id_str,                        "title": text,                        "level": level,                        "content": ""                    }                else:                    # 不是新段落标题,加入当前内容                    if current_chunk:                        current_chunk["content"] += text + "\n"                    else:                        # 文档开头没有编号,强行起一块                        current_chunk = {                            "id": "",                            "title": "",                            "level": 0,                            "content": text + "\n"                        }    if current_chunk:        parsed_chunks.append(current_chunk)    return parsed_chunks# 示例使用chunks = parse_docx_structure("example.docx")# 美观打印输出for chunk in chunks:    print(f"\n=== {chunk['id']} ({chunk['title']}) ===")    print(chunk["content"])# 可选:保存为 JSONwith open("parsed_chunks.json", "w", encoding="utf-8") as f:    json.dump(chunks, f, ensure_ascii=False, indent=2)

在实际应用中,可以根据文档的格式选择合适的分块工具和方法:

这些工具通常会在分块的同时添加元数据(如标题、层级信息等),以便在后续的检索和生成过程中提供更丰富的上下文。

优势与适用场景

优势

适用场景:

注意事项:

文档结构化分块是一种有效的分块策略,特别适用于结构清晰、层次分明的文档。在实际应用中,可以结合其他分块方法(如递归分块、语义分块)进行混合使用,以获得更好的效果。

四、递归切分(Recursive Splitting)

实际上 递归切分(Recursive Splitting) 也是 固定大小文本切块

在 RAG(Retrieval-Augmented Generation)系统中,递归切分(Recursive Splitting)是一种常用的文本分块策略,旨在将长文本有效地划分为适合处理的小块(chunk),以便后续的嵌入、检索和生成任务

原理概述

递归切分的核心思想是使用一组预定义的分隔符(如段落符、句号、空格等)按层次递归地将文本拆分成更小的块,直到每个块的长度满足设定的要求。

具体步骤如下:

    初步切分:使用第一个分隔符(例如换行符 \n)对文本进行初步切分。检查块大小:对于每个切分得到的块,判断其长度是否超过设定的最大块大小(chunk_size)。递归处理:如果某个块的长度仍然超过 chunk_size,则使用下一个分隔符(例如句号 。)对该块进行进一步切分。继续递归:重复上述过程,依次使用预定义的分隔符列表中的下一个分隔符,直到所有块的长度都不超过 chunk_size,或者无法再进行切分。合并块(可选):在某些实现中,如果相邻的文本块合并后长度不超过 chunk_size,则可以将它们合并,以确保块的长度尽可能接近 chunk_size,同时保留上下文完整性。

这种方法的优点在于它能够尽量保留文本的语义结构,例如段落和句子边界,从而在保持上下文连贯性的同时,生成大小合适的文本块。

示例(LangChain):

LangChain 提供了 RecursiveCharacterTextSplitter 类来实现递归切分。以下是一个使用示例:

from langchain.text_splitter import RecursiveCharacterTextSplittertext_splitter = RecursiveCharacterTextSplitter(    chunk_size=200,    chunk_overlap=50,    length_function=len,    separators=["\n", "。", " ", ""])text = "..."  # 待处理的文本texts = text_splitter.create_documents([text])for doc in texts:    print(doc)

在这个示例中:

这种方式确保了文本在切分时尽量保持语义的完整性和上下文的连贯性。

优势

适用场景

劣势

不适用场景

五、句子窗口检索(Sentence-Window Retrieval)

准确地讲,Sentence-Window Retrieval(检索期再扩窗)不是切片方式,而是 检索策略 —— 先以“单句 chunks”建索引,命中后再把 前后 N 句 拼回去给 LLM。

原理概述

传统的 RAG 系统通常将文档按固定长度或段落进行切分,并对每个块进行向量化处理。然而,这种方法可能导致语义相关的信息被切割,影响检索效果。

句子窗口检索通过以下步骤优化这一过程:

    按句子切分文档:将文档按句子进行切分,每个句子作为一个最小的检索单元。构建句子窗口:对于每个句子,记录其前后若干个句子,形成一个“窗口”。这个窗口包含了目标句子及其上下文信息。向量化处理:仅对目标句子进行向量化处理,而将其窗口信息作为元数据存储。检索与生成:在检索阶段,根据用户查询与句子向量的相似度,找到最相关的句子,并将其对应的窗口信息提供给语言模型,以生成更准确的答案。

这种方法结合了细粒度的检索和丰富的上下文信息,提升了检索的精确度和生成的质量。

示例(LlamaIndex)

在 LlamaIndex 中,可以通过 SentenceWindowNodeParser 实现句子窗口检索:from llama_index.node_parser import SentenceWindowNodeParser

node_parser = SentenceWindowNodeParser.from_defaults(    window_size=3,    window_metadata_key="window",    original_text_metadata_key="original_text",)

在检索阶段,可以使用 MetadataReplacementPostProcessor 将检索到的句子替换为其对应的窗口内容,提供给语言模型进行生成。

优势

适用于需要高精度检索和丰富上下文支持的场景,如问答系统、文档摘要等。

注意事项

六、自动合并检索(Auto-merging retrieval)

Auto-merging Retrieval(自动合并检索)本质上是一种检索策略,而非单纯的 chunk(文本切分)方法。要实现这一策略,确实需要配合特定的 chunk 策略,尤其是层次化的 chunking(Hierarchical Chunking)。

我们前文说的 “Document structure-based chunking”(基于文档结构的切分)就属于“Hierarchical Chunking”(层次化切分)策略的一种。

原理概述

自动合并检索的核心思想是将文档划分为多个层次的块(chunk),形成父子关系的树状结构。在检索过程中,如果多个相关的子块属于同一个父块,系统会自动将这些子块合并为其父块,从而提供更完整的上下文信息给大语言模型(LLM)

实现步骤

    文档层次化拆分:使用如 LlamaIndex 的 HierarchicalNodeParser 或 Haystack 的 HierarchicalDocumentSplitter,将文档按预设的块大小(如 2048、512、128)递归拆分,构建出多层级的节点结构。索引构建:将最小的叶子节点(最小块)进行向量化,并存入向量数据库中,供后续检索使用。检索与合并:
      在用户查询时,系统首先检索与查询最相关的叶子节点。如果检索到的叶子节点中,有多个属于同一个父节点,并且超过设定的阈值(例如,超过该父节点子节点数量的50%),则系统会自动将这些子节点合并为其父节点,作为最终的检索结果返回

优势

注意事项

Document structure-based chunking 结合 Auto-merging Retrieval

要将“基于文档结构的切分”(Document Structure-Based Chunking)与“自动合并检索”(Auto-merging Retrieval)结合应用于 RAG(Retrieval-Augmented Generation)系统,可以按照以下步骤进行开发:

1 基于文档结构的层次化切分

目标:利用文档的结构信息(如标题、段落、列表等)进行多层次的切分,构建父子关系的树状结构。

实现方法:

2 构建向量索引与文档存储

目标:将最小的叶子节点进行向量化,并存入向量数据库中,同时保留父节点的信息以供后续合并使用。实现方法:

3 配置自动合并检索器

目标:在检索过程中,根据设定的阈值自动将相关的子块合并为其父块,提供更完整的上下文信息。实现方法:

总结

在构建基于大语言模型(LLM)的检索增强生成(RAG)系统时,文本分块策略的选择对系统性能和生成质量具有决定性影响。本文深入探讨了六种主流的文本分块方法,分别是:固定大小切分、语义切分、基于文档结构的切分、递归切分、句子窗口检索和自动合并检索。以下是对这些方法的综合比较与应用建议:

1. 固定大小切分(Fixed-size Chunking)

2. 语义切分(Semantic Chunking)

3. 基于文档结构的切分(Document Structure-based Chunking)

4. 递归切分(Recursive Splitting)

5. 句子窗口检索(Sentence-Window Retrieval)

6. 自动合并检索(Auto-merging Retrieval)

在实际应用中,选择合适的文本分块策略应根据具体任务需求、数据类型和可用资源综合考虑。对于结构清晰的文档,建议优先采用基于文档结构的切分方法,并结合递归切分优化块大小;对于对语义连贯性要求高的任务,可采用语义切分方法;对于需要高精度检索和丰富上下文支持的场景,可采用句子窗口检索或自动合并检索策略。此外,根据实际情况以上 chunk 策略不必拘泥于任何一种,可以混合使用

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

RAG 文本切分 信息检索 语义理解 LangChain
相关文章