ConversationalRetrievalChain是一种结合检索增强生成和聊天历史的解决方案,允许用户与文档进行“对话”。虽然它提供了一体化的体验,但其内部实现复杂,给定的问答过程隐藏了问题重新表述的步骤。在这篇文章中,我们将探讨迁移到LCEL(LangChain Enhanced Language)实现的优势,以及如何进行代码迁移。
LCEL通过清晰地拆分和管理各个组件,提供了可运行的方法,如流式处理和异步操作。它包含两个主要步骤:利用上下文转化为独立问题的检索过程,以及进行内容组合以生成最终回答。这种方式提供了更多的灵活性和可配置性,特别是在处理源文档返回时。
以下是从ConversationalRetrievalChain迁移到LCEL的代码实现。我们将展示如何加载文档,进行文本分割,存储向量,配置LLM,以及最终的问答过程。
%pip install --upgrade --quiet langchain-community langchain langchain-openai faiss-cpu
import os
from getpass import getpass
os.environ["OPENAI_API_KEY"] = getpass()
# 加载文档
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data = loader.load()
# 文本分割
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
all_splits = text_splitter.split_documents(data)
# 存储分割结果
vectorstore = FAISS.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())
from langchain.chains import ConversationalRetrievalChain
from langchain_core.prompts import ChatPromptTemplate
condense_question_template = """
Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.
Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""
condense_question_prompt = ChatPromptTemplate.from_template(condense_question_template)
qa_template = """
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer
the question. If you don't know the answer, say that you
don't know. Use three sentences maximum and keep the
answer concise.
Chat History:
{chat_history}
Other context:
{context}
Question: {question}
"""
qa_prompt = ChatPromptTemplate.from_template(qa_template)
convo_qa_chain = ConversationalRetrievalChain.from_llm(
llm,
vectorstore.as_retriever(),
condense_question_prompt=condense_question_prompt,
combine_docs_chain_kwargs={
"prompt": qa_prompt,
},
)
convo_qa_chain(
{
"question": "What are autonomous agents?",
"chat_history": "",
}
)
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
condense_question_system_template = (
"Given a chat history and the latest user question "
"which might reference context in the chat history, "
"formulate a standalone question which can be understood "
"without the chat history. Do NOT answer the question, "
"just reformulate it if needed and otherwise return it as is."
)
condense_question_prompt = ChatPromptTemplate.from_messages(
[
("system", condense_question_system_template),
("placeholder", "{chat_history}"),
("human", "{input}"),
]
)
history_aware_retriever = create_history_aware_retriever(
llm, vectorstore.as_retriever(), condense_question_prompt
)
system_prompt = (
"You are an assistant for question-answering tasks. "
"Use the following pieces of retrieved context to answer "
"the question. If you don't know the answer, say that you "
"don't know. Use three sentences maximum and keep the "
"answer concise."
"\n\n"
"{context}"
)
qa_prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
("placeholder", "{chat_history}"),
("human", "{input}"),
]
)
qa_chain = create_stuff_documents_chain(llm, qa_prompt)
convo_qa_chain = create_retrieval_chain(history_aware_retriever, qa_chain)
convo_qa_chain.invoke(
{
"input": "What are autonomous agents?",
"chat_history": [],
}
)
LCEL能够更灵活地实现复杂的问答任务,适用于需要异步处理或流式响应的应用场景。此外,它能更直接地返回源文档,这对于需要追溯问答来源的行业(如法律、金融)尤其重要。
如果遇到问题欢迎在评论区交流。
—END—