LangChain RAG(检索增强生成)文档(二) 啥叫独立问题?
https://python.langchain.com.cn/docs/expression_language/cookbook/retrieval
要搞懂“跟进问题+对话历史改写为独立问题”,核心是解决“指代词(他/她/它、这个/那个)没上下文,检索器看不懂”的问题。我用文档里的具体案例拆解,不绕抽象概念,每一步都对应实际场景和代码逻辑。
一、先明确3个关键概念(用文档案例定义)
先把“对话历史、跟进问题、独立问题”这三个词和具体例子绑定,你就能直观理解:
| 概念 | 定义(文档案例) | 例子(文档中的对话) |
|---|---|---|
| 对话历史 | 之前的交互记录,包含用户和AI的对话内容,是理解“指代词”的关键上下文。 | 「用户:Who wrote this notebook?(谁写的这个笔记本?);AI:Harrison(哈里森)」 |
| 跟进问题 | 用户在对话历史后提出的新问题,通常包含指代词(他/她/它、这个/那个),依赖历史才能理解。 | 「where did he work?(他在哪工作?)」——“he”(他)依赖历史才知道是指Harrison |
| 独立问题 | 把“跟进问题+对话历史”合并改写后,去掉指代词、补充完整上下文的问题,不依赖历史也能看懂。 | 「where did Harrison work?(哈里森在哪工作?)」——无指代词,单独看也明确 |
二、为什么基础RAG处理不了“跟进问题”?(核心矛盾)
基础RAG的检索逻辑是:用用户当前的问题直接去向量库匹配相似文本。但“跟进问题”里的指代词(如“he”)没有上下文,向量库看不懂“他”指谁,会导致检索失效——
比如基础RAG遇到“where did he work?”:
- 检索器拿到问题“where did he work?”,生成对应的向量;
- 向量库中只有“harrison worked at kensho”这条文本,其向量与“he”(无明确主体)的向量语义不匹配;
- 检索器找不到相关上下文,LLM就无法生成准确回答。
这就是必须改写的原因:跟进问题有“歧义”,必须用对话历史补充信息,把歧义消除,变成无歧义的独立问题,检索器才能正确匹配上下文。
三、改写的具体过程(对应文档代码,分3步)
文档里是通过“提示模板+LLM”实现改写的,核心是让LLM读“对话历史+跟进问题”,自动把指代词替换成明确的主体。具体步骤对应文档中的代码逻辑:
步骤1:准备“改写提示模板”(CONDENSE_QUESTION_PROMPT)
先定义一个提示,明确告诉LLM“你的任务是把‘对话历史+跟进问题’改成独立问题”,模板内容来自文档:
_template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language.
Chat History:
{chat_history} # 这里填对话历史
Follow Up Input: {question} # 这里填跟进问题
Standalone question:"""
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template)
这个模板的作用是给LLM“发指令”,让它按规则改写。
步骤2:把“对话历史+跟进问题”传入模板
将实际的对话历史和跟进问题填充到模板中,形成LLM能理解的完整指令。比如文档中的案例:
- 对话历史(chat_history):
"Human: Who wrote this notebook?\nAI: Harrison"(用get_buffer_string函数把Message列表转成这种字符串格式) - 跟进问题(question):
"where did he work?"
填充后,传给LLM的指令是:
Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language.
Chat History:
Human: Who wrote this notebook?
AI: Harrison
Follow Up Input: where did he work?
Standalone question:
步骤3:LLM生成独立问题
用ChatOpenAI(文档中用的是这个模型)处理上述指令,LLM会自动识别“he”指的是对话历史中的“Harrison”,生成独立问题:
where did Harrison work?
到这里,“跟进问题+对话历史”就成功改写成了“独立问题”——没有指代词,单独看也明确,检索器能直接用它去向量库匹配。
四、改写后的作用(回到RAG流程)
改写完成后,整个对话式RAG的流程就通了,对比基础RAG的区别:
| 流程环节 | 基础RAG(无改写) | 对话式RAG(有改写) |
|---|---|---|
| 1. 接收用户输入 | 直接拿“where did he work?”去检索 | 先把“对话历史+where did he work?”改写成“where did Harrison work?” |
| 2. 检索上下文 | 因“he”无明确主体,检索不到“harrison worked at kensho” | 用“where did Harrison work?”检索,成功匹配到相关文本 |
| 3. LLM生成回答 | 无上下文,无法准确回答 | 基于检索到的“harrison worked at kensho”,回答“Harrison worked at Kensho.” |
五、总结:改写的核心目的
一句话说清:给跟进问题中的“指代词”(他/她/它、这个/那个)补充对话历史中的“明确主体”,消除歧义,让检索器能看懂问题,从而找到正确的上下文。
比如:
- “他” → 补充历史后明确是“Harrison”;
- “这个工具” → 补充历史后明确是“LangChain”;
- “那个问题的答案” → 补充历史后明确是“‘2+2等于几’的答案”。
没有这一步,对话式RAG就无法处理日常对话中常见的“指代性提问”,只能像基础RAG一样,要求用户每次都问“无歧义的完整问题”,体验很差。
更多推荐

所有评论(0)