本文是 RAG 系列博客的第二篇。第一篇我们详细拆解了索引阶段文档加载 →文档切分 → embedding向量化 → 向量存储本篇将聚焦于查询与生成阶段涵盖查询理解、检索策略、重排序、Prompt 构造以及生成优化等核心环节。建议先阅读第一篇再继续本文。1. 回顾与引入在第一篇中我们把 RAG 比作“给大模型配一个专属图书馆管理员”并完成了第一步——建馆藏把海量文档切碎、向量化、存入向量数据库构建了一个可语义检索的知识索引。现在读者用户带着问题来了。第二阶段的任务就是馆员RAG 系统如何快速准确地找到最相关的几页资料并整理成一份条理清晰的摘要交给大模型专家阅读最终由专家给出有理有据的回答。整个第二阶段可以拆解为四个核心步骤用户问题 → [查询理解与改写] → [向量检索] → [重排序] → [Prompt 构造] → [大模型生成] → 最终回答 ↑ ↑ ↑ ↑ (在线实时) (相似度匹配) (精排过滤) (组装上下文)下面我们逐一深挖每个环节的技术细节、常见陷阱和最佳实践。2. 查询理解与优化Query Understanding Enhancement第二阶段的第一步不是直接去向量库检索而是先审视和优化用户的问题。原因很简单用户的问题往往是口语化、模糊、甚至带有歧义的直接用原始问题去检索结果可能差强人意。2.1 问题改写Query Rewriting场景用户问“苹果昨天股价怎么样”——这里的“苹果”可能指公司 Apple Inc.也可能指水果。如果直接检索可能会召回关于水果价格的文档。解决方案在检索之前利用大模型对原始问题进行改写或消歧生成一个更清晰、更具体的检索查询。常见改写策略包括策略说明示例多查询Multi-Query从不同角度生成多个同义变体分别检索后合并结果。原始“如何提高睡眠质量” → 生成“改善睡眠的方法”、“失眠怎么办”、“助眠技巧”假设文档生成HyDE先让大模型生成一个假设性的答案文档然后用这个“假答案”去检索真实文档。问题“什么是 Transformer 中的自注意力机制” → 先生成一段解释文字再用该文字去向量库召回更精准的技术文档。上下文补全结合对话历史将指代不明的代词替换为具体实体。历史“我们讨论了 GPT-4。” 当前“它的参数量是多少” → 改写为“GPT-4 的参数量是多少” 实战经验HyDE 在开放域问答中效果显著但在事实性极强的场景如法律、医疗中需要谨慎使用因为生成的“假答案”可能引入误导性信息。2.2 查询扩展Query Expansion除了改写还可以对查询进行扩展——即在原始关键词基础上补充同义词、相关词或上下位词提升召回率。传统方法基于 WordNet、同义词词典进行扩展。现代方法利用 LLM 生成相关术语或利用知识图谱如 Wikidata进行实体链接和扩展。例如用户搜索“治疗感冒的方法”系统自动扩展为“感冒 治疗 药物 休息 食疗 退烧”。3. 检索策略Retrieval Strategies优化完查询后就要真正去知识库中“找书”了。检索并非只有“向量检索”一条路实际工程中往往会结合多种检索方式以达到最佳效果。3.1 纯向量检索这是最基础的方案将优化后的查询文本通过 Embedding 模型转为向量然后在向量数据库中执行 ANN近似最近邻搜索召回 Top-K 个最相似的文档片段。优点支持语义匹配能召回同义词、隐含语义相关的文档。缺点对长尾实体、罕见专有名词、精确数字/ID 的匹配能力弱例如搜索“型号 XF-2000”向量检索可能不如关键词匹配准确。3.1.1稠密向量检索defencode_query(model,query):使用 BGE-M3 模型同时生成查询的稠密向量和稀疏向量vectorsmodel.encode([query],return_denseTrue,return_sparseTrue)# 提取列表中的第一个也是唯一一个元素的向量dense_vecvectors[dense_vecs][0]sparse_vecvectors[lexical_weights][0]returndense_vec,sparse_vec# 5. 稠密向量查询defdense_vector_search_example(client,query:str,limit:int5):modelBGEM3FlagModel(rD:/bge/bge-m3)# 将用户的提问与模型一并传入该方法进行向量化将用户的提问进行向量化生成稠密向量dense_vec,_encode_query(model,query)# 用户的提问提示词已经向量化了然后去查询向量数据库返回相似度匹配的chunk文档块# 这样返回的文档块还需进行重排因为还有稀疏向量的查询也就是说我们需要将稠密和稀疏做混合查询后再经过reranker(重排模型)进行排序# 这样返回的结果才是最终的结果resultsclient.search(collection_namedemo_collection,data[dense_vec],anns_fieldvector,limit5,search_params{metric_type:L2},output_fields[id,text,metadata])returnresults[0]3.1.2 稀疏向量检索# 6. 稀疏向量查询defsparse_vector_search_example(client,query:str,limit:int5):modelBGEM3FlagModel(rD:/bge/bge-m3)_,sparse_vecencode_query(model,query)resultsclient.search(collection_namedemo_collection,data[sparse_vec],anns_fieldsparse_vector,limitlimit,search_params{metric_type:IP},output_fields[id,text,metadata],)returnresults[0]3.2 关键词检索BM25 / TF-IDF传统的倒排索引检索基于词频和逆文档频率进行匹配。优点精确匹配能力强对专有名词、编号、日期等非常友好。缺点无法理解语义同义关系召回“汽车”相关文档时不会召回“轿车”。defscalar_query_examples(client,keyword:str大模型):# 对text字段进行模糊检索like_resclient.query(collection_namedemo_collection,filterftext like %{keyword}%,output_fields[id,text],limit5,)# 对metadata的JSON字段进行检索json_resclient.query(collection_namedemo_collection,filtermetadata[source] like %sample%,output_fields[id,metadata],limit5,)3.3 混合检索Hybrid Search主流生产级方案同时执行向量检索和关键词检索然后将两路结果通过某种方式合并如加权求和、RRF倒数排名融合Reciprocal Rank Fusion等算法得到最终的候选集。RRF 融合公式最常用Score(d) Σ_{r ∈ 检索器} 1 / (k rank_r(d))其中 k 通常取 60rank_r(d) 是文档 d 在第 r 个检索器中的排名。这个公式无需归一化不同检索器的分数简单且效果稳定。# 混合检索与重排序defhybrid_vector_search_example_rrf(client,query:str,limit:int5):modelBGEM3FlagModel(rD:/bge/bge-m3)dense_vec,sparse_vecencode_query(model,query)dense_reqAnnSearchRequest(data[dense_vec],anns_fieldvector,param{metric_type:COSINE},limitlimit,)sparse_reqAnnSearchRequest(data[sparse_vec],anns_fieldsparse_vector,param{metric_type:IP},limitlimit,)resultsclient.hybrid_search(collection_namedemo_collection,reqs[dense_req,sparse_req],rankerRRFRanker(k60),limitlimit,output_fields[id,text,metadata],)returnresults[0]✅ 最佳实践混合检索 RRF 融合是目前 RAG 生产环境中最稳妥的检索基线方案兼顾了语义泛化和精确匹配。3.4 多路检索Multi-Path Retrieval更进一步可以设计多条检索路径例如路径一向量检索语义路径路径二BM25 检索关键词路径路径三时间衰减排序针对时效性敏感的问题近期文档加分路径四知识图谱检索针对实体关系明确的问题最终将多路结果融合最大程度保证召回率。4. 重排序Reranking检索阶段召回的 Top-K 个候选比如 K50中排名靠前的未必最相关。这是因为向量检索的 ANN 算法追求效率牺牲了一定的精度混合检索融合后的排序可能不够精确向量相似度是“粗粒度”的语义匹配无法精准判断哪个片段最有助于回答具体问题。重排序的作用就是用一个更强大、更精细的模型对候选集合重新打分排序选出最相关的 Top-N 个例如 N5交给大模型。4.1 交叉编码器Cross-Encoder重排序的核心是交叉编码器模型它与双编码器即常规 Embedding 模型的区别在于类型结构输入输出特点双编码器Bi-Encoder两个独立的编码器(查询, 文档) 分别编码两个独立向量计算相似度速度快可离线建索引但精度相对低交叉编码器Cross-Encoder单个联合编码器(查询, 文档) 拼接后一起输入单个相似度分数0~1精度高但每次计算需在线推理耗时长重排序的做法是将查询与每个候选文档拼接成 [CLS] 查询 [SEP] 文档 [SEP] 的形式送入 Cross-Encoder得到精细化的相关性分数然后按新分数重新排序。主流模型开源BGE-reranker、Cohere Rerank、Sentence-Transformers 中的 cross-encoder 系列商业 APICohere Rerank API效果优秀且易用4.2 重排序的工程权衡性能问题Cross-Encoder 推理较慢。如果候选集是 50N5则每请求需 50 次推理。可通过 GPU 批处理加速或使用更轻量级的 reranker 模型。时机选择重排序应发生在所有检索路径合并之后对最终候选集做一次精细筛选。经验法则检索阶段召回 50~100 个候选重排序后取前 5~10 个送入大模型。这样既保证了召回率又控制了上下文长度。5. Prompt 构造Prompt Construction重排序完成后我们获得了最相关的若干文档片段。接下来的任务就是把这些片段和原始问题组合成一份高质量的提示词Prompt喂给大模型。5.1 基础模板最经典的 Prompt 模板如下你是一个乐于助人的助手。请根据以下参考资料回答用户的问题。 如果参考资料中不包含相关信息请明确告知“我无法从提供的资料中找到答案” 不要编造信息。 参考资料 [1] {chunk_1} [2] {chunk_2} ... [N] {chunk_N} 问题{user_query} 请给出准确、有条理的回答5.2 高级构造技巧技巧说明示例引用标注要求模型在回答中标注信息来源增强可溯源性。“根据参考资料[2]中的说明该产品的保修期为 12 个月。”去重与压缩多个检索片段可能存在内容重叠使用 LLM 进行初步压缩或摘要减少上下文冗余。对 5 个片段各生成一句摘要再拼接。条件性指令告诉模型哪些情况下应该拒绝回答如资料不足、信息矛盾。“如果参考资料之间存在矛盾请指出矛盾点不要强行统一。”角色扮演Role Prompting根据业务场景设定角色如“你是一位法律顾问”、“你是一位客服经理”影响回答风格。Few-shot 示例在 Prompt 中加入 1-2 个问答示例引导模型按期望格式输出。适用于结构化输出场景如 JSON、表格。5.3 上下文长度管理即使现代 LLM如 GPT-4、Claude支持 100K 的上下文窗口也不建议把所有检索片段都塞进去原因有三成本输入 Token 越多成本越高尤其是 API 调用。性能长上下文会增加推理延迟。“中间迷失”问题研究表明大模型对长上下文中间位置的信息关注度会显著下降Lost-in-the-Middle 现象。因此最相关的片段应放在 Prompt 的开头或结尾而非中间。 推荐策略将最相关的 1-2 个片段放在开头次相关的放在结尾其余放在中间——但这与“按相似度降序排列”的直觉相反需要特别注意。6. 生成与输出Generation Output大模型接收到构造好的 Prompt 后进行推理生成输出最终的回答。这一步看似“黑盒”但仍有不少可以优化的空间。6.1 生成参数调优6.2 流式输出Streaming对于需要实时交互的应用如聊天机器人建议启用流式输出SSE/WebSocket让用户逐字看到回答生成过程显著提升体验。6.3 引用与置信度生产级 RAG 系统通常会额外输出引用来源回答中每个观点对应的参考资料编号或文档链接。置信度评分模型对自己回答的信心程度可用 Token 概率或单独训练一个置信度判别器。当置信度低于阈值时系统可以主动触发“无法回答”或“转人工”流程。7. 进阶优化RAG 的“自我进化”除了上述线性流程还有一些更高阶的优化策略值得有追求的团队探索7.1 自反思 RAGSelf-RAG在生成过程中模型会插入特殊的 反思 TokenReflection Token对自己的输出进行自我评估如“这个信息是否有据可查”“我是否遗漏了什么”并据此决定是否需要再次检索或修正回答。这种方法能显著提升回答的准确性和完整性。7.2 自适应检索Adaptive Retrieval不是所有问题都需要检索。系统先判断问题的类型和复杂度简单事实性问答如“今天星期几”→ 直接由 LLM 回答无需检索。复杂知识型问题如“最新款 iPhone 的处理器型号”→ 触发检索。这样可以节省成本并降低延迟。判断器可以是一个轻量级分类模型也可以是一个基于 LLM 的 Router。7.3 迭代检索Iterative Retrieval对于复杂问题可以执行多轮“检索→生成→再检索→再生成”的循环。例如模型先生成一份初步回答从中提取出新的关键词或实体再进行第二轮检索补充信息最终生成更完善的答案。典型实现如 ReAct 模式。8. 完整流程总览至此整个 RAG 第二阶段的全貌已经展开。我们用一张流程图做最终总结┌─────────────────────┐ │ 用户输入问题 │ └──────────┬──────────┘ ▼ ┌─────────────────────┐ │ 查询理解与优化 │ │ · 改写 / 扩展 │ │ · HyDE / Multi-Query│ └──────────┬──────────┘ ▼ ┌────────────────────┼────────────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ 向量检索 │ │ BM25检索 │ │ 其他路径 │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └────────────────────┼────────────────────┘ ▼ ┌─────────────────────┐ │ 结果融合RRF │ │ 候选集 K50 │ └──────────┬──────────┘ ▼ ┌─────────────────────┐ │ 重排序Rerank │ │ Cross-Encoder │ │ 精选 Top-N5 │ └──────────┬──────────┘ ▼ ┌─────────────────────┐ │ Prompt 构造 │ │ · 引用标注 │ │ · 去重压缩 │ │ · 角色/指令设定 │ └──────────┬──────────┘ ▼ ┌─────────────────────┐ │ 大模型生成 │ │ · 参数调优 │ │ · 流式输出 │ └──────────┬──────────┘ ▼ ┌─────────────────────┐ │ 最终回答 引用溯源 │ └─────────────────────┘9. 总结在本文中我们深入剖析了 RAG 第二阶段——查询与生成阶段的全链路优化查询理解与优化通过改写、扩展、HyDE 等方式让检索“问对问题”。检索策略混合检索向量 BM25结合多路召回最大化召回率。重排序利用 Cross-Encoder 对候选集精筛提升 Top-N 的相关性。Prompt 构造精心设计提示词模板嵌入引用、角色、条件指令。生成与输出参数调优、流式输出、置信度评估。进阶进化Self-RAG、自适应检索、迭代检索等前沿方向。至此RAG 的两大阶段——索引阶段与查询生成阶段——已全部讲完。 你可以将这两篇文章作为 RAG 工程落地的技术检查清单逐步对照优化你的系统。