目錄
- 引言
- 一、LangChain實現RAG多輪問答核心機制
- 1. 對話歷史管理(Memory)
- 2. 問題重寫(Query Rewriting)
- 3. 檢索增強生成(RAG Core)
- 4. 鏈式工作流(Chain)
- 二、關鍵設計特點
- 三、完整示例代碼
- 四、優化建議
引言
小馬之所以寫今天這篇文章是因為在上一篇文章《多輪問答與指代消解》中的第一章節有提到“LangChain是怎么實現的多輪問答”,但很顯然沒有提到它在RAG下是怎么實現的多輪問答,畢竟這兩者還是有區別的。前一篇文章剛好提到自己實現RAG下的多輪問答要怎么實現,唯獨沒有提到LangChain在RAG下是怎么實現的多輪。
前篇文章中提到的MultiQueryRetriever
小馬已經在文中聲明,這個并不是為了解決RAG多輪的機制。
慶幸的是,我們回過頭來看LangChain實現RAG多輪問答的方式其實和我們自己實現的思路不能說一模一樣,簡直是毫無區別。只是LangChain封裝了組件,我們直接調用就能實現了,省去了自己寫工程代碼來實現的過程。接下來我們有必要一起看下的,不過看過上一篇的同學其實看這篇文章就很好理解了。
一、LangChain實現RAG多輪問答核心機制
在 LangChain 中實現 RAG(Retrieval-Augmented Generation)的多輪問答主要通過以下核心機制完成,確保對話歷史被有效利用:
1. 對話歷史管理(Memory)
LangChain 使用 Memory 組件 存儲和傳遞上下文:
ConversationBufferMemory
存儲完整的原始對話歷史(用戶輸入 + AI 響應)。ConversationSummaryMemory
用 LLM 壓縮歷史為摘要,避免 token 超限。ConversationBufferWindowMemory
僅保留最近 K 輪對話,控制上下文長度。
from langchain.memory import ConversationBufferMemorymemory = ConversationBufferMemory(memory_key="chat_history", # 存儲對話的鍵名return_messages=True # 返回消息列表而非字符串
)
2. 問題重寫(Query Rewriting)
將當前問題結合歷史重寫為獨立查詢,使檢索更準確:
ConversationalRetrievalChain
內置condense_question
步驟:- 輸入:當前問題
+
對話歷史 - 輸出:重寫后的獨立問題(如將代詞替換為具體實體)
- 輸入:當前問題
from langchain.chains import ConversationalRetrievalChainqa_chain = ConversationalRetrievalChain.from_llm(llm=chat_model, # 如 ChatOpenAIretriever=vectorstore.as_retriever(), # 向量檢索器memory=memory,condense_question_llm=condense_model # 可選:專用重寫模型
)
3. 檢索增強生成(RAG Core)
多輪流程如下:
- 檢索上下文
用重寫后的查詢從向量庫檢索相關文檔片段。 - 組裝提示
將以下內容組合為最終提示:- 檢索到的上下文
- 對話歷史(原始或摘要)
- 當前問題
- 生成答案
LLM 基于完整上下文生成連貫回復。
提示詞示例:
[檢索到的文檔]
對話歷史:用戶: 量子計算是什么?AI: 量子計算利用量子比特...
當前問題:它有什么優勢?
最終答案:量子計算的優勢包括...
4. 鏈式工作流(Chain)
ConversationalRetrievalChain
自動處理整個流程:
# 第一輪
result = qa_chain({"question": "什么是RAG?"})
print(result["answer"])# 第二輪(自動攜帶歷史)
result = qa_chain({"question": "它如何解決幻覺問題?"}) # "它"指代RAG
print(result["answer"])
二、關鍵設計特點
組件 | 作用 |
---|---|
Memory | 持久化歷史對話,支持多種存儲策略(原始/摘要/滑動窗口) |
Query Rewriting | 解決指代消解(如“它”指代上文的 RAG),提升檢索準確性 |
Contextual Prompt | 動態組裝提示,包含歷史 + 檢索結果 + 當前問題 |
檢索器優化 | 支持自定義檢索器(如過濾元數據、調整相似度閾值) |
三、完整示例代碼
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain# 初始化向量庫和模型
vectorstore = FAISS.load_local("rag_vector_db", OpenAIEmbeddings())
llm = ChatOpenAI(model="gpt-4-turbo")
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)# 構建鏈
qa_chain = ConversationalRetrievalChain.from_llm(llm=llm,retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),memory=memory
)# 模擬多輪對話
questions = ["LangChain是什么?","它的核心組件有哪些?", # "它" 指代LangChain"如何用RAG實現多輪對話?"
]
for q in questions:result = qa_chain.invoke({"question": q})print(f"問題: {q}\n答案: {result['answer']}\n")
四、優化建議
- 歷史摘要:長對話時使用
ConversationSummaryMemory
避免 token 超限。 - 重寫模型:用輕量級模型(如 GPT-3.5)專門處理問題重寫,降低成本。
- 檢索過濾:基于元數據(如對話 ID)過濾無關文檔。
- 提示工程:定制提示模板明確區分歷史/當前問題/檢索內容。
通過組合 Memory 管理、智能查詢重寫和 上下文感知的 RAG 流程,LangChain 有效實現了多輪問答的連貫性與準確性。
本篇文章旨在補充上一篇文章的內容,建議結合上一篇文章食用,口感效果更加。