【LangChain】chain = load_qa_chain(llm, chain_type=‘stuff‘) 迁移到 LCEL 链

如果代码中出现 LangChainDeprecationWarning,提示 load_qa_chain 函数及其相关类(如 StuffDocumentsChain)已废弃,推荐根据 chain_type(如 stuffmap_reduce 等)迁移到新的实现。这是 LangChain 0.2.x 及以上版本模块重构的一部分,旨在用更灵活的 LCEL(LangChain Expression Language)替代旧的链类。

本回答基于 LangChain 0.3.x,详细解释警告原因、load_qa_chain 的作用、迁移方法,并提供一个独立示例,展示如何将 load_qa_chain(chain_type='stuff') 迁移到 LCEL 实现,构建一个简单的 RAG 系统。示例将包含文本加载、分割、嵌入生成、Milvus 向量存储和查询。

代码废弃告警:
LangChainDeprecationWarning: This class is deprecated. See the following migration guides for replacements based on chain_type:
stuff: https://python.langchain.com/docs/versions/migrating_chains/stuff_docs_chain
map_reduce: https://python.langchain.com/docs/versions/migrating_chains/map_reduce_chain
refine: https://python.langchain.com/docs/versions/migrating_chains/refine_chain
map_rerank: https://python.langchain.com/docs/versions/migrating_chains/map_rerank_docs_chain
See also guides on retrieval and question-answering here: https://python.langchain.com/docs/how_to/#qa-with-rag
chain = load_qa_chain(llm, chain_type=‘stuff’) ……


警告原因

LangChainDeprecationWarning 表明你使用了已废弃的 load_qa_chain 函数:

from langchain.chains import load_qa_chain
chain = load_qa_chain(llm, chain_type='stuff')  # 废弃

在 LangChain 0.2.x 及以上版本中,load_qa_chain 及其相关链类(如 StuffDocumentsChainMapReduceDocumentsChain)被标记为废弃,推荐使用 LCEL 或其他现代组件构建问答链。

原因

  1. 模块重构:LangChain 鼓励使用 LCEL 替代旧的链类,LCEL 更模块化、可组合,支持复杂逻辑。
  2. 统一接口:LCEL 提供标准化的 Runnable 接口,便于调试和扩展。
  3. 性能优化:LCEL 支持异步、流式处理,优于旧链类的固定模式。
  4. 向后兼容:旧函数暂时有效,但会触发警告,未来版本可能移除。

废弃的 chain_type

  • stuff:将所有文档塞入上下文,适合短文档。
  • map_reduce:分步处理文档,适合长文档。
  • refine:逐个精炼答案,适合需要逐步改进的场景。
  • map_rerank:重新排序文档,适合高精度检索。

load_qa_chain 简介

load_qa_chain 是 LangChain 的一个工厂函数,用于创建基于文档的问答链,根据 chain_type 选择不同的处理逻辑:

  • stuff:将所有文档内容拼接为单一上下文,输入 LLM 回答。
  • map_reduce:对每个文档单独调用 LLM,汇总结果。
  • refine:迭代处理文档,逐步精炼答案。
  • map_rerank:对文档评分并重新排序,选择最佳文档回答。

典型用法(已废弃):

from langchain.chains import load_qa_chain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4")
chain = load_qa_chain(llm, chain_type="stuff")
answer = chain.run(input_documents=docs, question="问题")

问题

  • stuff 模式对长文档可能超过 LLM 上下文限制。
  • 旧链类缺乏灵活性,难以自定义提示或处理复杂逻辑。
  • 不支持现代功能(如异步、流式)。

迁移方法

根据警告,load_qa_chain(chain_type='stuff') 需迁移到 LCEL 实现。以下是针对 stuff 模式的迁移步骤,参考官方迁移指南(Stuff Documents Chain):

  1. 替换 load_qa_chain

    • 使用 LCEL 组合 ChatPromptTemplateLLMStrOutputParser,手动实现 stuff 逻辑。
    • 将文档和问题格式化为提示,传递给 LLM。
  2. 保留 stuff 逻辑

    • stuff 模式将所有文档拼接为上下文,LCEL 通过 ChatPromptTemplate 实现相同效果。
    • 使用 {context} 占位符包含文档内容,{question} 包含用户问题。
  3. 集成检索器

    • 结合向量存储(如 langchain_milvus.Milvus)的检索器,获取相关文档。
    • 使用 as_retriever 方法生成文档。
  4. 更新依赖

    • 确保使用最新版本:
      pip install --upgrade langchain langchain-openai langchain-milvus pymilvus
      
  5. 自动迁移(可选)

    • 使用 LangChain CLI 检查代码:
      pip install langchain-cli
      langchain migrate
      
    • CLI 可能无法完全替换 load_qa_chain,需手动实现 LCEL。

LCEL 迁移示例:替换 load_qa_chain(chain_type='stuff')

以下是一个独立的 RAG 示例,展示如何将 load_qa_chain(chain_type='stuff') 迁移到 LCEL,使用 langchain_milvus.Milvus 作为向量存储,加载文档、分割、嵌入并回答问题。

准备文件
创建 qa_knowledge.txt

云计算是一种通过互联网提供计算资源(如服务器、存储)的技术。
虚拟化是云计算的基础,允许在单一物理服务器上运行多个虚拟机。
容器化是另一种技术,使用 Docker 等工具提供轻量级隔离。

代码

import os
os.environ["OPENAI_API_KEY"] = "Your OpenAI API Key"

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_milvus import Milvus
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader

# 加载文档
loader = TextLoader(file_path="qa_knowledge.txt", encoding="utf-8")
documents = loader.load()

# 分割文档
splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,
    chunk_overlap=20,
    separators=["\n\n", "\n", " ", ""]
)
split_documents = splitter.split_documents(documents)

# 创建 Milvus 向量存储
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Milvus.from_documents(
    documents=split_documents,
    embedding=embeddings,
    connection_args={"uri": "http://localhost:19530"},
    collection_name="cloud_knowledge",
    drop_old=True
)

# 初始化 LLM
llm = ChatOpenAI(temperature=0, model="gpt-4")

# 提示模板(模拟 stuff 模式)
prompt = ChatPromptTemplate.from_template(
    """根据以下上下文回答问题:
上下文:{context}
问题:{question}
回答:"""
)

# 格式化文档函数
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# 创建 LCEL 链(替换 load_qa_chain chain_type='stuff')
rag_chain = (
    {
        "context": vectorstore.as_retriever(search_kwargs={"k": 2}) | format_docs,
        "question": RunnablePassthrough()
    }
    | prompt
    | llm
    | StrOutputParser()
)

# 调用链
print("RAG 链输出:")
response = rag_chain.invoke("什么是云计算?")
print(response)
response = rag_chain.invoke("它依赖哪些技术?")
print(response)

输出示例

RAG 链输出:
云计算是一种通过互联网提供计算资源(如服务器、存储)的技术。
云计算依赖技术,如虚拟化和容器化。
代码说明
  1. 文档加载与分割
    • TextLoader 加载 qa_knowledge.txt
    • RecursiveCharacterTextSplitter 分割为 100 字符块,chunk_overlap=20
  2. 向量存储
    • Milvus.from_documents 创建 cloud_knowledge 集合,存储嵌入。
  3. LCEL 链(替换 stuff
    • vectorstore.as_retriever 检索 2 个相关文档。
    • format_docs 拼接文档内容,模拟 stuff 模式。
    • prompt 包含 {context}(拼接的文档)和 {question}
    • llmgpt-4)生成答案,StrOutputParser 提取文本。
  4. 独立性
    • 示例聚焦云计算主题,独立于之前上下文。

运行要求

  • Milvus 服务运行(localhost:19530)。
  • qa_knowledge.txt 存在,编码为 utf-8
  • OpenAI API 密钥有效。
迁移要点
  • 旧代码(废弃):
    from langchain.chains import load_qa_chain
    chain = load_qa_chain(llm, chain_type="stuff")
    answer = chain.run(input_documents=docs, question="问题")
    
  • 新代码(LCEL):
    • 使用 ChatPromptTemplate 定义提示。
    • format_docs 拼接文档,替代 stuff 的文档合并。
    • LCEL 链通过 | 组合组件,替代 run 方法。
  • 优势
    • LCEL 更灵活,可自定义提示、异步支持。
    • 更容易调试(打印中间结果)。

注意事项

  1. API 密钥
    • 使用 .env 文件:
      from dotenv import load_dotenv
      load_dotenv()
      
    • 确保密钥支持 text-embedding-3-smallgpt-4
  2. 依赖
    • 安装:
      pip install --upgrade langchain langchain-openai langchain-milvus pymilvus
      
  3. Milvus 配置
    • 验证服务:
      docker ps  # 检查 Milvus 容器
      
    • 非本地部署:
      connection_args=connection_args={"uri": "http://your-milvus-host:your-port"}
      
  4. 性能优化
    • 调整 chunk_size(500-1000)、search_kwargs={"k": 3}
    • 限制文档长度,避免超过 LLM 上下文:
      def format_docs(docs):
          return "\n\n".join(doc.page_content[:200] for doc in docs)  # 截断每文档
      
  5. 错误调试
    • 检查 Milvus 连接:from pymilvus import connections; connections.connect(uri="http://localhost:19530")
    • 设置 langchain.debug = True 查看 LCEL 链日志。

常见问题

Q1:如何迁移其他 chain_type(如 map_reduce)?
A:参考官方指南:

  • map_reduce:使用 LCEL 的 MapReduceDocumentsChain 或自定义 map-reduce 逻辑(指南)。
  • refine:使用 RefineDocumentsChain 或 LCEL 迭代处理(指南)。
  • map_rerank:使用 MapRerankDocumentsChain 或 LCEL 排序(指南)。

Q2:LCEL 比 load_qa_chain 有何优势?
A:LCEL 更模块化,支持异步、流式、自定义提示,且调试更透明。

Q3:如何处理长文档?
A:增加 chunk_size(如 1000),或截断文档:

def format_docs(docs):
    return "\n\n".join(doc.page_content[:500] for doc in docs)

Q4:可以添加对话历史吗?
A:使用 RunnableWithMessageHistory

from langchain_core.runnables import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory

总结

LangChainDeprecationWarning 提示 load_qa_chain(chain_type='stuff') 已废弃,需迁移到 LCEL。LCEL 通过 ChatPromptTemplateformat_docsRunnable 组合实现 stuff 模式,替代旧链。示例代码展示了使用 TextLoaderRecursiveCharacterTextSplitterlangchain_milvus.Milvus 和 LCEL 构建 RAG 系统,处理云计算主题查询。

变分模态分解(Variational Mode Decomposition, VMD)是一种强大的非线性、无参数信号处理技术,专门用于复杂非平稳信号的分析与分解。它由Eckart Dietz和Herbert Krim于2011年提出,主要针对传统傅立叶变换在处理非平稳信号时的不足。VMD的核心思想是将复杂信号分解为一系列模态函数(即固有模态函数,IMFs),每个IMF具有独特的频率成分和局部特性。这一过程与小波分析或经验模态分解(EMD)类似,但VMD通过变分优化框架显著提升了分解的稳定性和准确性。 在MATLAB环境中实现VMD,可以帮助我们更好地理解和应用这一技术。其核心算法主要包括以下步骤:首先进行初始化,设定模态数并为每个模态分配初始频率估计;接着采用交替最小二乘法,通过交替最小化残差平方和以及模态频率的离散时间傅立叶变换(DTFT)约束,更新每个模态函数和中心频率;最后通过迭代优化,在每次迭代中优化所有IMF的幅度和相位,直至满足停止条件(如达到预设迭代次数或残差平方和小于阈值)。 MATLAB中的VMD实现通常包括以下部分:数据预处理,如对原始信号进行归一化或去除直流偏置,以简化后续处理;定义VMD结构,设置模态数、迭代次数和约束参数等;VMD算法主体,包含初始化、交替最小二乘法和迭代优化过程;以及后处理,对分解结果进行评估和可视化,例如计算每个模态的频谱特性,绘制IMF的时频分布图。如果提供了一个包含VMD算法的压缩包文件,其中的“VMD”可能是MATLAB代码文件或完整的项目文件夹,可能包含主程序、函数库、示例数据和结果可视化脚本。通过运行这些代码,可以直观地看到VMD如何将复杂信号分解为独立模态,并理解每个模态的物理意义。 VMD在多个领域具有广泛的应用,包括信号处理(如声学、振动、生物医学信号分析)、图像处理(如图像去噪、特征提取)、金融时间序列分析(识
from langchain_ollama.llms import OllamaLLM from langchain.prompts import PromptTemplate from langchain.chains import LLMChain, ConversationChain from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE from langchain.chains.router.llm_router import RouterOutputParser from langchain.chains.router import LLMRouterChain, MultiPromptChain # 专家模型(提示词), llm = OllamaLLM(model="qwen2:7b") # 数 math_template = """ 你是一位伟大的数学家,知道所有的数学问题,你超越了高斯和莱布尼茨,我向你提问,问题如下: {input} """ math_prompt = PromptTemplate.format_prompt(math_template) math_chain = LLMChain(llm=llm, prompt=math_prompt,output_key="text") # 理 physics_template = """ 你是一位伟大的物理学家,知道所有的物理问题,你得过诺贝尔物理学奖,我向你提问,问题如下: {input} """ physics_prompt = PromptTemplate.format_prompt(physics_template) physics_chain = LLMChain(llm=llm, prompt=physics_prompt,output_key="text") # 化 chemistry_template = """ 你是一位伟大的化学家,知道所有的化学问题,你得过诺贝尔化学奖,我向你提问,问题如下: {input} """ chemistry_prompt = PromptTemplate.format_prompt(chemistry_template) chemistry_chain = LLMChain(llm=llm, prompt=chemistry_prompt,output_key="text") # 输入问题分析清楚有哪些可能和目的,目的 destination_chain = {"math":math_chain,"physics":physics_chain,"chemistry":chemistry_chain} # 默认 default_chain = ConversationChain(llm=llm, output_key="text") default_template = """ math:擅长回答数学问题 physics:擅长回答物理问题 chemistry:擅长回答化学问题 """ # 路由构建 router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format( destinations=default_template ) router_promt = PromptTemplate( template=router_template, input_variables=["input"], output_parser=RouterOutputParser() ) # 路由 router_chain = LLMRouterChain.from_llm(llm=llm,prompt=router_promt) # 最终选择() multi_prompt_chain = MultiPromptChain( router_chain=router_chain, default_chain=default_chain, destination_chains=destination_chain, verbose=False ) res = multi_prompt_chain.invoke({"input":"介绍一下高斯"}) print(res) 上面是源代码 KeyError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_16272
04-03
<think>好的,用户遇到了LangChain中的KeyError问题,具体是在多提示路由配置时出现的。我需要先分析可能的原因,然后给出解决方案。 首先,根据用户提到的错误类型KeyError,通常是因为访问了字典中不存在的键。在LangChain的多提示配置中,路由配置可能涉及到选择不同的提示模板,这时候如果路由逻辑返回的键不在现有的提示名称中,就会导致KeyError。例如,用户可能配置了多个提示,但路由函数返回了一个未被定义的名称,或者的名称拼写不一致。 接下来,查看用户提供的引用内容。引用[2]提到如果没有配置Token,会报错ValidationError,说明环境变量或参数配置的重要性。这可能提示用户检查是否正确定义了路由所需的参数或配置项。引用[3]讨论了数据结构和API协议的一致性,这可能对应到路由配置中的Schema是否正确,确保各个提示的名称和路由逻辑中的预期一致。 可能的原因有: 1. 提示名称拼写错误,导致路由返回的键无法匹配。 2. 路由函数逻辑错误,返回了不在可用列表中的键。 3. 配置文件中的名称与实际代码中的名称不一致。 解决方案方面,首先需要检查路由配置中的名称是否正确,确保所有可能的返回值都在可用的提示名称列表中。其次,验证路由函数的逻辑是否正确处理了所有情况,并返回有效的键。此外,检查配置文件或代码中是否存在拼写错误,比如大小写不一致或拼写错误。 最后,建议用户使用调试工具或打印中间变量来跟踪路由函数的输出,确认返回的键是否正确,并确保所有提示都已正确注册和命名。如果问题依旧存在,可能需要审查整个多提示的配置流程,确保每个步骤都符合LangChain的文档要求。</think>### LangChain KeyError 多提示路由配置错误分析及解决方案 #### 错误原因分析 1. **路由键值不匹配** 当使用多提示路由功能时,若`prompt_router`返回的`destination`值未在`destination_chains`定义的键名列表中找到匹配项,会触发`KeyError`错误[^3]。例如: ```python destination_chains = {"math": math_chain, "history": history_chain} # 若路由函数返回"science",但该键未在destination_chains中定义 ``` 2. **Schema定义不一致** 输入数据的字段名称与路由判断逻辑中使用的字段名称不一致。例如路由逻辑使用`query_type`字段进行判断,但输入数据中该字段名为`question_type`。 3. **默认缺失** 未配置`default_chain`时,当所有路由条件均不满足,系统无法选择默认处理。 #### 分步解决方案 1. **检查路由键值映射** 验证`destination_chains`的键名是否包含路由函数所有可能的返回值: ```python print("可用名称:", list(destination_chains.keys())) # 输出示例: ['math', 'history'] ``` 2. **标准化Schema定义** 确保输入数据字段与路由判断逻辑字段完全一致: ```python class RouterInput(BaseModel): query: str = Field(description="用户问题") query_type: str = Field(description="问题类型") # 必须与路由判断字段名相同 ``` 3. **添加默认处理** 配置默认处理未匹配情况: ```python from langchain.chains import LLMChain default_chain = LLMChain(llm=llm, prompt=default_prompt) ``` 4. **增强路由可靠性** 在路由函数中添加异常捕获和日志记录: ```python def route_query(info): try: if "方程" in info["query"]: return "math" elif "历史事件" in info["query"]: return "history" else: return "default" except KeyError as e: print(f"缺少必要字段: {e}") return "default" ``` 5. **验证配置完整性** 使用单元测试验证配置: ```python test_cases = [ {"query": "解方程x²-5x+6=0", "query_type": "数学问题"}, {"query": "明朝建立时间", "query_type": "历史问题"}, {"query": "天气预报", "query_type": "其他"} ] for case in test_cases: result = router_chain.run(case) print(f"输入: {case['query']} => 路由结果: {result}") ``` #### 典型错误示例修正 **错误配置:** ```python destination_chains = { "math_problem": math_chain, # 键名包含下划线 "history_qa": history_chain } def route_query(info): if "数学" in info["query"]: return "math-problem" # 返回带连字符的键名 ``` **修正方案:** ```python destination_chains = { "math-problem": math_chain, # 统一使用连字符命名 "history-qa": history_chain } ``` #### 调试建议 1. 启用LangChain调试模式: ```python import langchain langchain.debug = True ``` 2. 检查路由中间结果: ```python print("路由输入:", router_input) print("路由输出:", destination) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彬彬侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值