系列文章地址
【可能是全網(wǎng)最絲滑的LangChain教程】一、LangChain介紹 - 簡(jiǎn)書 (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】二酗洒、LangChain安裝 - 簡(jiǎn)書 (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】三窗骑、快速入門LLMChain - 簡(jiǎn)書 (jianshu.com)
【可能是全網(wǎng)最絲滑的LangChain教程】四、快速入門Retrieval Chain - 簡(jiǎn)書 (jianshu.com)
使用LangChain構(gòu)建應(yīng)用
LangChain支持構(gòu)建應(yīng)用程序耳鸯,將外部數(shù)據(jù)源和計(jì)算源連接到LLM湿蛔。我們將從一個(gè)簡(jiǎn)單的 LLM 鏈開(kāi)始,它只依賴于提示模板中的信息來(lái)響應(yīng)县爬。 接下來(lái)阳啥,我們將構(gòu)建一個(gè)檢索鏈,該鏈從單獨(dú)的數(shù)據(jù)庫(kù)獲取數(shù)據(jù)并將其傳遞到提示模板中财喳。 然后察迟,我們將添加聊天記錄,以創(chuàng)建對(duì)話檢索鏈耳高。這允許您以聊天方式與此 LLM 進(jìn)行交互扎瓶,因此它會(huì)記住以前的問(wèn)題。 最后泌枪,我們將構(gòu)建一個(gè)代理概荷,利用 LLM 來(lái)確定它是否需要獲取數(shù)據(jù)來(lái)回答問(wèn)題。
Conversation Retrieval Chain
到目前為止碌燕,我們創(chuàng)建的鏈只能回答單個(gè)問(wèn)題误证。人們正在構(gòu)建的LLM應(yīng)用程序的主要類型之一是聊天機(jī)器人继薛。那么,我們?nèi)绾螌⑦@條鏈轉(zhuǎn)化為一條可以回答后續(xù)問(wèn)題的鏈呢愈捅?
我們?nèi)匀豢梢允褂胏reate_retrieval_chain函數(shù)遏考,但我們需要更改兩件事:
- 檢索方法現(xiàn)在不應(yīng)該只針對(duì)最近的輸入,而是應(yīng)該考慮整個(gè)歷史蓝谨。
- 最后的LLM鏈同樣應(yīng)考慮整個(gè)歷史诈皿。
背景
基于百度百科的《讓子彈飛》介紹,設(shè)計(jì)一個(gè)帶歷史對(duì)話的聊天機(jī)器人像棘。
準(zhǔn)備工作
主要是網(wǎng)頁(yè)數(shù)據(jù)的加載稽亏、嵌入模型的初始化、向量數(shù)據(jù)庫(kù)的初始化等缕题,具體代碼如下截歉。
from langchain_community.document_loaders import WebBaseLoader
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
import torch
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 詞嵌入模型
EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
embeddings = HuggingFaceEmbeddings(model_name='D:\models\m3e-base', model_kwargs={'device': EMBEDDING_DEVICE})
# 加載外部文檔
loader = WebBaseLoader("https://baike.baidu.com/item/%E8%AE%A9%E5%AD%90%E5%BC%B9%E9%A3%9E/5358?fr=ge_ala")
docs = loader.load()
# 文檔向量化到FAISS
text_splitter = RecursiveCharacterTextSplitter()
documents = text_splitter.split_documents(docs)
vector = FAISS.from_documents(documents, embeddings)
# 得到一個(gè)檢索器
retriever = vector.as_retriever()
Updating Retrieval
為了更新檢索,我們將創(chuàng)建一個(gè)新的鏈烟零。該鏈將接收最近的輸入(input)和會(huì)話歷史(chat_history)瘪松,并使用LLM生成搜索查詢。
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
("user", "Given the above conversation, generate a search query to look up in order to get information relevant to the conversation")
])
retriever_chain = create_history_aware_retriever(llm, retriever, prompt)
現(xiàn)在我們有了這個(gè)新的檢索器锨阿,我們可以創(chuàng)建一個(gè)新的鏈來(lái)繼續(xù)對(duì)話宵睦,并牢記這些檢索到的文檔。
prompt = ChatPromptTemplate.from_messages([
("system", "Answer the user's questions based on the below context:\n\n{context}"),
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
])
document_chain = create_stuff_documents_chain(llm, prompt)
retrieval_chain = create_retrieval_chain(retriever_chain, document_chain)
測(cè)試一下效果
chat_history = [
HumanMessage(content="《讓子彈飛》什么時(shí)候在大陸上映的墅诡?"),
AIMessage(content="《讓子彈飛》的大陸上映時(shí)間是2010年12月16日壳嚎。")
]
retrieval_chain.invoke({
"chat_history": chat_history,
"input": "那韓國(guó)上映時(shí)間呢?"
})
最終輸出
《讓子彈飛》在2011年10月8日在韓國(guó)上映末早。
以上就是一個(gè)簡(jiǎn)單的烟馅,帶歷史記錄的聊天機(jī)器人。整個(gè)的流程大概是:檢索文檔找到關(guān)聯(lián)的文檔(一條或者多條)然磷,基于文檔和歷史對(duì)話進(jìn)行問(wèn)答郑趁,得到問(wèn)題的答案。如果如果還要繼續(xù)進(jìn)行問(wèn)答姿搜,往chat_history中添加新的問(wèn)答對(duì)即可寡润。
Peace Guys~