RAG 技术原理——常见文档分片方式
RAG 系统的性能瓶颈往往藏在一个容易被跳过的地方——embedding 模型没问题LLM 推理也没问题但检索结果就是不准。问题出在文档怎么切。切得太碎一段话被拆成三截语义断了。切得太大一篇 5000 字的文章塞进一个 chunkembedding 向量里的信息被稀释成平均。RAG 的检索质量chunk 策略至少占一半权重。下面拆解六种市面上最常见的分片方式以及每种方式的代码实现和适用场景。固定大小分片最直接的做法每个 chunk 固定 N 个 token遇到上限就切断。from langchain_text_splitters import CharacterTextSplittersplitter CharacterTextSplitter( chunk_size512, chunk_overlap50, separator)chunks splitter.split_text(document)chunk_size 设 512 是一个常见的起点——跟 BERT 的上下文窗口保持兼容也足够容纳一段完整论述。chunk_overlap 设 50 是为了让相邻 chunk 之间有一段重叠区避免刚好在关键信息处切断。这种做法速度最快预处理几乎没有计算开销。代价也很明显它不管文本的语义边界。一段话正在论证一个观点恰好到第 512 个 token 被拦腰截断前后两个 chunk 各残一半逻辑。固定大小分片适合两种场景一是做原型验证快速跑通整个 RAG 流水线二是文档本身结构弱、没有清晰的段落和标题层级这时候纠结语义切分也没有太多收益。基于句子和段落分片比固定大小进了一步——至少不切散一句话。from langchain_text_splitters import SentenceTransformersTokenTextSplittersplitter SentenceTransformersTokenTextSplitter( chunk_size512, chunk_overlap50)chunks splitter.split_text(document)先用 NLP 工具做句子边界检测——中文靠句号、问号、感叹号定位英文靠 NLTK 或 spaCy 的 sentence tokenizer。然后按句子为最小单位往 chunk 里填填到接近 chunk_size 上限为止。不会出现半句话的情况。段落分片是同一个思路只不过最小单位从句子换成段落。适合结构清晰的自然语言文本——技术博客、新闻报道、产品文档。但如果段落长度差异太大一段 300 token下一段 3000 tokenchunk 尺寸会很不均匀。递归分片固定大小太粗暴句子分片太依赖分隔符质量。递归分片走了一条中间路线——按优先级尝试多种分隔符逐层往下切。from langchain_text_splitters import RecursiveCharacterTextSplittersplitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, ., ])chunks splitter.split_text(document)逻辑是这样的先用双换行符段落边界切。切完如果某段还是超过 chunk_size改用单换行符切。再超用句号切。再超用空格切。最后实在没办法了才按字符硬切。这套策略优先保高级语义结构。段落切分能保住一个完整的论证单元就不会降级到句子级切分句子能保住就不会退到字符级。LangChain 把 RecursiveCharacterTextSplitter 作为默认推荐原因就在这里——它对大多数文档类型都能给出过得去的结果不需要针对每种文档单独调参。语义分片前面三种策略都在看文本的形式——换行符、标点、字符数。语义分片看的是文本的意思。原理不复杂把文档先拆成句子用 embedding 模型给每个句子算一个向量。然后遍历句子列表算相邻两句向量的余弦相似度。相似度突然掉下来说明话题变了在这里切一刀。from langchain_experimental.text_splitter import SemanticChunkersplitter SemanticChunker( embeddingsOpenAIEmbeddings(), breakpoint_threshold_typepercentile)chunks splitter.create_documents([document])breakpoint_threshold_type 控制切分的敏感度。percentile模式下系统追踪所有相邻句子的相似度分布在相似度明显低于平均水平的地方切开。也可以设一个绝对阈值——相似度低于 0.6 就切。语义分片的检索质量通常比前三种高一个档次尤其是处理主题跳跃较多的长文档。代价也很直接每个句子都要过一遍 embedding 模型预处理时间跟文档长度成正比。文档量大时成本不低。用语义分片需要注意阈值调试。太松了切不出边界太紧了切成碎片。通常建议先用一份小样本跑一轮在 RAGAS 之类评估框架上看检索精度再定阈值。父子分片前面五种策略都在解决一个单层问题把文档切成一堆 chunk每个 chunk 独立建索引检索时返回匹配的 chunk 喂给 LLM。但这里藏着一个根深蒂固的矛盾。chunk 设小了embedding 向量聚焦检索精度高。但单个小 chunk 携带的上下文有限——LLM 拿到一段 200 token 的文字可能连这段话在原文哪个章节、前后文在讨论什么都看不出来。chunk 设大了上下文够用但 embedding 向量被稀释成平均值检索时很难精准命中。父子分片的思路是把建索引和送上下文的 chunk 分开——用两套不同粒度的切分各取所长。具体做法先把文档按较大粒度切出一批父 chunk——比如每个父 chunk 是 1024 token对应原文的一个完整小节。然后在每个父 chunk 内部按较小粒度再切一批子 chunk——比如每个子 chunk 是 256 token对应一小段话。建向量索引时只对子 chunk 做 embedding。检索时用子 chunk 的向量跟用户查询做相似度匹配但返回给 LLM 的不是匹配到的子 chunk而是它所属的完整父 chunk。from langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain.storage import InMemoryStorefrom langchain.retrievers import ParentDocumentRetriever# 子chunk小粒度用于检索child_splitter RecursiveCharacterTextSplitter( chunk_size256, chunk_overlap30)# 父chunk大粒度用于返回给LLMparent_splitter RecursiveCharacterTextSplitter( chunk_size1024, chunk_overlap80)retriever ParentDocumentRetriever( vectorstorevectorstore, docstoreInMemoryStore(), child_splitterchild_splitter, parent_splitterparent_splitter)retriever.add_documents(documents)这个设计解决了矛盾。检索时拿 256 token 的子 chunk 做精确匹配拿到结果后把 1024 token 的父 chunk 整段塞给 LLM。模型看到的不只是匹配到的那两句话还有它所在章节的完整上下文。LangChain 的 ParentDocumentRetriever 对这个策略做了封装LlamaIndex 也有对应的 SentenceWindowNodeParser 实现类似效果。两个框架的实现思路一致——索引粒度和上下文粒度解耦。父子分片对长文档的问答场景效果提升明显。用户问第三章提到的那个优化方案具体怎么实现子 chunk 能精准命中方案描述所在的段落父 chunk 能提供前后文的推导过程和前提假设。不会出现只捞到一两句话、LLM 靠猜补齐上下文的情况。代价是存储翻倍。子 chunk 和父 chunk 都要存索引只建在子 chunk 上但要维护映射关系。文档量大的时候存储成本需要评估。按文档结构分片有些文档天然自带切分逻辑——Markdown 的各级标题、HTML 的 h1/h2/h3 标签、PDF 的书签层级。这类文档不需要靠 embedding 算相似度来找边界结构本身已经把边界画好了。from langchain_text_splitters import MarkdownHeaderTextSplitterheaders_to_split_on [ (#, h1), (##, h2), (###, h3),]splitter MarkdownHeaderTextSplitter( headers_to_split_onheaders_to_split_on)chunks splitter.split_text(markdown_text)每个 chunk 是某个标题层级下的全部内容同时附带标题层级作为 metadata。后续检索时可以用 metadata 做过过滤——只搜 h2 级别的 chunk、只看某个特定标题段落。这种做法的优势在于 chunk 天然结构完整。一个三级标题下的内容不会被拆到两个 chunk 里标题的信息量也不会丢失。问题是 chunk 尺寸不可控——有些章节写了 3000 token有些只有 50 token。通常需要配合递归分片做兜底先用文档结构切如果某个 chunk 还是太大再用递归分片往下拆。怎么选没有哪一种分片方式适合所有场景。不同文档结构、不同查询类型、不同成本预算对应不同的最优策略。原型验证阶段固定大小分片512 token 50 overlap足够跑通流程。代码最少速度最快发现问题了再换策略不迟。大多数通用场景递归分片是安全牌。它自动适配多种文本结构不用为每类文档手工调参。LangChain 把它设成默认不是没原因的。文档质量高、结构清晰的场景技术文档站、知识库按 Markdown 或 HTML 的标题层级分片收益最大。chunk 附带标题 metadata检索精度和可解释性都比纯文本切分好。对检索精度要求极高、文档主题跳跃明显的场景学术论文、法律合同、行业报告语义分片能拿到最好的效果。成本高一些但如果检索错了导致的后果比预处理成本更严重这笔投入是值的。文档长、对答案完整度要求高的场景技术手册、政策文件父子分片值得投入。检索精度靠子 chunk 保证上下文充足靠父 chunk 兜底。代价是存储成本翻倍需要评估文档规模是否在预算内。实际生产环境很少只用一种策略。一套完整的 RAG 系统通常会组合几种分片方式——用文档结构分片做第一层拆分用递归分片或语义分片做第二层细切再给每个 chunk 挂上来源、标题、时间等 metadata。分片不是一步做完的事而是一个需要根据检索效果持续迭代的工程环节。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】