Skip to content

RAG 优化策略与知识库模块功能解析 ​

章节核心学习内容 ​

  1. LangChain RAG开发多阶段优化策略:掌握查询转换、路由、问题构造、索引、检索、生成6个阶段的优化思路,近20种优化策略的案例实现与Prompt编写,以及复杂RAG应用从流程图到LCEL链的实现思路。
  2. LLMOps知识库模块功能解析:学习知识库模块UI功能拆解、API文档撰写、数据库表ER图设计;掌握多知识库/多用户/多应用关联的产品设计思路;实现向量数据库元数据结构设计、weaviate数据库复杂条件筛选及数据隔离与同步。

一、RAG开发6个阶段优化策略分析 ​

01. LLM应用中的RAG开发核心目标与优化原则 ​

RAG优化核心目标 ​

提升LLM生成内容的准确性,针对Transformer架构大模型,实现该目标的核心三步骤:

  1. 传递更准确的内容:让LLM识别到与提问高度关联的信息,提升生成准确性;
  2. 让重要的内容更靠前:利用GPT模型注意力机制,Prompt中靠前内容权重更高;
  3. 尽可能不传递不相关内容:缩短文档块大小,减少无关内容占比,提升信息密度。

关键优化注意点 ​

  • 优化策略并非越多越好,策略越多单次响应成本越高、性能越差,需根据场景合理选择;
  • RAG优化本质是切割合适的文档块、构造更准确的搜索语句、正确排序文档、剔除重复无关检索内容;
  • 核心可优化组件:query(提问查询)、TextSplitter(文本分割器)、VectorStore(向量数据库)、Retriever(检索器)、Prompt(提示词)。

RAG开发6大阶段划分 ​

将RAG流程按功能模块拆分为查询转换、路由、问题构建、索引、检索、生成6个阶段,每个阶段对应不同优化策略,主流策略包括问题转换、多路召回、混合检索、搜索重排、动态路由、图查询、自检索等,策略间相互影响且适用场景不同。

核心:RAG优化与开发框架/编程语言无关,掌握优化思路是核心。

02. RAG开发6个阶段核心优化策略映射 ​

开发阶段核心优化策略
查询转换(Query Translation)多查询重写、问题分解、Step-Back回答回退、HyDE混合策略、集成检索
路由(Routing)动态路由、知识库路由、提示词路由
问题构建(Query Construction)关系型查询构建、自然语言查询生成、查询重构
索引(Indexing)切块优化、多表征索引、领域特定Embedding、增量索引
检索(Retrieval)多路召回、混合检索、搜索重排(RRF)、精修RAG、Corrective-RAG/CRAG
生成(Generation)自检索(Self-RAG)、RRR、基于“自我思考”的LLM生成控制、检索质量判定与重新检索

二、多查询重写策略提升检索准确性 ​

01. Multi-Query多查询策略核心原理 ​

多查询策略(子查询):从多个角度重写用户原始问题,生成多个相关子问题,分别检索后合并去重返回唯一文档,核心解决基于距离的相似性搜索的局限性,提升检索覆盖面。

运行流程 ​

原始问题 → LLM生成多个子问题 → 每个子问题独立检索向量数据库 → 文档列表合并+去重 → 传递给LLM生成答案

LangChain实现:MultiQueryRetriever ​

LangChain封装了专用检索器MultiQueryRetriever,支持构造函数/from_llm类方法实例化,核心参数:

  1. retriever:基础检索器(必填);
  2. llm:大语言模型,用于生成子问题(必填);
  3. prompt:子问题生成提示模板(可选,有默认值);
  4. parser_key:解析键(已弃用);
  5. include_original:是否保留原始问题检索(默认False)。

代码实现示例(基于Weaviate向量数据库) ​

python
import weaviate
import dotenv
from langchain.retrievers import MultiQueryRetriever
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_weaviate import WeaviateVectorStore
from weaviate.auth import AuthApiKey

dotenv.load_dotenv()
# 1. 构建向量数据库与基础检索器
db = WeaviateVectorStore(
    client=weaviate.connect_to_wcs(
        cluster_url="https://eftofnujtxqcsa0sn272jw.c0.us-west3.gcp.weaviate.cloud",
        auth_credentials=AuthApiKey("21pzYy0or12dx9xCoZ12beuDJNEbB0")
    ),
    index_name="DatasetDemo",
    text_key="text",
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
)
retriever = db.as_retriever(search_type="mmr")
# 2. 创建多查询检索器
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=retriever,
    llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0),
)
# 3. 执行检索
docs = multi_query_retriever.invoke("关于LLMOps应用配置的文档有哪些")
print(docs)
print(len(docs))

02. 核心注意事项 ​

内置默认Prompt ​

默认Prompt用于生成3个子问题,基于OpenAI模型调试,效果最优,核心逻辑:

python
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}"
)

中文场景适配 ​

国内模型需将Prompt改为中文,避免子问题生成格式错误,示例:

python
from langchain_core.prompts import ChatPromptTemplate
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=retriever,
    llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0),
    prompt=ChatPromptTemplate.from_template(
        "你是一个AI语言模型助手。你的任务是生成给定用户问题的3个不同版本,以从向量数据库中检索相关文档。通过提供用户问题的多个视角,你的目标是帮助用户克服基于距离的相似性搜索的一些限制。请用换行符分隔这些替代问题。原始问题:{question}"
    )
)

关键使用技巧 ​

  1. 模型参数设置:生成子问题时将temperature=0,减少幻觉,保证生成确定性;
  2. 格式兼容:部分模型生成的子问题可能不按\n分割,需多次测试Prompt,或设置include_original=True,兜底原始问题检索;
  3. 去重逻辑:底层仅通过循环遍历实现简单去重,无权重计算,核心代码:
    python
    def _unique_documents(documents: Sequence[Document])->List[Document]:
        return [doc for i,doc in enumerate(documents) if doc not in documents[:i]]
  4. 模型选择:子问题生成对LLM能力要求低,可使用轻量本地模型+定制Prompt,降低成本。

三、RAG多查询结果融合策略 ​

01. 多查询结果融合核心问题与RRF算法 ​

多查询重写的局限性 ​

  1. 合并文档时未考虑数量控制,极端情况会返回大量冗余文档;
  2. 无文档权重计算,仅按检索顺序合并,高相关性文档可能被后置。

RAG融合核心思想 ​

在Multi-Query基础上,通过RRF(Reciprocal Rank Fusion,倒排序排名算法) 对多轮检索结果重新排序+去重+TopK筛选,再传递给LLM生成答案,解决权重和数量问题。

RRF算法原理 ​

由滑铁卢大学与Google联合开发,核心公式: [R R F_{score }(d \in D)=\sum_{r \in R} \frac{1}{k+r(d)}]

  • 参数说明:D为相关文档全集,k为固定常数(最优值60),r(d)为文档d在某一子查询检索结果中的排名;
  • 核心特点:高排名文档权重更高,但低排名文档的重要性不会指数级消失,兼顾检索覆盖面和准确性;
  • 计算逻辑:二重遍历,外层遍历文档全集,内层遍历各子查询结果,累计文档在不同结果中的倒序排名得分。

RRF算法Python实现 ​

python
from langchain.load import dumps, loads
from langchain_core.documents import Document

def rrf(results:list[list],k:int=60)->list[tuple]:
    """倒排序排名融合RRF算法,合并多检索结果并排序"""
    fused_scores = {}
    # 遍历每个子查询的检索结果
    for docs in results:
        # 遍历文档并计算得分
        for rank, doc in enumerate(docs):
            doc_str = dumps(doc)  # 文档序列化
            if doc_str not in fused_scores:
                fused_scores[doc_str] = 0
            fused_scores[doc_str] += 1 / (rank + k)  # 累计RRF得分
    # 按得分降序排序并反序列化
    reranked_results = [
        (loads(doc), score) 
        for doc, score in sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
    ]
    return reranked_results

02. 多查询结果融合策略LangChain实现 ​

LangChain无内置融合检索器,需继承MultiQueryRetriever并重写核心方法,实现RRF排序与合并。

核心重写方法 ​

  1. retrieve_documents:重写为返回二维文档列表(每个子查询对应一个子列表);
  2. unique_union:重写为基于RRF算法计算得分、排序、去重并返回TopK文档。

完整代码实现 ​

python
from typing import List
import dotenv
import weaviate
from langchain.load import dumps, loads
from langchain.retrievers import MultiQueryRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_weaviate import WeaviateVectorStore
from weaviate.auth import AuthApiKey

dotenv.load_dotenv()

class RAGFusionRetriever(MultiQueryRetriever):
    """基于RRF的RAG多查询结果融合检索器"""
    k: int = 4  # 返回TopK文档
    def __init__(self, k: int=4,** kwargs):
        super().__init__(**kwargs)
        self.k = k

    def retrieve_documents(
        self, queries: List[str], run_manager: CallbackManagerForRetrieverRun
    )->List[List]:
        """重写检索方法,返回二维文档列表"""
        documents = []
        for query in queries:
            docs = self.retriever.invoke(query, config={"callbacks": run_manager.get_child()})
            documents.append(docs)
        return documents

    def unique_union(self, documents:List[List])->List[Document]:
        """重写合并方法,使用RRF算法排序去重"""
        fused_scores = {}
        for docs in documents:
            for rank, doc in enumerate(docs):
                doc_str = dumps(doc)
                if doc_str not in fused_scores:
                    fused_scores[doc_str] = 0
                fused_scores[doc_str] += 1 / (rank + 60)
        # 排序并取TopK
        reranked = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
        return [loads(doc) for doc, _ in reranked[:self.k]]

# 1. 构建基础检索器
db = WeaviateVectorStore(
    client=weaviate.connect_to_wcs(
        cluster_url="https://eftofnujtxqcsa0sn272jw.c0.us-west3.gcp.weaviate.cloud",
        auth_credentials=AuthApiKey("21pzYy0or12dx9xCoZ12beuDJNEbB0")
    ),
    index_name="DatasetDemo",
    text_key="text",
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
)
retriever = db.as_retriever(search_type="mmr")

# 2. 创建融合检索器并执行检索
rag_fusion_retriever = RAGFusionRetriever.from_llm(
    retriever=retriever,
    llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0),
)
docs = rag_fusion_retriever.invoke("关于LLMOps应用配置的文档有哪些")
print(docs)
print(len(docs))

四、问题分解策略提升复杂问题检索正确率 ​

01. 复杂问题检索的难点与分解策略核心 ​

复杂问题检索难点 ​

  1. 复杂问题由多个步骤/子问题组成,向量数据库存储的基础文档与问题相似度低(文本嵌入模型无法无损记录段落信息);
  2. 问题复杂度高导致LLM无法一次性生成答案,大量相关文档会压缩LLM上下文长度,影响生成效果。

Decomposition问题分解策略核心 ​

将单个复杂问题分解为多个独立子问题,分别检索并生成答案,再通过迭代式或并行式合并子答案得到最终结果,与多查询重写的核心区别:

  • 多查询重写:生成同维度的相似问题,并行检索后直接合并文档;
  • 问题分解:生成有逻辑关联的子问题,按逻辑处理子答案后合并。

两种实现方案 ​

  1. 迭代式回答:深度优先,解决完前一个子问题后,将前问题+答案传递给下一个子问题,迭代至最后得到最终答案;
  2. 并行式回答:所有子问题同时检索并生成答案,最后由LLM汇总所有子答案,生成最终答案。

02. 迭代式回答实现(LangChain自定义链) ​

LangChain无内置分解检索器,需自定义链实现问题分解+迭代检索+答案合并。

核心步骤 ​

  1. 构建问题分解链:将复杂问题拆分为多个子问题;
  2. 构建迭代问答链:基于子问题检索,结合历史问答对生成子答案;
  3. 循环迭代:遍历所有子问题,累计历史问答对,最终合并为完整答案。

完整代码实现 ​

python
from operator import itemgetter
import dotenv
import weaviate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_weaviate import WeaviateVectorStore
from weaviate.auth import AuthApiKey

dotenv.load_dotenv()

def format_qa_pair(question: str, answer: str)-> str:
    """格式化问题-答案对"""
    return f"Question: {question}\nAnswer:{answer}\n\n".strip()

# 1. 构建问题分解链:将复杂问题拆分为子问题
decomposition_prompt = ChatPromptTemplate.from_template("""
你是一个乐于助人的AI助理,可以针对一个输入问题生成多个相关的子问题,
目标是将输入问题分解成一组可以独立回答的子问题或子任务。
生成与以下问题相关的多个搜索查询:{question}
并使用换行符进行分割,输出3个子问题/子查询:
""")
decomposition_chain = (
    {"question": RunnablePassthrough()}
    | decomposition_prompt
    | ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0)
    | StrOutputParser()
    | (lambda x: x.strip().split("\n"))
)

# 2. 构建向量数据库与基础检索器
db = WeaviateVectorStore(
    client=weaviate.connect_to_wcs(
        cluster_url="https://mbakeruerziae6psyex7ng.c0.us-west3.gcp.weaviate.cloud",
        auth_credentials=AuthApiKey("zltVa9zs0xUcfafelsggGyyH6tnTYQYJVBx")
    ),
    index_name="DatasetDemo",
    text_key="text",
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
)
retriever = db.as_retriever(search_type="mmr")

# 3. 构建迭代问答链:结合子问题、历史问答对、检索上下文生成答案
prompt = ChatPromptTemplate.from_template("""
这是你需要回答的问题: {question}
这是所有可用的背景问题和答案对: {qa_pairs}
这是与问题相关的额外背景信息: {context}
使用上述背景信息和所有可用的背景问题和答案对来回答这个问题: {question}
""")
chain = (
    {
        "context": itemgetter("question") | retriever,
        "question": itemgetter("question"),
        "qa_pairs": itemgetter("qa_pairs"),
    }
    | prompt
    | ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0)
    | StrOutputParser()
)

# 4. 执行问题分解与迭代回答
question = "关于LLMOps应用配置的文档有哪些"
sub_questions = decomposition_chain.invoke(question)
qa_pairs = ""
for sub_question in sub_questions:
    answer = chain.invoke({"question": sub_question, "qa_pairs": qa_pairs})
    qa_pairs += "\n---\n" + format_qa_pair(sub_question, answer)
    print(f"问题:{sub_question}")
    print(f"答案:{answer}\n")

并行式回答课后练习提示 ​

使用Runnable.map实现子问题并行检索与回答,替代循环迭代,核心思路:

  1. 将子问题列表转为可并行处理的输入;
  2. 用map方法让问答链并行处理所有子问题;
  3. 构建答案汇总链,将所有子答案合并为最终答案。

五、Step-Back回答回退策略扩大检索范围 ​

01. 前置知识:LangChain少量示例提示模板(Few-shot) ​

少量示例(Few-shot)是指导LLM生成的高效方式,通过提供示例问题-答案对,降低Prompt复杂度,明确生成规范,LangChain封装了专用模板:

  • FewShotChatMessagePromptTemplate:适用于聊天模型;
  • FewShotPromptTemplate:适用于文本补全模型。

使用示例 ​

python
import dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()
# 1. 定义示例模板与示例
example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{question}"),
    ("ai", "{answer}"),
])
examples = [
    {"question": "帮我计算下20*15等于多少?", "answer": "300"},
    {"question": "帮我计算下2+2等于多少?", "answer": "4"},
    {"question": "帮我计算下2+3等于多少?", "answer": "5"},
]
# 2. 构建少量示例提示模板
few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)
# 3. 构建最终Prompt并执行
prompt = ChatPromptTemplate.from_messages([
    few_shot_prompt,
    ("system", "你是一个可以计算复杂数学问题的聊天机器人"),
    ("human", "{question}"),
])
llm = ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0)
chain = prompt | llm | StrOutputParser()
print(chain.invoke("帮我计算下14*15等于多少"))  # 输出:210

02. Step-Back回答回退策略核心原理 ​

策略概念 ​

Step-Back(后退提示):为复杂问题生成一个更抽象、更通用的前置回退问题,通过回退问题检索扩大范围,再结合回退问题的检索结果+原始问题执行RAG生成,核心解决复杂问题直接检索相关性低的问题。

概念来源:https://arxiv.org/pdf/2310.06117.pdf

核心优势 ​

  1. 回退问题更易检索到相关文档,提升检索覆盖面;
  2. 仅多调用一次LLM生成回退问题,性能高于问题分解策略;
  3. 对LLM能力要求低,轻量模型即可实现,无迭代信息丢失风险。

运行流程 ​

原始问题 → 少量示例Prompt → LLM生成回退问题 → 回退问题检索向量数据库 → 文档列表 + 原始问题 → LLM生成最终答案

03. Step-Back检索器自定义实现(LangChain) ​

继承LangChain基础检索器BaseRetriever,实现回退问题生成+检索核心逻辑。

完整代码实现 ​

python
from typing import List
import dotenv
import weaviate
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from langchain_core.language_models import BaseLanguageModel
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
from langchain_core.retrievers import BaseRetriever
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_weaviate import WeaviateVectorStore
from weaviate.auth import AuthApiKey

dotenv.load_dotenv()

class StepBackRetriever(BaseRetriever):
    """Step-Back回答回退检索器"""
    retriever: BaseRetriever
    llm: BaseLanguageModel

    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    )->List[Document]:
        # 1. 构建少量示例Prompt,定义回退问题生成规则
        examples = [
            {"input": "慕课网上有关于AI应用开发的课程吗?", "output": "慕课网上有哪些课程?"},
            {"input": "小课出生在哪个国家?", "output": "小课的个人经历是怎样的?"},
            {"input": "司机可以开快车吗?", "output": "司机可以做什么?"},
        ]
        example_prompt = ChatPromptTemplate.from_messages([
            ("human", "{input}"),
            ("ai", "{output}"),
        ])
        few_shot_prompt = FewShotChatMessagePromptTemplate(
            examples=examples,
            example_prompt=example_prompt,
        )
        # 2. 构建回退问题生成Prompt
        system_prompt = """
        你是一个世界知识的专家。你的任务是回退问题,将问题转述为更一般或者前置问题,这样更容易回答,请参考示例来实现。
        """
        prompt = ChatPromptTemplate.from_messages([
            ("system", system_prompt),
            few_shot_prompt,
            ("human", "{question}"),
        ])
        # 3. 构建回退问题生成+检索链
        chain = (
            {"question": RunnablePassthrough()}
            | prompt
            | self.llm
            | StrOutputParser()
            | self.retriever
        )
        return chain.invoke(query)

# 1. 构建基础检索器
db = WeaviateVectorStore(
    client=weaviate.connect_to_wcs(
        cluster_url="https://mbakeruerziae6psyex7ng.c0.us-west3.gcp.weaviate.cloud",
        auth_credentials=AuthApiKey("21tPVazs0xucfafelsggGyyH6tnTYQYJVBx")
    ),
    index_name="DatasetDemo",
    text_key="text",
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
)
retriever = db.as_retriever(search_type="mmr")

# 2. 创建回退检索器并执行检索
step_back_retriever = StepBackRetriever(
    retriever=retriever,
    llm=ChatOpenAI(model="gpt-3.5-turbo-16k", temperature=0),
)
documents = step_back_retriever.invoke("人工智能会让世界发生翻天覆地的变化吗?")
print(documents)
print(len(documents))

策略对比:Step-Back vs 问题分解 ​

对比维度Step-Back回答回退问题分解(迭代式)
性能高(仅多1次LLM调用)低(多次迭代检索+生成)
LLM要求低(轻量模型即可)高(需准确生成/合并子答案)
信息丢失无(无迭代)有(迭代中信息逐步丢失,可能偏离原始问题)
实现复杂度低(自定义检索器即可)高(需自定义多条链+迭代逻辑)
适用场景复杂描述性问题多步骤/逻辑关联性强的复杂问题

六、集成多种检索器算法实现混合检索 ​

01. EnsembleRetriever集成检索器核心原理 ​

核心优势 ​

LangChain封装的EnsembleRetriever支持将多个不同类型的检索器集成,通过RRF算法对各检索器的结果进行重新排序、合并,充分利用不同检索算法的互补优势,提升检索效果。

经典混合检索组合:稀疏+密集检索 ​

  • 稀疏检索(如BM25):擅长关键词匹配,对字面相似的问题检索效果好;
  • 密集检索(如向量嵌入相似度):擅长语义匹配,对同义不同形的问题检索效果好;
  • 混合检索:结合两者优势,覆盖更多检索场景,是Dify、Coze、智谱等平台的主流检索策略。

核心参数 ​

  1. retrievers:检索器列表(必填);
  2. weights:各检索器的权重(可选,默认等权重)。

02. 混合检索实现示例(BM25 + FAISS) ​

前置依赖 ​

安装BM25检索库:

bash
pip install -U rank_bm25

完整代码实现 ​

python
import dotenv
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

dotenv.load_dotenv()

# 1. 准备测试文档列表
documents = [
    Document(page_content="笨笨是一只很喜欢睡觉的猫咪", metadata={"page": 1}),
    Document(page_content="我喜欢在晚上听音乐。这让我感到放松。", metadata={"page": 2}),
    Document(page_content="猫咪在窗台上打盹,看起来非常可爱.", metadata={"page": 3}),
    Document(page_content="学习新技能是每个人都应该追求的目标。", metadata={"page": 4}),
    Document(page_content="我最喜欢的食物是意大利面,尤其是番茄酱的那种。", metadata={"page": 5}),
    Document(page_content="昨晚我做了一个奇怪的梦,梦见自己在太空飞行.", metadata={"page": 6}),
    Document(page_content="我的手机突然关机了,让我有些焦虑。", metadata={"page": 7}),
    Document(page_content="阅读是我每天都会做的事情,我觉得很充实。", metadata={"page": 8}),
    Document(page_content="他们一起计划了一次周末的野餐,希望天气能好。", metadata={"page": 9}),
    Document(page_content="我的狗喜欢追球,看起来非常开心.", metadata={"page": 10}),
]

# 2. 构建BM25稀疏检索器(关键词检索)
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 4  # 每次检索返回4个文档

# 3. 构建FAISS密集检索器(语义检索)
faiss_db = FAISS.from_documents(
    documents,
    embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
faiss_retriever = faiss_db.as_retriever(search_kwargs={"k": 4})

# 4. 构建集成检索器(等权重融合BM25+FAISS)
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, faiss_retriever],
    weights=[0.5, 0.5],
)

# 5. 执行混合检索
docs = ensemble_retriever.invoke("除了猫,你养了什么宠物呢?")
print(docs)
print(len(docs))

高级用法:运行时动态配置检索器 ​

通过configurable_fields为检索器添加可配置参数,实现运行时动态调整检索参数(如返回数量k),无需修改代码。

python
from langchain_core.runnables import ConfigurableField

# 为FAISS检索器添加可配置的搜索参数
faiss_retriever = faiss_db.as_retriever(search_kwargs={"k": 4}).configurable_fields(
    search_kwargs=ConfigurableField(
        id="search_kwargs_faiss",
        name="搜索参数",
        description="要使用的搜索参数",
    )
)
# 重新构建集成检索器
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, faiss_retriever],
    weights=[0.5, 0.5],
)
# 动态配置:FAISS检索器仅返回1个文档
config = {"configurable": {"search_kwargs_faiss": {"k": 1}}}
docs = ensemble_retriever.invoke("苹果", config=config)

七、查询转换阶段优化策略总结 ​

查询转换是RAG优化的核心阶段,主流6种优化策略各有优劣,需根据业务场景选择,核心对比与适用场景如下:

优化策略核心逻辑优点缺点适用场景
多查询重写生成多个相似子问题,并行检索后简单去重实现简单、性能高、轻量模型可实现无权重计算、可能冗余/遗漏高相关文档简单问题、需提升检索覆盖面的场景
多查询融合(RRF)多查询重写+RRF算法排序/去重/TopK兼顾覆盖面和准确性,高相关文档前置略增性能开销,需自定义实现大多数通用场景,替代纯多查询重写
问题分解(迭代/并行)复杂问题拆分为子问题,分别处理后合并答案解决多步骤/高复杂度问题性能低、易信息丢失、LLM要求高逻辑关联性强的复杂问题(如步骤类、数学类)
Step-Back回答回退生成抽象回退问题,扩大检索范围性能高、无信息丢失、轻量模型可实现回退问题生成不佳时检索效果差描述性复杂问题、直接检索相关性低的场景
HyDE混合策略将query转为假设性doc,执行doc-doc对称检索提升语义检索相似度,解决query信息不足问题开放性问题/上下文不足时效果差信息明确的具体问题,语义检索效果不佳的场景
集成检索(稀疏+密集)融合多种检索器(如BM25+FAISS),RRF合并结合不同算法优势,检索效果最优实现稍复杂,需调优各检索器权重生产环境核心场景,对检索准确性要求高的场景

核心选型建议 ​

  1. 通用场景:多查询融合(RRF)/ 集成检索(BM25+FAISS);
  2. 简单问题:纯多查询重写(追求性能);
  3. 复杂描述性问题:Step-Back回答回退;
  4. 复杂逻辑步骤问题:问题分解策略;
  5. 语义检索效果差:HyDE混合策略 + 集成检索。

Talk is cheap, show me the code!