当前位置: 首页 > news >正文

【LangChain专栏】Retrieval 高级检索策略

引言

在前面的介绍中说明了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(model="text-embedding-ada-002") db = Chroma( persist_directory="./chroma_db", embedding_function=embeddings, collection_name="enterprise_knowledge_base", ) # 从向量库生成基础检索器 retriever = db.as_retriever( search_type="similarity", # 检索类型 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_type="similarity_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(model="text-embedding-v4", dashscope_api_key=os.getenv("DASHSCOPE_API_KEY") ) db = Chroma(persist_directory="./chroma_db", embedding_function=embeddings) llm = ChatOpenAI(model="qwen-plus", api_key=os.getenv("DASHSCOPE_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) # 初始化多查询检索器 retriever = MultiQueryRetriever.from_llm( retriever=db.as_retriever(search_kwargs={"k": 3}), # 基础检索器 llm=llm, # 用于改写查询的LLM include_original=True, # 是否保留原始查询 ) # 执行检索 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"], template="""You 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(model="text-embedding-v4", dashscope_api_key=os.getenv("DASHSCOPE_API_KEY") ) db = Chroma(persist_directory="./chroma_db", embedding_function=embeddings) llm = ChatOpenAI(model="qwen-plus", api_key=os.getenv("DASHSCOPE_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) # 初始化文档压缩器 compressor = LLMChainExtractor.from_llm(llm) # 初始化上下文压缩检索器 compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=db.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(model="text-embedding-ada-002") # 定义父子拆分器 parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) # 父块,大尺寸 child_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20) # 子块,小尺寸 # 初始化向量库(存储子块向量)与文档存储(存储完整父块) vector_store = Chroma(collection_name="parent_child_docs", embedding_function=embeddings) doc_store = InMemoryStore() # 生产环境可替换为Redis、MongoDB等持久化存储 # 初始化父子文档检索器 retriever = ParentDocumentRetriever( vectorstore=vector_store, docstore=doc_store, child_splitter=child_splitter, parent_splitter=parent_splitter, ) # 加载文档并入库 loader = TextLoader("./data/企业产品手册.txt", encoding="utf-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算法),兼顾语义匹配与关键词匹配,大幅提升召回率

http://www.jsqmd.com/news/482852/

相关文章:

  • Chord - Ink Shadow 代码生成潜力探索:辅助编写基础Python爬虫脚本
  • 从MySQL慢查询到CPU 100%:一次IO等待引发的性能故障复盘
  • 热键冲突智能诊断系统:破解Windows快捷键资源竞争的技术方案
  • MybatisPlus + ShardingSphere JDBC批量插入不返回主键?5.2.1版本终极解决方案
  • 避坑指南:Vivado多层IP嵌套时模块重名的3种解决方案(附IP-XACT文件修改示例)
  • 【2026年最新600套毕设项目分享】springboot河南传统文化的展示与交流网站平台(14153)
  • 线性规划(Linear Programming, LP)
  • 基于微信小程序的勤工俭学系统设计与实现
  • Phi-3-vision-128k-instruct代码实例:Python调用vLLM API实现图片问答
  • SpringBoot新手避坑指南:从零搭建Web项目到Thymeleaf模板实战
  • 立创EDA实战:TDA1521双声道HiFi功放板发烧级元件选型与PCB布局解析
  • 避坑指南:1688/抖音店铺批量备注最容易出错的3个环节(附正确操作截图)
  • Qwen3-14B开源大模型实践:Qwen3-14b_int4_awq在vLLM下支持function calling实测
  • 为什么92%的Dify团队仍在用错误方式统计Token成本?3个被官方文档忽略的计费陷阱与权威校验脚本
  • CLIP图文匹配工具实测:上传宠物图,自动识别“猫”还是“狗”
  • Qwen3-14b_int4_awq实战入门:基于Chainlit的Web化文本生成应用搭建
  • Unity2023中Dynamic Bone的实战应用:如何为女性角色添加逼真胸部物理效果(附参数调优指南)
  • 【仅限头部平台内部流出】MCP Sampling高级开发手册V3.2:含17个未公开API参数、8种跨服务采样对齐策略
  • 【新手必看】CrackMe下载失败?被删?打不开?
  • C++学习笔记
  • 外卖平台AI智能问答客服系统架构设计与实战优化
  • 老旧设备焕新:OpenCore Legacy Patcher的逆袭升级方案
  • 【2026年最新600套毕设项目分享】springboot基层智能化人员调度系统(14154)
  • PPT生成工具大揭秘!谁才是你的效率神器?
  • OpenClaw 接入飞书完整教程10分钟搭建专属 AI 助手
  • 立创·实战派ESP32-S3开发板全套资料(原理图/固件/例程)百度网盘下载中心
  • 3个技巧让AMD显卡实现Blender性能优化
  • 码农生存指南:从996到财务自由
  • 基于Web的留守儿童爱心网站的设计与实现
  • 立创ESP32-S3小智AI开发板:从开源复刻到新手友好的硬件设计优化之路