ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

分块真相:如何定义RAG的检索基本单元

分块真相:如何定义RAG的检索基本单元 RAG 里的分块看起来像是在调分块大小等参数或者选择一个分割器。但它真正影响的是后续的检索效果因为分块涉及一个更根本的问题你准备让什么样的一段内容成为检索系统里的基本知识单元这才是分块真正在做的事情。一、为什么需要分块最直接的原因是模型有上下文长度限制长文档无法无限地整体送进嵌入模型或生成模型中。但即使文本没有超过长度限制通常也不能简单把整篇文档压成一个向量。因为嵌入最终会把一段文本压缩成固定维度的表示。一个块里包含的内容越多、主题越杂局部信息就越容易被稀释检索粒度也会越来越粗。于是分块一直存在一个基本权衡块越大上下文更完整但检索粒度更粗也更容易带回无关内容块越小语义更集中检索更精准但更容易丢失原来的上下文。所以分块真正要解决的并不是每多少个 token 切一刀而是对于我的文档和用户问题什么样的内容最适合作为一个独立的检索单元理解了这个问题再来看不同的分块方式就会清楚很多。二、常见的分块方式有哪些① 固定长度分块最简单的方式就是按照固定的字符数或 token 数控制每个块的大小比如每 500 个 token 切成一块。LangChain 中的TokenTextSplitter就属于这类很基础的实现。它简单、稳定也方便控制最终进入嵌入模型的文本长度。但因为主要依据长度切分边界可能刚好落在一句话或一个完整段落中间也可能把两个主题不同的内容放进同一个块。因此后面的很多方法其实都在解决一个问题除了长度我们还能不能利用更多信息找到更合理的切分位置② 按自然文本边界分块一种很常见的改进是优先沿着段落、句子、换行和标点等自然边界切分。LangChain 中很常见的RecursiveCharacterTextSplitter就是这种思路先尝试保留较大的文本单元如果仍然超过设定长度再逐级寻找更小的分隔符继续拆分。这样可以尽量避免从一句话或一个段落中间硬切开也是目前非常常见的一种基础分块方式。但它判断边界主要依赖文本本身的形式。这里有一个换行、一个句号并不意味着这里一定发生了语义变化更不知道它是不是文档中的一个新章节或新的逻辑单元。③ 基于语义分块再进一步可以直接利用内容之间的语义关系决定是否切开。一种典型做法是先对连续的句子或段落计算语义表示再比较它们之间的相似度。当相邻内容的语义差异明显变大时就把这里作为新的分块边界。例如 Unstructured 的by_similarity会使用嵌入模型判断连续内容的主题相似度相似的内容尽量组合在一起相似度较低的内容则不会进入同一个块。相比依赖字符和标点这种方法开始真正考虑内容是不是还在讨论同一件事但语义发生变化并不一定意味着文档结构也在这里发生变化。④ 基于文档结构分块对于法规、论文、报告、手册这类结构明确的文档还可以更进一步直接利用文档本身的结构决定主要边界。比如标题、章节、段落、列表、表格以及法规里的章、节、条、款本身就在表达内容之间的组织关系。实现上可以先通过文档解析或版面分析恢复这些结构再根据自己的场景制定分块规则。例如法规可以优先按“条”形成块表格尽量保持完整如果某个结构单元本身仍然太大再在内部继续使用前面的递归分割或其他方法。一些现成工具也在采用类似思路。Unstructured 的 by_title 会利用已经识别出的标题来保持章节边界Azure 的文档布局方案也是先识别文档结构再在结构内部继续控制块的大小。这里最值得留下的不是某个具体工具而是一种思路先利用文档结构确定大的边界再用长度、递归或语义方法处理过大的结构单元。这也是为什么对于复杂 PDF分块往往不是从选择一个分割器开始而是从上游能不能正确恢复文档结构开始。一个重要参数 overlap块与块之间为什么要保留重叠内容实际分块时还经常会看到一个参数重叠长度overlap。它指的是相邻两个块之间保留一部分重复内容。例如前一个块最后的 100 个 token也会出现在下一个块的开头块 AAAAA BBBB块 BBBBB CCCC这样做主要是为了减少信息刚好落在切分边界上时造成的上下文断裂。Overlap 可以和前面的多种分块方式一起使用。它本身并不决定“在哪里切”而是在已经确定边界之后为相邻块保留一些连续性。不过重叠越大也意味着更多重复内容、更大的索引和更多冗余召回所以它同样需要结合实际效果来调整。三、进阶分块策略补回上下文前面解决的是在哪里切、切多大。但块切得越小另一个问题就越明显一段内容从原文中拿出来以后可能失去原本的语境。比如一个块里只有“其设计重现期不应小于 3 年。”句子本身没有被切坏但单独拿出来以后“其”指什么、属于哪个章节、正在讨论什么问题都可能看不出来了。因此除了继续调整块大小还可以从另一个方向解决问题保持较小的检索粒度同时把必要的上下文补回来。① 给小块补充上下文一种做法是在原始块之外再补充一小段能够说明它所在语境的信息然后用增强后的内容参与检索。Anthropic 的Contextual Retrieval就采用了这种思路针对每个块生成一段简短的上下文说明这部分内容在整篇文档中的位置和含义再把它和原始块一起用于向量检索和关键词检索。原本只有“其设计重现期不应小于 3 年。”增强后可能变成“这部分来自某排水设计规范中关于雨水管渠设计重现期的规定其设计重现期不应小于 3 年。”重点不在于把块重新变大而是让小块在脱离全文以后仍然保留足够的信息被正确检索。② 小块检索大块返回另一种常见思路是Parent-Child也常被称为Small-to-Big。它不直接给小块增加更多文字而是同时保存两种不同粒度的块较大的父块 → 再切成多个较小的子块检索时使用小块因为它们主题更集中更容易精准匹配命中以后再找到对应的父块把更完整的上下文交给生成模型。这样就把两个原本冲突的目标拆开了检索时用小块追求准确生成时用大块保留上下文。这里面有一个很重要的认识用于检索的基本单元不一定必须和最终交给生成模型的上下文单元完全相同。 Context Enrichment和Parent-Child的具体做法不同但它们都在解决同一个问题如何在保持精细检索的同时不因为块变小而丢掉必要的上下文。四、生产环境中怎么确定最终的分块策略看到这里会发现分块并没有一个固定的“最佳方案”。块应该多大、是否需要重叠、采用递归还是语义分块、是否值得增加上下文增强或 Parent-Child都取决于文档本身和用户实际会提出的问题。因此生产环境里更可靠的做法不是凭经验不断调参数而是基准方案 → 运行评估 → 调整一种分块策略 → 再次评估也就是先建立一个合理的基准方案再用固定的检索评估集进行比较。例如保持其他条件不变只修改块大小只比较普通递归分块和基于文档结构的分块再观察 RecallK、MRR 等检索指标以及具体的失败案例。这样分块就从“感觉这样切可能更好”变成了一个可以验证、比较和持续迭代的工程问题。参考资料https://learn.microsoft.com/en-us/azure/search/vector-search-how-to-chunk-documentshttps://learn.microsoft.com/en-us/azure/search/search-how-to-semantic-chunkinghttps://docs.unstructured.io/api-reference/partition/chunkinghttps://unstructured.io/blog/chunking-for-rag-best-practiceshttps://www.anthropic.com/engineering/contextual-retrievalhttps://docs.databricks.com/gcp/en/ai-search/retrieval-quality
返回列表