在 LangChain 生态中,完成检索步骤后,我们需要将相关的文档片段整合并传给大语言模型(LLM)。本文将深入解析 RetrievalQA 链的用法,并对比四种不同的文档处理策略。
1. 核心流程:从检索到回答
- 输入查询:用户提出问题。
- 检索:从向量库中寻找相关文档。
- 传递:将问题和文档作为提示(Prompt)发送给 LLM。
- 生成:LLM 根据提供的上下文回答问题。
2. 四种文档整合策略 (chain_type)
当检索到的文档总量超过模型上下文窗口限制时,需采用特定策略:
A. Stuffing (填充)
- 方法:将所有检索到的文档直接拼接在一起,一次性塞进 Prompt。
- 优点:简单,只需调用一次 LLM,模型能同时看到所有上下文。
- 缺点:文档过多时会超出 Token 限制。
B. Map Reduce (映射-规约)
- 方法:先让 LLM 分别对每个文档块进行总结/提取(Map),最后将所有总结汇总再生成最终答案(Reduce)。
- 优点:可以处理海量文档,不受上下文限制。
- 缺点:调用 LLM 次数多,耗时长,且可能丢失文档间的关联信息。
C. Refine (精炼)
- 方法:先根据第一个文档生成初步答案,然后将该答案和第二个文档发给 LLM 进行更新,以此类推。
- 优点:允许模型逐步迭代,答案通常较精确。
- 缺点:必须按顺序执行,速度较慢。
D. Map Re-rank (重排序)
- 方法:对每个文档块分别提问,并要求 LLM 给出一个分数,最后选择分数最高的答案。
- 优点:适用于需要从特定文档中找到唯一确切答案的场景。
3. 代码实现示例 (Python)
以下是在 LangChain 中使用 RetrievalQA 链的典型代码:
from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. 准备数据源
embeddings = OpenAIEmbeddings()
persist_directory = 'docs/chroma/'
vectordb = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
# 2. 初始化 LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# 3. 自定义 Prompt 模板 (可选)
template = """使用以下上下文片段来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。 最多使用三句话。保持回答简明扼要。
{context}
问题: {question}
有用的回答:"""
QA_CHAIN_PROMPT = PromptTemplate.from_template(template)
# 4. 创建问答链
qa_chain = RetrievalQA.from_chain_type(
llm,
retriever=vectordb.as_retriever(),
return_source_documents=True, # 允许查看是哪些文档提供了答案
chain_type="stuff", # 指定整合策略:stuff,map_reduce, refine, map_rerank
chain_type_kwargs={"prompt": QA_CHAIN_PROMPT} )
# 5. 执行提问
question = "这门课关于线性代数的前提要求是什么?"
result = qa_chain({"query": question})
print(result["result"])
# 查看来源文档
print(result["source_documents"])
4. 关键点总结
- 上下文注入:问答的核心本质是将原本 LLM 不知道的实时/私有数据通过提示词(Prompt Injection)的方式注入给它。
- 幻觉控制:在 Prompt 模板中明确告知模型“如果不知道就说不知道”,是减少 AI 幻觉的有效手段。
- 策略选择:
- 大多数情况下优先使用 Stuff(简单高效)。
- 如果文档极长且需要全面总结,考虑 Map Reduce。
- 如果需要极高的答案质量且不计成本,考虑 Refine。
提示:通过 langchain.debug = True 可以实时观察文档如何拼接到 Prompt 中,这是 本课程 强调的实用调试技巧。
|