多轮对话 RAG:会话化检索
解决"它呢?""第二个呢?"这类指代问题:把对话历史融入检索查询(Conversational RAG)。
单轮 RAG 的盲区
用户先问"LangChain 是什么",再问"它支持哪些语言?" —— 直接拿"它支持哪些语言"去检索什么都检不到。指代消解是多轮 RAG 的核心难题。
方案:查询重写(问题凝练)
先用模型把"历史 + 新问题"改写成独立完整的问题,再检索:
condense_prompt = ChatPromptTemplate.from_messages([
("system", "根据对话历史,把用户最新问题改写成一个不依赖上下文、可独立理解的搜索问题。"),
("human", "历史:{history}\n最新问题:{question}"),
])
condense_chain = condense_prompt | llm | StrOutputParser()
standalone_q = condense_chain.invoke({"history": hist_text, "question": q})
# "它支持哪些语言?" → "LangChain 支持哪些编程语言?"
docs = retriever.invoke(standalone_q)
完整链路
新问题 → 查询凝练(带历史) → 检索 → 生成(带历史+资料) → 回答
↑ 只改写检索词,不改用户原话
要点:最终提示词里仍保留对话历史(回答口吻连贯),但检索用凝练后的问题。
工程细节
- 历史裁剪:只带最近 3~5 轮,避免 token 爆炸;
- 凝练失败的兜底:改写结果为空时直接用原始问题检索;
- 会话隔离:以 session_id 存取历史,多用户互不干扰。
📝 课后练习
quiz-1 多轮对话中用户问"它支持哪些语言?",检索前应先做什么?