从ConversationalRetrievalChain迁移到LCEL:代码实现与优势解析

技术背景介绍

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—

<think>好的,用户遇到了ConversationalRetrievalChain未被访问的错误,需要解决这个问题。首先,我得回想一下ConversationalRetrievalChain在LangChain中的作用。它通常用于结合聊天历史和检索到的文档生成回答,所以如果出现未被访问的问题,可能涉及到组件配置或流程中的断点。 根据用户提供的引用[4],里面提到迁移LCEL(LangChain Expression Language)可以带来更清晰的内部结构,支持返回源文档和流式操作。这可能意味着原来的ConversationalRetrievalChain在配置时某些步骤没有正确连接,比如问题重述、检索器或文档处理链。 首先检查是否正确初始化了历史感知检索器。如果聊天历史没有被正确处理,检索器可能无法生成正确的查询,导致无法访问所需数据。需要确认是否在链中正确集成了历史记录,比如使用ChatMessageHistory类来管理对话上下文。 然后,确保检索器部分配置正确。引用[3]提到嵌入搜索能提高准确性,可能需要检查向量存储的索引是否构建正确,以及检索器是否使用了合适的嵌入模型。如果代码库未正确索引,检索器可能无法找到相关文档,导致链中断。 接下来,查看文档处理链是否正常。引用[1]中的文本分割器设置可能影响文档的拆分,如果分割不当,检索到的文档块可能不符合预期,进而影响后续处理。需要确认分割符和块大小是否适合当前数据,比如中文使用“。”而英文使用“.”。 另外,引用[4]还提到LCEL的组件包括历史感知检索器、检索链和文档组合链。如果用户还在使用旧版的ConversationalRetrievalChain,可能需要迁移LCEL结构,确保每个组件正确连接。例如,检查是否调用了正确的链组合方法,如RunnablePassthrough来传递参数。 最后,日志和错误信息是关键。启用调试模式或添加打印语句,查看哪一步骤未执行,比如问题重述是否生成正确的查询,检索器是否返回结果,文档处理是否正常。如果某一步返回空,可能需要调整参数或检查数据源。 总结可能的问题点:历史处理不正确、检索器配置错误、文档分割不当、链组件连接缺失。需要逐一排查这些部分,参考引用中的建议,如使用合适的文本分割器,确保索引构建正确,迁移LCEL结构以增强流程控制。</think>针对ConversationalRetrievalChain未被访问的错误,以下是分步解决方案: ### 1. 验证历史感知检索器配置 检查是否在链中正确集成了聊天历史处理模块。需使用`ChatMessageHistory`类管理对话上下文: ```python from langchain.memory import ChatMessageHistory history = ChatMessageHistory() history.add_user_message("你的问题") ``` ### 2. 重构为LCEL架构(关键步骤) 根据引用[4],迁移LCEL结构可解决隐藏的流程中断问题: ```python from langchain.schema.runnable import RunnablePassthrough retriever_chain = ( RunnablePassthrough.assign(history=lambda x: x["history"]) | question_rewriter | retriever # 确保retriever已连接向量库[^3] ) ``` ### 3. 检查嵌入搜索配置 确认已启用嵌入搜索并正确构建索引(引用[3]): ```python from langchain.vectorstores import FAISS vectorstore = FAISS.from_documents(docs, embeddings) retriever = vectorstore.as_retriever() ``` ### 4. 优化文本分割策略 根据引用[1],文档预处理需适配语言特性: ```python text_splitter = CharacterTextSplitter( chunk_size=400, separator="。", # 中文文档使用句号分割 chunk_overlap=20 ) ``` ### 5. 验证链式调用完整性 确保完整包含三大核心组件(引用[4]): ```python full_chain = { "docs": retriever_chain, "question": lambda x: x["question"] } | document_combiner | answer_generator ``` ### 常见错误排查表 | 现象 | 检查点 | 解决方案 | |------|--------|----------| | 无历史记录 | 内存模块初始化 | 添加`ConversationBufferMemory` | | 检索结果为空 | 向量库索引状态 | 重新执行`vectorstore.add_documents()` | | 文档解析错误 | 文本分割符设置 | 切换中英文分隔符[^1] |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值