进阶应用 高级

多轮对话 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 多轮对话中用户问"它支持哪些语言?",检索前应先做什么?