资讯动态

RAG技术演进路线:从朴素RAG到混合检索与重排序的优化实践

发布时间:2026/10/20 15:52:08 来源:尧图企业网站定制
从开发者视角聊聊 RAG 技术的演进路线。这个系列的上篇已经聊过 RAG检索增强生成的基础原理和朴素架构这篇“中”篇主要聚焦在它从“能用”到“好用”的进化过程。如果你正在做 RAG 应用开发或者准备给自己的业务接入大模型知识库这篇文章应该能帮你理清技术选型的思路搞清楚那些花里胡哨的优化手段到底在解决什么问题。先说一个基本判断RAG 现在的形态已经不是最初那个“检索 拼接 生成”的三板斧了。它分化出了很多变体有侧重检索质量的有侧重生成策略的还有侧重流程编排的。很多人看到一堆术语——HyDE、RAG-Fusion、Self-RAG、CRAG——就头大其实它们都有明确的进化动因。1. 演进主线从朴素管线到模块化编排1.1 朴素 RAG 的经典三段式要说清演进得先回顾起点。朴素 RAG 的结构非常简单就是索引Indexing、检索Retrieval、生成Generation三个环节串联。索引阶段把文档切块chunking用 embedding 模型转成向量写入向量数据库。检索阶段把用户 query 转成向量在向量库里做相似度检索取 top-k 个相关块。生成阶段把检索到的上下文块 原始 query 拼进 prompt交给 LLM 生成答案。这套管线的优点是实现快、思路直白。我当时第一次跑通的时候也觉得“这不就是 openAI 接口包了个壳嘛”。但真正放到业务场景里问题很快就暴露出来了。1.2 朴素 RAG 的三个痛点第一个痛点是检索精度不够。向量相似度并不总是等于语义相关性尤其在处理专有名词、代码片段、缩写这类文本时embedding 模型经常“看不懂”。结果是检索回来的 top-k 块里混着不少噪声甚至核心信息根本没进到 top-k 里。第二个痛点是上下文利用率低。LLM 的输入长度虽然不断在扩展但把大量检索块全部塞进去既浪费 token 成本也会稀释关键信息。我见过一个案例有人给模型塞了十几个块答案反而被无关上下文带偏了。第三个痛点是管线固定缺乏自适应能力。不管用户问什么流程都是“检索-拼接-生成”。但实际场景里有的 query 需要多跳推理有的 query 根本不需要检索比如闲聊有的 query 需要从多个异构数据源汇总。一个固定管线没法处理这些复杂情况。1.3 三个演进方向高级 RAG、模块化 RAG、新范式针对这些痛点业界逐渐分化出了几个演进方向。高级 RAGAdvanced RAG仍然保留三段式结构但在每个环节内部做精细化优化比如更聪明的分块策略、混合检索、重排序rerank、查询改写等。这是目前工程落地的主流形态。模块化 RAGModular RAG把原来固定管线中的每个环节抽象成独立模块模块之间可以灵活编排、替换、增删甚至引入搜索模块、记忆模块、路由模块。它追求的是流程的灵活性。新范式如 Self-RAG、CRAG 等试图让系统具备自我反思和自我纠正能力。比如检索结果不可靠时怎么办答案生成后如何做质量自检。这属于比较前沿的探索方向。用一张表来对比这三者会更直观演进阶段核心特征典型手段解决的核心问题朴素 RAG固定三段式管线向量检索 prompt 拼接从无到有让 LLM 能访问外部知识高级 RAG环节内精细化分块调优、混合检索、重排序从有到准提升检索质量和答案准确率模块化 RAG流程可编排路由、多源检索、推理模块从准到活适配复杂业务场景这个系列既然是“中”重点自然放在高级 RAG 的工程落地上因为它离实际项目最近收益也最直接。2. 索引优化不是在切文档是在设计知识结构索引阶段看似最简单其实是最容易出问题也最值得深挖的环节。2.1 分块策略——切法不同效果天差地别分块chunking的核心矛盾在于块太大检索出来的内容噪声多、token 浪费大块太小语义信息不完整embedding 难以捕捉完整表达。业界常用的分块策略大概分这么几类固定长度分块按字符数或 token 数硬切比如每 500 字符一块相邻块重叠 50 字符。优点是零实现成本缺点是容易切断句子或语义单元。句级/段落级分块按自然边界句号、换行、markdown 标题切分。实现也不难效果会比固定长度好不少特别是在文档结构良好比如技术文档、论文时。内容感知分块结合文档结构信息标题层级、表格、列表来切。这种方法效果最好但需要对文档格式做解析通常引入布局识别模型。对于 Markdown 或 HTML 类文档可以用专门的解析器做结构切块。我自己的实践经验是先看看你的语料长什么样。如果是结构化程度高的 Markdown 或 HTML优先做结构感知切块如果是纯文本句级切块基本就够了。固定长度切块不是不能用但一定要配合重叠否则一个完整知识点被拦腰切断后检索效果会非常随机。2.2 元数据与文档结构——被低估的检索加成给每个分块挂上必要的元数据标题、章节路径、文件来源、更新时间能显著优化后续的检索逻辑。元数据可以用于过滤。比如用户指定“只看 2024 年的政策文件”就能直接在查询时附带元数据过滤条件减少向量检索的候选范围。章节路径信息可以用于上下文重建。检索到的某个块如果过于碎片化可以顺着章节路径把前文或后文一并带上补全上下文再送进 LLM。来源信息可以用于引用溯源这对企业知识库场景几乎是硬性要求。我建议在搭建索引时至少为每个 chunk 保存块内文本、对应文档 ID、章节路径、摘要级别的元数据比如文档标题、发布时间、作者。把这些信息存进数据库后面做过滤、加权、溯源都会方便很多。另外还有一个经常被忽略的做法是多粒度索引。同一个文档可以在段落级建一层精细索引用于精确检索再在更高层小节或全文建一层粗粒度索引用于上下文补全。检索时先用细粒度索引定位再到粗粒度索引取完整上下文。2.3 Embedding 模型的选型思路Embedding 模型这块网上讨论很多我这里只说几条被踩坑踩出来的经验先看领域匹配度。通用 embedding 模型在垂直领域医疗、法律、代码的表现可能远不如领域微调过的模型。有条件的话用自己领域的数据对 embedding 模型做一遍评测再决定用哪个。中文场景要特别注意分词粒度。部分模型对中文长文本的支持不好embedding 出来的向量整体质量偏差。可以在自己的语料上抽一个小的评测集人工检查 top-k 检索结果的相关性。别盲目追新。新版 embedding 模型在 MTEB 或者 C-MTEB 上的分数提升不代表你在自己场景下一定能拿到同样的收益。做一次端到端的离线评测比什么都重要。3. 检索优化让“找到”变成“找对”检索环节是高级 RAG 改动最大、收益也最明显的地方。3.1 混合检索——向量不是万能的纯向量检索有个天然的盲区对精确匹配如产品型号、人名、代码变量、身份证号不敏感。两个字符串完全一致但语义上可能没有任何相似度。比如用户搜“RS-485 接线图”向量检索可能把“RS-422 接线说明”也捞出来因为语义相近。但对用户来说他要的就是字面完全匹配的内容。混合检索的思路很朴素把 BM25 这类稀疏检索和向量检索稠密检索结合起来。BM25 擅长抓精确词项匹配向量检索擅长抓语义相关两者互补。工程实现上一般有两种思路分数融合。两个检索器分别返回 top-k 结果用 RRFReciprocal Rank Fusion或加权分数融合算法合并排序。RRF 公式很简单对每个文档把它在不同检索结果中排名的倒数相加最终求和排序。这个方案实现成本低效果也比较稳。多路召回 重排序。先让 BM25 和向量检索各自召回一批候选可以多召回一些比如每路 top-50然后统一交给一个重排序模型做精细化的相关性打分输出最终的 top-k。从我实测的效果看混合检索至少能在大多数业务场景里把“找不到”的问题下降一个量级。特别是用户习惯用专有名词提问的时候BM25 那一路往往能救命。3.2 重排序——精度提升最直接的模块RAG 流程里重排序rerank几乎是性价比最高的一项优化。思路是向量检索和 BM25 召回的都是“可能相关”的候选集精度不够重排序模型会逐一计算 query 和每个候选块的相关性打分从几十个候选中挑出真正的 top-k。常见方案有两类Cross-encoder 模型。这类模型把 query 和 doc 拼接成一个序列输入模型计算相关性。效果最好但速度慢不适合对大候选集逐条重排。各种 rerank API 或开源 rerank 模型。性能与延迟的权衡各有不同可以根据业务体量选。实操上建议把重排序放在最后一步只对混合检索召回后的候选做重排因为这样一来计算量可控。候选集控制在 20-50 条重排后取 5-10 条进 prompt准确率和延迟都能兼顾。有一个细节值得说重排序的分数并不是一个绝对意义的相关性概率它只适合做排序。别试图给重排分数设绝对阈值而是用相对排序来决定取哪些块进上下文。3.3 查询改写与扩展——用户没说清楚的话用户的问题往往是简短的、模糊的、包含指代词这个、那个、它的。直接把这类 query 拿去检索结果自然不稳定。查询改写Query Rewriting就是在这里介入多查询扩展Multi-Query。把用户的一个查询通过 LLM 扩写成几个不同角度、不同措辞的查询分别检索合并结果。适合用户问题信息量足但表述不精准的场景。子问题分解Sub-Query。针对复杂问题拆成多个子问题分别检索再汇总答案。适合多跳推理类问题Multi-hop QA。假设性文档嵌入HyDE。先让 LLM 基于用户查询生成一个假设性回答文档再用这个“假文档”的 embedding 去找真实文档。思路很有意思适合查询与文档风格差异特别大的情况但注意生成假文档会引入额外延迟和成本需要实测评估收益。老实说查询改写这块的调优空间很大但也最容易“优化过度”。我在项目里见过把简单查询复杂化之后效果变差的情况。建议先搭好基线不加改写确认纯检索确实存在“问法一变就检索不到”的问题再考虑加改写模块。4. 生成优化上下文管理和提示词工程检索质量上去了最后还得看生成环节能不能把这些上下文用好。4.1 上下文压缩——给 LLM 减负很多时候检索回来的块里只有三分之一甚至更少的内容跟答案直接相关其余都是铺垫背景。把这些都塞给 LLM不仅浪费 token反而会引入“上下文偏移”上下文噪声干扰判断。上下文压缩的思路有几种按块压缩。把每个块单独交给 LLM让它提取与问题相关的核心信息压缩后再统一拼接。效果好但会明显增加调用次数和延迟。按内容选择性截取。利用摘要或关键词信息只取块中与 query 语义最接近的句子或片段。效率高但需要一定的文本切分能力。硬截断。设定 token 预算超出就从相关性较低的块开始丢弃。最简单粗暴适合对成本敏感的场景。我个人建议在系统早期不要做过于激进的上下文压缩因为每次压缩都可能丢失关键信息。先把检索和重排做到足够精准让进入 prompt 的本身就已经是高相关度内容再做压缩才有意义。4.2 提示词结构——把“参考答案”摆稳RAG 的提示词一般需要包含系统指令、上下文内容、用户问题、输出约束比如要求严格基于上下文回答无法回答时明确说不知道。几个我踩过坑之后的切身体会上下文和问题之间要有明确的标记分隔。LLM 才能准确区分“哪里是参考资料哪里是待回答的问题”。用清晰的 XML 标签或 Markdown 引用块都可。指令不要让模型对上下文进行“思考题式”的重新组织。如果你的目标是中文回答就让它直接把基于上下文的答案写出。别让它“先分析再回答”容易让输出膨胀。如果业务要求答案简明在 prompt 里明确给出目标字数或格式一段话/要点列表效果比让模型自由发挥稳定得多。4.3 生成后的自检——最后的兜底前面提到的新范式里Self-RAG 的思路就是在生成之后让模型判断“检索到的上下文是否足够支撑答案”。工程上可以低成本实现一个简化版生成答案后再给模型发一个独立的校验请求给它原文答案、检索上下文和用户问题让它判断“回答是否完全基于提供的上下文”以及“上下文是否包含足够的信息来回答问题”。如果校验不通过可以触发补充检索或拒答。这个做法的代价是额外一次 LLM 调用但能显著降低幻觉率。对回答准确性要求高的业务医疗咨询、法律问答、技术支持这笔开销是值得的。5. 实操环节搭一个支持混合检索与重排序的 Mini RAG理论说了这么多还是得落到代码上。这里分享一个我在模拟项目里使用过的简化实现技术选型相对常见语言模型接口统一封装向量数据库用轻量级实现重排序使用公开 API 或本地推理均可以。5.1 系统设计和依赖整体流程文档解析与分块。embedding 建索引。查询阶段同时做向量检索和 BM25 检索。合并后用重排序模型精排。拼 prompt 给 LLM 生成最终答案。工程结构清单假设场景是一个内部知识库问答文档处理支持 Markdown 和纯文本。分块按 Markdown 标题层级切分段落过长时再按句号切割。存储向量存储 文档元数据便于过滤和溯源。检索层向量检索 关键词检索用轻量全文索引即可。重排序本地加载一个小型 cross-encoder 模型。生成层统一风格的 prompt 模板。5.2 分块与索引实现要点分块模块的关键是“保留结构信息”。我处理 Markdown 时会先按标题层级构建一个树状结构然后从叶子节点开始切块。每个块的元数据里记录它的上级标题路径。这样后续既方便过滤也方便做上下文重建。伪代码思路def parse_markdown(filepath): # 解析标题层级构建文档树 # 叶子节点按内容切块保留标题路径 return blocks # 每个 block 包含 text, metadata向量化时我习惯把“标题 块正文”拼起来做 embedding因为标题往往包含关键的领域信息能提升检索效果。5.3 混合检索的实现先定义一个检索统一接口内部同时调用两个检索器再用 RRF 融合。def hybrid_search(query, top_k20): vector_hits vector_store.search(query, top_ktop_k) bm25_hits bm25_index.search(query, top_ktop_k) rrf_scores {} for rank, hit in enumerate(vector_hits): rrf_scores[hit.id] rrf_scores.get(hit.id, 0) 1 / (60 rank 1) for rank, hit in enumerate(bm25_hits): rrf_scores[hit.id] rrf_scores.get(hit.id, 0) 1 / (60 rank 1) ranked_ids sorted(rrf_scores, keyrrf_scores.get, reverseTrue) return [doc_by_id[i] for i in ranked_ids]RRF 公式里的常数 60 是常见的默认值不用过分调优。如果你发现某种检索器某一方明显更可靠可以对不同路召回的分数设置权重但一般用 RRF 已经足够稳。之后接重排序def rerank(query, docs): pairs [[query, doc.text] for doc in docs] scores rerank_model.predict(pairs) scored_docs sorted(zip(docs, scores), keylambda x: x[1], reverseTrue) return [doc for doc, _ in scored_docs[:5]]需要注意这里重排序的候选集规模建议控制在 20-50 条。如果候选太多cross-encoder 的推理延迟会高到影响真实用户体验。5.4 生成阶段的 Prompt 设计一个经过多轮实测稳定可用的 prompt 模板简化示意请仅根据给定的参考资料回答问题。 参考资料 context {context} /context 用户问题 question {question} /question 要求 1. 如果参考资料中没有足够的信息请直接回答信息不足无法回答。 2. 回答必须用中文。 3. 回答控制在 300 字以内。这个模板看起来简单但“仅根据”、“无法回答时明确说不知道”这两条约束能明显降低幻觉。我不建议在模板里加入过多“思考链”要求会拉长输出影响响应速度。5.5 离线评测与参数调优经验搭好系统后第一要务不是调 prompt而是做一个小的离线评测集。我从项目里总结的经验是准备 30-50 条真实业务问答标注好标准答案或者说至少标注清楚“应该在哪个文档的哪个区域里找”。每次改动检索参数或模型都跑一遍评测集统计检索命中率标准答案对应的文档片段是否出现在最终进 prompt 的上下文里。端到端准确率生成回答能否覆盖标准答案中的关键点。这两项是硬指标。不少工程团队上线前只看几个 demo 问题过了就放行之后遇到真实流量就会翻车。分块大小和重叠度是影响检索命中率最敏感的参数。我一般以 200-400 token 的块大小为起点用一个固定评测集多组对照看命中率的边际变化。如果一个块超过 500 token检索命中率会下降低于 100 token又容易丢失实体间的关联信息。具体数值和 embedding 模型、语料风格都有关系只能拿评测数据说话。6. 常见问题与排查实录RAG 系统的问题排查十次里有七八次是检索链路的问题而不是模型的问题。下面这些坑是我在真实项目和社区交流里见了无数次的。6.1 检索结果不相关症状用户问 A系统答非所问或者召回结果里混入大量无关块。排查顺序先看 query 的 embedding 向量和召回块的相似度分数。如果分数普遍很低说明 query 和语料的表达差异太大。这时候加查询改写HyDE 或多查询或者换一个领域更强的 embedding 模型。如果分数不低但内容还是不对那就是 embedding 模型本身对语义理解不足或者分块太粗导致块内语义分散。尝试升级模型或者把召回候选集调大一点比如从 10 调到 30交给重排序兜底。如果只有个别 query 不对把它们加入评测集针对性分析是哪一环的问题。6.2 检索到了但答案仍然错误症状上下文里明明有正确答案LLM 还是输出错误。这种一般不是检索问题而是上下文使用效率问题。可能是 prompt 结构不佳模型没分清哪里是参考材料也可能是块内信息太密集关键信息被埋没。可以尝试调整 prompt 格式明确上下文与问题边界。用上下文压缩突出与问题直接相关的句子。在 prompt 中强调“直接引用参考材料中的事实”。这一步很微妙措辞不同效果区别很大。6.3 系统延迟过高症状接口响应很慢尤其是加了重排序和查询改写之后。这类问题的原因往往很直接cross-encoder 重排序的候选集太大了。真没必要对 100 个候选都做重排控制在 20-30 个就够。查询改写调用了多轮 LLM 推理。如果业务时效要求高可以把多查询数量从 3 个减到 2 个或者仅在命中低置信度时才启用改写。向量库索引参数不合理。部分向量库的召回效率和索引类型、数据量大小直接相关这个需要具体分析。6.4 成本暴涨RAG 的成本大头通常不是向量检索而是 LLM 调用次数和输入 token 数量。如果查询改写、上下文压缩、生成后校验都用了 LLM一次用户请求可能触发 4 次以上模型调用。成本能不涨吗建议分级启用简单问题走快速通道只有复杂问题才走全链路优化。进 prompt 的上下文也值得盯。一次 token 支出 5000 和 1500价格差三倍多。重排序 压缩是省 token 最直接的手段。6.5 相似度检索质量好但召回率低这种情况听上去矛盾实际很常见向量检索捞回来的都是高相似度但没有必要信息的块真正信息所在的块被挤出去了。常见原因是分块粒度不合理或者车辆检索模型的“语义精度”和“语义召回”不可兼得。解决套路是把索引粒度调细多*粒度索引。混合检索之后扩大召回候选集再靠重排序从大候选集中找回“信息块”。7. 结尾的几句实在话这个系列走到“中篇”其实就是想表达一个核心观点RAG 的挺进不是靠某一个“聪明模型”而是一整套检索工程、上下文工程和评测体系的持续打磨。我在接项目时最深的体会是很多团队把大量精力去追最新的检索模型或生成策略却连一个像样的评测集都没有。没有评测你根本说不清楚一个改动到底是变好了还是变坏了——这种感觉比技术栈陈旧还要让人难受。如果你正在做 RAG 应用建议从简单的基线起步先把“专利检索 BM25 重排”这套扎实的组合搭建起来再逐步引入查询改写、上下文压缩等高级特性。每一层改动都用一个固定评测集去验证收益。实际做下来我见过很多项目单靠混合检索加重排效果就比原来纯向量检索提升了一大截。接下来的系列下篇我们再聊模块化 RAG 的流程编排以及 Self-RAG 这类带反思机制的镜形态。到时候会结合实际代码讲讲怎样才能让 RAG 从“知识检索工具”进化成“会思考的知识助手”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑