技术背景介绍
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())
设置LLM和问答流程
Legacy方法
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": "",
}
)
LCEL方法
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能够更灵活地实现复杂的问答任务,适用于需要异步处理或流式响应的应用场景。此外,它能更直接地返回源文档,这对于需要追溯问答来源的行业(如法律、金融)尤其重要。
实践建议
- 首先确保所有的向量存储和LLM配置正确,以避免不必要的调试过程。
- 利用LCEL的灵活性,定制代码以满足特定的业务需求。
- 定期检查和更新文档加载器和存储插件,以确保数据新鲜和高效。
如果遇到问题欢迎在评论区交流。
—END—