RAG高级技巧
RAG 高级技巧:从基础检索到生产级智能问答系统
RAG 技术演进历程
Retrieval-Augmented Generation(检索增强生成)自 2023 年提出以来,已成为企业落地大模型应用的首选方案。从最初简单的"检索-生成"模式,到 2026 年的智能化 RAG 系统,技术经历了四个阶段的演进:
阶段1: 基础 RAG (2023) ↓ 简单向量检索 + LLM 生成 阶段2: 高级 RAG (2024) ↓ 混合检索、重排序、查询优化 阶段3: 模块化 RAG (2025) ↓ 可插拔组件、流水线编排 阶段4: Agentic RAG (2026) ↓ 自主决策、多轮检索、工具调用每个阶段都解决了上一阶段的痛点,同时引入了新的挑战。理解这个演进过程,有助于我们选择合适的技术方案。
基础 RAG 的局限性
问题1: 检索质量不足
传统向量检索只关注语义相似性,但语义相似不等于答案相关。
# 传统做法:简单的余弦相似度fromlangchain.vectorstoresimportChromafromlangchain.embeddingsimportOpenAIEmbeddings embeddings=OpenAIEmbeddings()vectorstore=Chroma.from_documents(documents,embeddings)# 问题示例query="如何优化数据库性能"results=vectorstore.similarity_search(query,k=5)# 可能返回的结果:# 1. "数据库性能测试报告" - 不相关,但语义相似# 2. "数据库架构设计文档" - 不相关# 3. "PostgreSQL 性能优化指南" - 相关!# 4. "MySQL 基础教程" - 不相关# 5. "数据库备份策略" - 不相关问题分析:前5个结果中只有1个真正相关,准确率仅20%。这是因为向量检索无法理解用户的真实意图,只能找到"看起来相似"的内容。
问题2: 上下文窗口浪费
# 检索到的文档doc1="数据库性能测试报告\n\n本报告详细记录了..."# 8000字doc2="数据库架构设计文档\n\n本文档描述了..."# 6000字doc3="PostgreSQL 性能优化指南\n\n1. 索引优化..."# 5000字(只有这部分相关)# 实际相关内容:500字# 浪费的 token:8000 + 6000 + 4500 = 18500字问题分析:大段文档中只有小部分内容真正相关,大量 token 被浪费在无关内容上,增加了成本和延迟。
问题3: 缺乏推理能力
用户问题:"比较 PostgreSQL 和 MySQL 在高并发场景下的性能差异" RAG 系统可能返回: - 文档A:PostgreSQL 性能优化技巧 - 文档B:MySQL 性能优化技巧 - 文档C:高并发系统设计原则 但缺少直接的对比分析,需要 LLM 自行整合, 效果不稳定且容易产生幻觉。高级 RAG 核心技巧
技巧1: 混合检索(Hybrid Search)
结合关键词检索和向量检索,取长补短。
fromlangchain.retrieversimportEnsembleRetrieverfromlangchain_community.retrieversimportBM25Retrieverfromlangchain.vectorstoresimportFAISSclassHybridRetriever:def__init__(self,documents,embeddings):# 向量检索器self.vectorstore=FAISS.from_documents(documents,embeddings)self.vector_retriever=self.vectorstore.as_retriever(search_kwargs={"k":10})# BM25 关键词检索器self.bm25_retriever=BM25Retriever.from_documents(documents)self.bm25_retriever.k=10# 混合检索器self.ensemble_retriever=EnsembleRetriever(retrievers=[self.bm25_retriever,self.vector_retriever],weights=[0.4,0.6]# BM25 40%, 向量 60%)defretrieve(self,query:str,top_k:int=5):# 混合检索results=self.ensemble_retriever.invoke(query)# 去重(同一文档可能被两种方法都检索到)seen=set()unique_results=[]fordocinresults:ifdoc.metadata.get('id')notinseen:seen.add(doc.metadata.get('id'))unique_results.append(doc)iflen(unique_results)>=top_k:breakreturnunique_results# 使用示例retriever=HybridRetriever(documents,embeddings)results=retriever.retrieve("如何优化数据库性能",top_k=5)# 效果提升:准确率从 20% 提升到 45%为什么混合检索有效?
- 向量检索:擅长理解语义,找到概念相关的内容
- BM25检索:擅长关键词匹配,找到精确匹配的内容
- 结合优势:向量检索找到相关主题,BM25确保关键词出现
技巧2: 重排序(Reranking)
检索后用精细模型二次排序,大幅提升相关性。
fromlangchain.retrieversimportContextualCompressionRetrieverfromlangchain.retrievers.document_compressorsimportCrossEncoderRerankerfromlangchain_community.cross_encodersimportHuggingFaceCrossEncoderclassRerankingRetriever:def__init__(self,base_retriever,model_name="BAAI/bge-reranker-v2-m3"):# 加载 Cross-Encoder 重排序模型self.cross_encoder=HuggingFaceCrossEncoder(model_name=model_name)# 创建重排序器self.compressor=CrossEncoderReranker(model=self.cross_encoder,top_n=5# 只返回最相关的5个文档)# 包装基础检索器self.reranking_retriever=ContextualCompressionRetriever(base_compressor=self.compressor,base_retriever=base_retriever)defretrieve(self,query:str):# 先检索(可能返回10个),再重排序(返回5个)returnself.reranking_retriever.invoke(query)# 使用示例hybrid_retriever=HybridRetriever(documents,embeddings)reranking_retriever=RerankingRetriever(hybrid_retriever.ensemble_retriever,model_name="BAAI/bge-reranker-v2-m3"# 中文优化)results=reranking_retriever.retrieve("如何优化数据库性能")# 效果提升:准确率从 45% 提升到 75%推荐的重排序模型:
| 模型 | 语言 | 大小 | 性能 |
|---|---|---|---|
BAAI/bge-reranker-v2-m3 | 多语言 | 560M | ⭐⭐⭐⭐⭐ |
cross-encoder/ms-marco-MiniLM-L-6-v2 | 英文 | 22M | ⭐⭐⭐⭐ |
BAAI/bge-reranker-large | 中英文 | 330M | ⭐⭐⭐⭐⭐ |
技巧3: 查询扩展与重写
将用户查询扩展为多个角度,提高检索覆盖面。
fromlangchain.promptsimportChatPromptTemplatefromlangchain.chat_modelsimportChatOpenAIclassQueryExpander:def__init__(self,llm=None):self.llm=llmorChatOpenAI(model="gpt-4",temperature=0)self.expansion_prompt=ChatPromptTemplate.from_messages([("system","""你是一个查询优化专家。将用户的原始查询扩展为3个不同角度的查询: 1. 同义改写:用不同的词汇表达相同的意思 2. 补充上下文:添加必要的背景信息 3. 细化具体问题:将模糊问题具体化 输出格式: 1. [同义改写] 2. [补充上下文] 3. [细化问题]"""),("user","{query}")])defexpand(self,query:str)->list[str]:response=self.llm.invoke(self.expansion_prompt.format(query=query))# 解析结果lines=response.content.strip().split('\n')expanded_queries=[]forlineinlines:ifline.strip()andline[0].isdigit():# 移除序号expanded=line.split('.',1)[1].strip()expanded_queries.append(expanded)# 包含原始查询return[query]+expanded_queries# 使用示例expander=QueryExpander()expanded=expander.expand("数据库优化")# 结果:# 原始: "数据库优化"# 1. "数据库性能调优方法"# 2. "MySQL/PostgreSQL 性能优化最佳实践"# 3. "高并发场景下数据库索引和查询优化技巧"多查询检索与融合:
classMultiQueryRetriever:def__init__(self,base_retriever,query_expander):self.retriever=base_retriever self.expander=query_expanderdefretrieve(self,query:str,top_k:int=5):# 扩展查询expanded_queries=self.expander.expand(query)# 分别检索all_docs=[]forqinexpanded_queries:docs=self.retriever.invoke(q)all_docs.extend(docs)# 去重并按相关性排序unique_docs=self._deduplicate_and_rank(all_docs,query)returnunique_docs[:top_k]def_deduplicate_and_rank(self,docs,original_query):# 基于文档ID去重seen={}fordocindocs:doc_id=doc.metadata.get('id',hash(doc.page_content))ifdoc_idnotinseen:seen[doc_id]=doc# 重新计算相关性分数# ... (实现细节省略)returnlist(seen.values())# 使用multi_query_retriever=MultiQueryRetriever(hybrid_retriever.ensemble_retriever,expander)results=multi_query_retriever.retrieve("数据库优化")技巧4: 智能分块策略
根据文档类型选择不同的分块方式。
fromlangchain.text_splitterimport(RecursiveCharacterTextSplitter,MarkdownHeaderTextSplitter,PythonCodeTextSplitter,CharacterTextSplitter)fromtypingimportList,DictimportreclassSmartTextSplitter:"""智能文本分块器"""def__init__(self):# Markdown 分块器self.markdown_splitter=MarkdownHeaderTextSplitter(headers_to_split_on=