RAG系统进阶:从朴素检索到自适应检索增强
朴素RAG的局限性 标准RAG流程很简单:文档分块→向量化→检索top-k→拼接到prompt→生成回答。但在实际场景中,朴素RAG面临几个核心问题: 检索不准:用户查询与文档语义空间不匹配 排序不佳:向量相似度高≠回答有用 上下文冗余:top-k中可能包含大量无关内容 无法自纠错:检索不到就硬编答案 检索层优化 混合检索 纯向量检索擅长语义匹配但弱于精确关键词。BM25等稀疏检索正好互补。混合检索的实践方案: 向量检索(Dense):召回语义相关段落 关键词检索(Sparse/BM25):召回精确匹配段落 融合排序:RRF(Reciprocal Rank Fusion)算法合并两路结果 def rrf_fusion(dense_results, sparse_results, k=60): scores = {} for rank, doc in enumerate(dense_results): scores[doc.id] = scores.get(doc.id, 0) + 1/(k + rank) for rank, doc in enumerate(sparse_results): scores[doc.id] = scores.get(doc.id, 0) + 1/(k + rank) return sorted(scores.items(), key=lambda x: -x[1]) 查询改写 用户的原始query往往不是最佳检索query。通过LLM对查询进行改写: 查询扩展:生成多个语义变体query,分别检索后合并 HyDE:先让LLM生成一个假设性回答,用该回答的embedding去检索(对长尾问题效果显著) 子问题分解:复杂问题拆成多个子问题分别检索 重排序(Reranking) 向量检索的召回质量参差不齐。在召回后加一个cross-encoder重排层: 向量检索召回top-50(宽召回) Cross-encoder(如bge-reranker-v2)对每对(query, doc)打分 按分数取top-5(精排序) Cross-encoder比bi-encoder准确率高20-30%,但速度慢,所以做两阶段检索。 生成层优化 上下文压缩 检索回来的文档可能很长,需要压缩到LLM能高效处理的篇幅: 提取式:用小模型抽取关键句子 摘要式:LLM对每个文档生成精简摘要 LLMLingua:基于困惑度删除低信息token,可压缩到原长的1/10 引用标注 高质量RAG需要可溯源。实现方式: 在prompt中要求模型标注引用段落编号 后处理时验证引用的段落确实支持该陈述 不支持的引用标记为"未验证" 自适应RAG:让模型决定怎么检索 Self-RAG Self-RAG让模型学会自主决定: 是否需要检索(通过特殊token [Retrieve]/[No Retrieve]) 检索到的段落是否相关([Relevant]/[Irrelevant]) 回答是否被段落支持([Supported]/[Partially Supported]) 这种方式将检索决策内化为模型能力,需要通过强化学习训练。 CRAG(Corrective RAG) CRAG在检索后增加一个"检索评估器": 评估检索结果质量:Correct / Ambiguous / Incorrect 如果Incorrect,触发web搜索补充 如果Ambiguous,将检索结果与web结果合并 对最终结果重排序后生成 Adaptive-RAG 根据查询复杂度自适应选择策略: 简单查询→单次检索 复杂查询→多跳检索+推理 分类器判断查询类型,路由到对应处理流水线 工程实践要点 评估体系 RAG系统必须有量化评估。推荐使用RAGAS框架,四个核心指标: ...