ARTICLE DETAIL

资讯详情

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

RAG检索链路全拆解:从分块策略到向量数据库的实战指南

RAG检索链路全拆解:从分块策略到向量数据库的实战指南 做 RAG 检索增强生成这件事有个问题绕不开检索到底在做什么我见过不少刚开始接触 RAG 的同学第一反应都是把文档塞进向量数据库然后让大模型回答结果真跑起来才发现检索这条链路远没那么简单。从检索策略怎么设计、向量数据库怎么选到底层搜索算法参数怎么调每一步都在直接影响最终回答的质量。这篇文章我准备把 RAG 检索完整拆一遍把我实际踩过的坑、验证过有效的方案、以及本地搭建 RAG 知识库的实操记录都写出来尤其适合正在做 RAG 项目、或者想系统性学习 RAG 实战的朋友参考。1. RAG 检索的本质它到底在解决什么问题1.1 从一次提问说起先举个日常例子。你问大模型我们公司上个月的华南区销售数据怎么样如果模型没见过你们公司的内部报表它只能瞎编。RAG 的思路很简单先从不对外公开的知识库里把相关报表检索出来把内容塞进上下文再让模型基于这些材料回答。这就像一个大型图书馆的管理员你的问题不是直接让图书馆长背诵全书而是先让图书管理员帮你把相关的那几本书找出来你再翻着书来回答。所以 RAG 的核心链条是文档先被拆散、向量化、存进向量数据库用户提问时再把问题转成向量去数据库里做相似度搜索捞回一批候选文档片段最后由大模型基于这些片段生成答案。这里面最关键也最容易被忽视的就是检索这一环。很多人的注意力全放在用哪个大模型上实际测下来模型再强检索回来的是垃圾生成质量照样拉胯。1.2 RAG 的完整工作链路把 RAG 的链路拆开看大致分三个阶段。索引阶段原始文档进来先做格式解析PDF、Word、Markdown、HTML然后清洗掉页眉页脚、表格噪声、无关注释接着按策略切分成文本块每个块用 Embedding 模型转成向量连同原文和元数据一起写入向量数据库。注意文本在 RAG 里的最小单元是分块不是整篇文档分块质量直接决定后续召回质量。检索阶段用户的 query 进来先做改写或扩展这个后面细说然后用同一个 Embedding 模型转成向量去向量数据库做近似最近邻搜索ANN拿到一个候选集合。候选集通常要经过重排序模型精排再配合元数据过滤、去重、阈值过滤最终确定送进大模型上下文的 Top-K 片段。生成阶段把检索结果拼进 Prompt一般会带上引用来源然后用大模型生成回答。这一步还会涉及上下文长度管理、指令模板设计、引用格式处理等细节。三个阶段合起来才是完整的 RAG。很多人只关心第三阶段的模型选型但前两个阶段才是决定 RAG 上限的地方。1.3 RAG 的真正瓶颈在哪里先说结论RAG 最大的瓶颈不是模型不够聪明而是检索召回质量不稳定。我跑过几个实际项目最深的体会是检索不出来的知识等价于不存在。如果相关文档没被召回大模型再强也只能要么答非所问要么一本正经地编答案。另一个常见瓶颈是上下文窗口的物理限制。文档多了之后能塞进 Prompt 的片段有限Top-K 只能取很小一部分如果切分策略不合理关键信息恰好被切碎、跨块分布召回率就直线下降。还有噪音污染问题召回回来的片段里如果混入大量无关内容大模型容易被带偏反而比不召回更糟。所以做 RAG 的第一课就是别急着调模型先审视检索链路。检索做扎实了哪怕用相对轻量的模型效果也能稳得住。2. 检索策略决定 RAG 上限的关键环节2.1 分块策略最容易被低估的环节我在实操里发现分块策略对检索效果的影响比换一个向量数据库大得多。分块要考虑两个核心参数chunk size块大小和 overlap重叠量。块太大一个块里塞了太多主题向量会被各主题稀释检索时相似度被拉到平均精度下降块太小语义不完整召回回来的片段信息量不足大模型无米下锅。我实测下来通用知识库场景块大小在 256 到 512 个 token 之间比较合适overlap 设 10% 到 20%让相邻块之间有部分重叠避免句子或段落被硬生生切断。怎么选择切分方式也得看文档结构。纯文本可以用固定长度切分但技术文档、规章制度这类有明确标题层级的一定要按章节结构切。我常用的是先按 Markdown 标题层级做结构切分再对超过上限的大段落做二次切分这样能最大程度保住语义完整性。表格数据更特殊直接转成 Markdown 表格或键值对文本往往比保留原始格式更利于检索。分块方式适用场景优点缺点固定长度切分纯文本、聊天记录简单可控、索引均匀容易切断语义结构感知切分技术文档、Markdown、规章制度语义完整、可读性好实现复杂度高语义切分教科书、长报告边界贴合语义变化计算成本高、不稳定按句子/段落切分新闻、短文自然边界清晰块长度差异大2.2 查询改写与意图扩展用户提问通常很口语化直接拿原始 query 去检索效果往往一般。我见过最典型的例子用户问上季度利润率为什么下滑文档里写的是毛利率下降 5 个百分点词汇层面毫无重合纯靠向量相似度可能召回不到。这时候就需要查询侧的加工。比较实用的方案有三种查询改写让大模型把口语化问题改写成适合检索的关键词组合多查询扩展把一个问题拆成多个角度分别去检索再把结果合并去重HyDE先让模型根据问题生成一段假设性回答再用这段伪文档去检索。HyDE 的原理是假设性回答和真实文档在语义向量空间里更接近实测在长尾问题上确实有效但多一次模型调用延迟和成本都上去了。我的建议是简单项目先做关键词提取加同义扩展效果能提升一截复杂领域再上多查询和 HyDE同时做好缓存别让每次提问都重复调用模型。2.3 混合检索与重排序密集向量检索dense retrieval擅长语义匹配但经常在精确词匹配上翻车BM25 这类稀疏检索恰好反过来擅长精确关键词但理解不了同义改写。混合检索就是把两者结合先用两种方式各召回一批候选再做融合。这是目前工业界召回质量最稳的方案之一。召回阶段结束重排序rerank是另一个性价比极高的环节。向量检索一次可能捞回几百条候选但上下文只能放几条怎么挑我线上常用的方案是用一个交叉编码器模型比如 bge-reranker把 query 和每条候选拼接起来做精排效果比单纯按向量相似度排序好很多。代价是每对都要算一遍模型候选集控制在 50 到 100 条以内就没问题。重排序之后一般还要做去重和阈值过滤。同一个知识点可能被多个文档片段覆盖送重复内容进上下文纯属浪费 token。我一般用语义相似度加字符重叠双重去重再用一个兜底的相似度阈值低于阈值直接丢弃宁可答不上来也别硬答。2.4 元数据过滤与知识结构向量相似度不是万能的很多时候检索要结合业务规则。比如只允许检索某个部门、某时间段、某个产品线的文档这时候元数据过滤就派上用场了。入库时给每个分块打上来源、时间、部门、权限等级等标签查询时先生成过滤条件再搜索效率和准确性都会大幅提升。更进一步很多人会把 RAG 和本体Ontology或知识图谱KG结合。纯 RAG 是扁平的它对实体之间的关系理解很弱而知识图谱擅长表达关系比如员工 A 隶属于部门 B部门 B 的负责人是 C。把图谱里的结构化三元组和文档碎片一起进检索能让答案更有依据。现在讨论比较多的 Ontology RAG本质上就是在索引阶段用领域本体约束哪些概念、关系值得被结构化提取再把结构化信息和原始文本一起送入向量库。这种模式在处理多实体强关联的垂直领域时效果要明显好于纯文本 RAG。3. 向量数据库选型与底层搜索算法3.1 主流向量数据库横向对比市面上的向量数据库五花八门选型时最容易纠结。我按实际使用体验做了一个对比供参考。名称部署方式适合规模过滤能力生态与语言上手难度Chroma嵌入式/本地百万级以下一般Python 友好低FAISS嵌入库千万级以上弱需自己组合Python/C中Qdrant服务端/本地千万级强多语言 SDK中Milvus服务端/集群亿级以上强多语言 SDK高Weaviate服务端千万级强多语言 SDK中pgvector数据库插件百万到千万极强SQL用 PostgreSQL 已有生态低个人项目或企业小规模知识库我通常建议先上 Chroma 或 pgvector部署简单和现有技术栈融合容易。数据量到了千万级以上再考虑 Milvus 或 Qdrant。这里有个容易踩的坑向量数据库本身不带 Embedding 模型embedding 的生成要单独接入别把模型能力算在数据库头上。3.2 HNSW 与 IVF两类主流算法的工作原理向量检索最朴素的实现是暴力扫描全库计算距离数据量一上来就不可行。所以实际用的都是近似最近邻ANN算法其中 HNSW 和 IVF 是两大主流。HNSWHierarchical Navigable Small World分层可导航小世界图的思想是分层建图底层包含所有节点越往上节点越稀疏。搜索时从顶层进入每层沿着离目标更近的邻居快速跳转逐步下降到最底层拿到结果。它有一个很生活化的类比你要在陌生城市找一家特定餐厅不会挨家挨户看而是先看路牌找到区域再在区域内沿路找。HNSW 的两个核心参数是M每个节点的最大连接数和efSearch搜索时的候选列表大小。M 越大图越稠密召回越高但内存占用越大efSearch 越大搜索越精细但延迟上升。IVFInverted File倒排文件的思路是先做聚类建索引时把全量向量聚成 nlist 个簇查询时先算出 query 离哪些簇心最近只在这些簇内做精确搜索。参数nprobe控制探测几个簇nprobe 越大召回的候选簇越多准确率越高但耗时越长。两者怎么选我的经验是HNSW 在延迟、召回率、内存占用上整体更均衡是目前默认首选IVF 胜在内存占用可控且训练简单适合超大规模、对召回率要求不是极端的场景。还有倒排乘积量化IVF-PQ这类组合方案先聚类再量化压缩能大幅压内存但牺牲一定精度。3.3 量化压缩PQ 与标量量化到底在算什么很多人看到 PQProduct Quantization就头大其实原理没那么玄。向量通常有几百维PQ 的做法是把向量切成若干子段比如 768 维切成 96 段每段 8 维对每一段单独做聚类得到一个码本。每个向量用它的子段所属聚类中心编号来表示编号是几个字节的整数。这样原本每个维度一个浮点数4 字节的向量被压缩成几十个短整型码内存爆炸式下降。代价是精度损失PQ 是用聚类中心近似代替原始向量搜索时为了快速比较会把 query 的子段距离预先算成距离表然后用查表近似估算候选向量的真实距离这个过程叫 ADCAsymmetric Distance Computation。实际项目里如果内存吃紧我会先用 PQ 跑一轮粗筛再用原始向量对候选集做精确重排兼顾内存和精度。标量量化Scalar QuantizationSQ就更简单了把每个维度的浮点数从 4 字节压到 1 字节映射到 0~255 整数是对所有维度统一做的全局量化实现简单压缩率固定但精度损失略大于 PQ。小型项目我建议直接上 HNSW 不加压缩数据规模真的大了再考虑 IVF-PQ。3.4 距离度量怎么选向量数据库里常见的距离度量有三种余弦相似度、欧氏距离、内积。选错度量索引白建。余弦相似度衡量的是方向一致性对向量模长不敏感。Embedding 模型输出的向量经过归一化后余弦相似度和内积是等价的这也是大多数 RAG 场景的默认选项适合文本语义相似检索。欧氏距离衡量绝对距离对向量长度差异敏感更适合向量本身就编码了强度信息的场景比如推荐系统里用户偏好强度。内积则在没有归一化的向量上表现不佳容易受到向量模长干扰。我的建议是用常见的文本 Embedding 模型比如 bge、nomic-embed-text、text-embedding-ada时都做 L2 归一化距离度量选余弦相似度这样既直观又稳定。另外注意数据库里设置的度量类型必须和建索引时一致中途改度量需要重建索引别指望线上直接切换。4. 从零搭建本地 RAG 的实操方案4.1 Ollama 本地模型组合一件能跑的最小配置很多人想先本地体验一套 RAG又不想碰云服务Ollama 是目前最顺手的方案。我推荐的组合是两个模型一个 Embedding 模型加一个对话模型。Embedding 模型我用过nomic-embed-text和bge-m3对话模型用过qwen2.5和llama3.1的量化版。在 macOS 上搭建没什么特别的坑安装 Ollama 后先拉模型# 拉取 embedding 模型 ollama pull nomic-embed-text # 拉取对话模型 ollama pull qwen2.5:7b需要注意两点一是本机内存最好 16G 以上7B 模型量化版加 Embedding 模型同时跑大概要吃 8~10G 内存二是 Ollama 默认在 11434 端口提供 OpenAI 兼容接口接 LangChain、LangChain4j 或其他框架时直接用这个地址就行base URL 通常是http://localhost:11434。实测下来把 embedding 维度设成 768查询时用同一个模型做向量化向量库中条目不多时召回效果足够稳定。4.2 文本拆解工具本地场景的实战选择做本地 RAG 时文档解析这一步最折磨人。PDF 转文本时表格错位、扫描件乱码、Markdown 层级丢失都是高频问题。我用下来比较顺手的工具链有这几类。通用解析unstructured库支持 PDF、HTML、DOCX 等多种格式的解析还能按文档结构输出元素markitdown专门把各种文件转成 Markdown对技术文档友好。PDF 专项pypdf适合简单文本 PDFpdfplumber对表格和版面还原好扫描件得先接 OCR比如paddleocr。分块工具LangChain 的RecursiveCharacterTextSplitter适合快速上手但想切得准还是得自己写标题感知切分器按 Markdown 标题、段首缩进这些特征先切分再补 overlap。我实际的经验教训是90% 的检索质量问题出在解析阶段。原始文档解析出来如果是乱的后续什么高级检索策略都补不回来。所以第一步宁可多花时间做文档清洗去掉页眉页脚、统一编码、保留标题层级、表格转成结构化文本。4.3 LangChain4j Easy RAGJava 生态的快捷路径如果团队技术栈是 JavaLangChain4j 的 Easy RAG 模块值得关注。它把文档导入、切分、向量化、存储、检索到回答的流程做了封装几行配置就能跑通一套基础 RAG。大概的样子EasyRag easyRag EasyRag.builder() .chatModel(OpenAiChatModel.builder() .baseUrl(http://localhost:11434/v1) .modelName(qwen2.5:7b) .build()) .embeddingModel(OpenAiEmbeddingModel.builder() .baseUrl(http://localhost:11434/v1) .modelName(nomic-embed-text) .build()) .documentSet(DocumentSet.fromPath(Paths.get(/data/kb))) .build(); String answer easyRag.chat(我们公司华南区上季度销售情况如何);Easy RAG 的优点是上手快但注意它的默认配置是面向通用场景的分块大小、检索 Top-K、重排序这些都需要根据实际效果调。我把它当成跑通第一步的工具后续还是回归到用 LangChain4j 的核心组件自己编排流程可控性更强。4.4 多模态扩展RAG 知识库到底能不能存图片这个问题被问得很多。先说答案传统文本向量库不能直接存图片但多模态 RAG 是可以做的关键看你的诉求。如果诉求是根据图片内容回答问题那需要多模态 Embedding 模型比如 CLIP它能同时把图片和文本映射到同一向量空间图片向量入库后用户用文本提问也能检索到相关图片再用多模态大模型理解图片内容生成回答。这个方案在当前本地环境跑成本不低对硬件要求高实际项目里用得不算多。更务实的方案是把图片转化为文本再检索。对图片做 OCR 提取文字或让多模态模型生成图片描述然后作为文本分块的一部分入库。检索时命中文本块再关联展示原始图片。我做一个企业产品手册知识库时就是这么处理的产品图生成了结构化描述型号、颜色、适用场景检索命中描述块前端展示图片效果和直接存图片几乎没有差别但技术栈完全复用现有文本 RAG。如果一定要原生支持图片向量选型时确认向量数据库支持多向量字段一个文档记录同时挂文本向量和图片向量即可。5. 常见问题与排查技巧实录5.1 检索效果差的常见原因与排查路径做 RAG 默认会遇到几类问题我列一个排查顺序给你照着查基本能定位源头。现象可能原因排查方法检索经常召回无关片段分块过大导致语义稀释缩小 chunk size检查切分边界该召回的文档没召回Embedding 模型不匹配换更强的 embedding 模型确认 query 和文档用同一模型关键词精确匹配失效只用 dense 检索加 BM25 做混合检索答案经常编造阈值过滤太松降低相似度阈值宁缺毋滥同一问题答案不稳定召回集抖动做多查询合并加 rerank 精排我最想强调的一点是遇到效果差先做单步验证别整体瞎调。先把检索结果单独打出来看召回的是不是相关片段如果召回就对再看生成环节是不是 Prompt 写得太含糊。我以前总是一上来就调分块参数调半天发现是解析阶段文档就没了。5.2 向量数据库性能调优参数背后的取舍数据量上来之后向量数据库的问题就变成查得慢、占内存。HNSW 场景我通常从M16、efConstruction100起步做索引查询时efSearch从 50 往上加观察召回率和 P99 延迟的拐点。如果大模型回答质量没随efSearch继续提升说明瓶颈不在召回而在重排序或生成侧。内存占用超标时优先做向量压缩而不是缩减数据量。前面说的 PQ 量化在 Milvus、Qdrant 里都有现成配置开了之后内存能降一个量级。另外还有一招冷却数据降级。热数据用 HNSW 全精度索引冷数据用 PQ 压缩索引查询时分层检索兼顾性能和成本。这招是我在一个用户百万级的项目里用过的组合方案效果非常稳定。5.3 RAG、知识图谱与 Ontology三种知识库形态怎么选这个问题在kg 知识库、rag 知识库和结构知识库区分的热搜里反复出现。其实它们不是互斥关系而是不同层级的组织方式。RAG 知识库以文本块为基本单元适合非结构化文档优点是不需要预先设计 schema缺点是不擅长表达实体间复杂关系。KG知识图谱知识库以实体和关系为基本单元适合强关系的业务场景人物关系、组织架构、供应链回答谁和谁有什么关系这类问题很准但构建成本高需要人工或模型抽取三元组。Ontology本体是 Schema 层的设计定义了领域内的概念、属性和关系约束本身不存具体事实它是指导图谱构建和 RAG 索引的宪法。实际项目里我会这样组合用 Ontology 约束领域概念用 KG 存结构化关系用传统 RAG 存长文本细节检索时三者联合召回再在生成阶段统一融合。这个组合方式的搭建成本不低适合知识密度高、关系复杂的垂直场景比如医疗问诊、法律条款咨询、企业规章制度问答。5.4 避坑清单这些细节我都是真金白银换来的最后整理一份避坑清单覆盖我从零搭建本地 RAG 到现在积累下来的教训。其一别用同一个 Embedding 模型同时处理不同语言的文本。多语言场景优先选bge-m3或multilingual-e5这类原生多语言模型否则中英文混合文档的召回率会很难看。其二Prompt 里一定要让模型注明引用来源。这不只是为了可解释性更是为了事后排查如果答案引用了明显不相干的片段你能快速定位是检索还是生成的问题。其三向量维度变了老向量库要做全量重建。换 embedding 模型是最容易被忽略的坑新旧向量混在一个集合里距离计算完全失去参照。我换过一次模型忘了重建索引线上召回率掉了二十多个百分点排查了两天才发现。其四先解决有没有再解决准不准。爬坡过程中最容易陷入追求完美算法的循环但更普遍的问题是文档根本没进去、解析烂了、元数据缺失。框架跑通、链路可见再逐步优化每个环节。做 RAG 这一年多我最大的体会是检索这件事的复杂度没比生成低多少。很多人以为 RAG 是向量数据库 大模型两步走实际上分块策略、查询改写、混合检索、重排序、底层索引参数每一个环节都是独立的工程问题。本地先用 Ollama 把链路跑通再去逐个环节做优化是最稳的成长路径。最后再分享一个小技巧每次改动任何一个环节都固定其他条件跑一组测试问题集把召回结果和生成答案记录下来对比。这套改动前后对照的习惯帮我避开了无数次无效调参也让我对检索链路的理解越来越细。
返回列表