[如何从ConversationalRetrievalChain迁移到LCEL:提升你的AI问答系统的透明度与扩展性!]

78 阅读3分钟

引言

ConversationalRetrievalChain 提供了一种将检索增强生成(RAG)与聊天历史结合在一起的便捷方式,使得“与文档聊天”成为可能。然而,随着需求的增长和复杂度的增加,从该实现迁移到Langchain-Community的扩展库(LCEL)可以为您带来更高的灵活性和透明度。本篇文章将详细介绍如何进行此类迁移,以及在迁移过程中可能面临的挑战和解决方案。

主要内容

1. ConversationalRetrievalChain的局限性

  • 隐藏的内部步骤: ConversationalRetrievalChain 自动处理了大量后台步骤,如问题的复述和上下文的重建,这对于开发者来说极少的修改空间。
  • 难以单独返回源文档: 对于更复杂的应用场景,需要更灵活的方式来管理和返回多个源文档。

2. LCEL的优势

  • 透明的内部机制: LCEL 清晰地展示了每一个处理步骤,开发者可以更细致地控制和优化。
  • 支持流处理和异步操作: LCEL 支持更多现代化的编程范式,使得在处理大规模数据时更加高效。

代码示例

以下是从ConversationalRetrievalChain迁移到LCEL的代码示例:

import os
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
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
from langchain_core.prompts import ChatPromptTemplate

# Configurations
os.environ["OPENAI_API_KEY"] = "sk-your-openai-key"  # 保留您自己的API密钥,确保安全性

# Load documents
loader = WebBaseLoader("https://lilianweng.github.io/posts/2023-06-23-agent/")
data = loader.load()

# Split the data into chunks for processing
chunk_size = 500
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=0)
all_splits = text_splitter.split_documents(data)

# Store the chunks in a vector store
vectorstore = FAISS.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())

# Create an instance of the language model
llm = ChatOpenAI()

# Define prompts
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}"),
    ]
)

# Create the QA chain
qa_chain = create_stuff_documents_chain(llm, qa_prompt)

# Create the conversational QA chain
convo_qa_chain = create_retrieval_chain(history_aware_retriever, qa_chain)

# Invoke the chain
result = convo_qa_chain.invoke({
    "input": "What are autonomous agents?",
    "chat_history": [],
})
print(result['answer'])

# 使用API代理服务提高访问稳定性

常见问题和解决方案

问题1:迁移过程中如何保持现有功能稳定可靠?

解决方案:逐步替换原有模块为LCEL中的API,确保每个步骤的输出与原有实现一致后,再进行整体替换。

问题2:如何优化LCEL的异步操作?

解决方案:使用Python的asyncio库,结合LCEL的异步API,确保在高并发情况下的高效操作。

总结和进一步学习资源

迁移到LCEL不仅可以提高系统的灵活性和可扩展性,还能使开发者更好地理解和控制数据流。希望通过这篇文章,您可以顺利完成从ConversationalRetrievalChain到LCEL的迁移。

进一步学习资源

参考资料

  1. Langchain GitHub Repository
  2. OpenAI API Documentation

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

---END---