引言在前面的介绍中说明了RAG系统从原理到核心环节的全拆解从文档加载、文本分块到文本向量化、向量存储已经搭建起了RAG系统的完整基础链路。但想要让RAG系统在生产环境中达到可用的效果还需要解决检索精准度、结果多样性、上下文连贯性、多工具协同等一系列问题。LangChain 检索器Retrievers从基础检索到高级策略检索器Retrievers是LangChain中对检索能力的统一抽象它的核心职责是接收用户的自然语言查询返回与查询语义最相关的文档块。向量数据库的similarity_search是最基础的检索能力而LangChain的检索器在其基础上封装了大量高级检索策略解决基础检索的常见痛点比如用户查询表述模糊、检索结果冗余、上下文信息分散、检索结果与问题无关等。检索器的核心定位与基础用法所有检索器都实现了统一的BaseRetriever接口核心方法是invoke(query: str)输入用户查询返回相关的Document列表。最基础的检索器可直接通过向量数据库的as_retriever()方法生成它封装了向量库的基础检索能力同时支持配置检索参数from langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings # 加载已持久化的向量库 embeddings OpenAIEmbeddings(modeltext-embedding-ada-002) db Chroma( persist_directory./chroma_db, embedding_functionembeddings, collection_nameenterprise_knowledge_base, ) # 从向量库生成基础检索器 retriever db.as_retriever( search_typesimilarity, # 检索类型 search_kwargs{k: 4}, # 检索参数返回top4相关文档 ) # 执行检索 query RAG系统的核心流程是什么 docs retriever.invoke(query) # 查看结果 print(f检索到文档数量{len(docs)}) for doc in docs: print(doc.page_content)as_retriever()支持4种基础检索类型适配不同的基础场景检索类型核心能力核心参数适用场景similarity基础语义相似度检索默认类型k返回的文档数量绝大多数通用场景similarity_score_threshold相似度阈值检索仅返回超过阈值的文档score_threshold相似度阈值0-1之间越接近1越严格避免返回低相关的无效文档提升回答准确率mmr最大边际相关性检索平衡结果的相关性与多样性k返回文档数量lambda_mult多样性权重0最大多样性1最小多样性避免检索结果高度重复覆盖更多相关维度mmr_score_threshold结合MMR与相似度阈值同时控制相关性与多样性上述两者参数结合对结果多样性和精准度都有要求的场景示例相似度阈值检索过滤低相关内容# 仅返回相似度超过0.7的文档避免无效内容进入Prompt retriever db.as_retriever( search_typesimilarity_score_threshold, search_kwargs{k: 4, score_threshold: 0.7}, ) docs retriever.invoke(今天天气怎么样) # 知识库中无相关内容返回空列表主流高级检索策略详解与实战基础检索只能解决简单场景的问题面对复杂的企业级场景我们需要用到LangChain提供的高级检索器这里详解生产环境中最常用的5种高级检索策略解决RAG系统的核心痛点。MultiQueryRetriever解决用户查询表述歧义问题基础检索的核心痛点之一用户的查询表述可能模糊、口语化、有歧义直接用原始查询检索往往无法匹配到知识库中最相关的内容。MultiQueryRetriever的核心思路是调用LLM将用户的原始查询改写为多个不同表述、不同维度的查询语句用多个查询同时检索再合并去重结果大幅提升召回率。import os from langchain_classic.retrievers import MultiQueryRetriever from langchain_community.chat_models import ChatOpenAI from langchain_community.embeddings import DashScopeEmbeddings from langchain_community.vectorstores import Chroma # 加载向量库与LLM embeddings DashScopeEmbeddings(modeltext-embedding-v4, dashscope_api_keyos.getenv(DASHSCOPE_API_KEY) ) db Chroma(persist_directory./chroma_db, embedding_functionembeddings) llm ChatOpenAI(modelqwen-plus, api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, ) # 初始化多查询检索器 retriever MultiQueryRetriever.from_llm( retrieverdb.as_retriever(search_kwargs{k: 3}), # 基础检索器 llmllm, # 用于改写查询的LLM include_originalTrue, # 是否保留原始查询 ) # 执行检索 query RAG的核心优势 docs retriever.invoke(query) # 查看结果 print(f多查询检索到的文档数量{len(docs)}) for doc in docs: print(doc.page_content[:100])通过大模型改写的原始提示词DEFAULT_QUERY_PROMPT PromptTemplate( input_variables[question], templateYou are an AI language model assistant. Your task is to generate 3 different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of distance-based similarity search. Provide these alternative questions separated by newlines. Original question: {question}, )ContextualCompressionRetriever解决检索结果冗余问题基础检索返回的文档块中往往只有一小部分内容与用户的问题相关大量无关内容会占用Token同时干扰LLM的推理。ContextualCompressionRetriever的核心思路是先基础检索得到相关文档块再调用LLM/压缩器对每个文档块进行压缩、提取仅保留与用户问题直接相关的内容过滤无效信息。import os from langchain_classic.retrievers import ContextualCompressionRetriever from langchain_classic.retrievers.document_compressors import LLMChainExtractor from langchain_community.chat_models import ChatOpenAI from langchain_community.embeddings import DashScopeEmbeddings from langchain_community.vectorstores import Chroma # 加载向量库与LLM embeddings DashScopeEmbeddings(modeltext-embedding-v4, dashscope_api_keyos.getenv(DASHSCOPE_API_KEY) ) db Chroma(persist_directory./chroma_db, embedding_functionembeddings) llm ChatOpenAI(modelqwen-plus, api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, ) # 初始化文档压缩器 compressor LLMChainExtractor.from_llm(llm) # 初始化上下文压缩检索器 compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrieverdb.as_retriever(search_kwargs{k: 4}), ) # 执行检索 query RAG的核心优势 compressed_docs compression_retriever.invoke(query) # 查看压缩后的结果仅保留与问题相关的内容 print(f压缩后的文档数量{len(compressed_docs)}) for doc in compressed_docs: print(f压缩后的内容{doc.page_content})ParentDocumentRetriever解决语义完整性与检索精准度的矛盾分块环节有一个天然的矛盾块太小会破坏语义完整性LLM无法理解完整的上下文块太大会降低检索的精准度无法匹配到细粒度的答案。ParentDocumentRetriever的核心思路是父子文档分块策略。将文档拆分为大的父块保证语义完整再将每个父块拆分为小的子块保证检索精准度。子块向量化入库检索时先匹配相关子块再返回子块对应的完整父块给LLM完美平衡精准度与语义完整性。from langchain.retrievers import ParentDocumentRetriever from langchain.storage import InMemoryStore from langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain.document_loaders import TextLoader # 初始化嵌入模型 embeddings OpenAIEmbeddings(modeltext-embedding-ada-002) # 定义父子拆分器 parent_splitter RecursiveCharacterTextSplitter(chunk_size1000, chunk_overlap100) # 父块大尺寸 child_splitter RecursiveCharacterTextSplitter(chunk_size200, chunk_overlap20) # 子块小尺寸 # 初始化向量库存储子块向量与文档存储存储完整父块 vector_store Chroma(collection_nameparent_child_docs, embedding_functionembeddings) doc_store InMemoryStore() # 生产环境可替换为Redis、MongoDB等持久化存储 # 初始化父子文档检索器 retriever ParentDocumentRetriever( vectorstorevector_store, docstoredoc_store, child_splitterchild_splitter, parent_splitterparent_splitter, ) # 加载文档并入库 loader TextLoader(./data/企业产品手册.txt, encodingutf-8) docs loader.load() retriever.add_documents(docs) # 执行检索先匹配子块返回完整父块 query 产品的退款政策是什么 parent_docs retriever.invoke(query) print(f返回的父文档数量{len(parent_docs)}) for doc in parent_docs: print(f完整父文档内容{doc.page_content})TimeWeightedVectorStoreRetriever解决知识时效性问题很多业务场景中文档的时效性非常重要如新闻资讯、产品更新日志、政策文件越新的内容权重应该越高基础检索无法区分内容的新旧。TimeWeightedVectorStoreRetriever的核心思路是结合语义相似度与时间权重对检索结果进行重新排序越新的文档得分越高越旧的文档得分越低保证优先返回最新的相关内容。EnsembleRetriever融合多路检索结果提升召回率单一的检索方式往往有局限性比如语义检索擅长匹配语义相关的内容关键词检索擅长匹配精准的专业术语、产品名称。EnsembleRetriever的核心思路是集成多个不同的检索器如语义检索关键词检索对多路检索结果进行融合重排序RRF算法兼顾语义匹配与关键词匹配大幅提升召回率。