【LangChain】langchain_core.runnables.RunnablePassthrough 类:在 LCEL 链中直接传递输入而不进行任何修改

langchain_core.runnables.RunnablePassthrough 类是 LangChain 核心库中用于在工作流中直接传递输入的工具类,属于 LangChain Expression Language (LCEL) 的一部分,旨在构建灵活的链式操作。

本文基于 LangChain 0.3.x ,详细介绍 RunnablePassthrough 的定义、参数、方法和典型场景,并提供一个独立示例,展示如何使用 RunnablePassthrough 结合 ChatOpenAIFAISS 实现人工智能主题的 RAG(Retrieval-Augmented Generation)问答,生示例突出 RunnablePassthrough 在输入传递中的作用。


langchain_core.runnables.RunnablePassthrough 简介

RunnablePassthrough 是 LangChain 核心库中的一个简单 Runnable 类,用于在 LCEL 链中直接传递输入而不进行任何修改。它作为一个占位符或桥梁,确保输入数据可以无缝流向链的下一个组件,特别适合需要保留原始输入或将其与其他处理结果组合的场景。

核心功能

  • 直接传递输入,不做任何转换。
  • 支持 LCEL 链的灵活组合,与其他 Runnable(如模型、检索器)无缝集成。
  • 提供静态方法(如 assign)处理字典输入的字段映射。
  • 支持同步和异步调用。

适用场景

  • 在 RAG 系统中传递用户查询到检索器或提示模板。
  • 组合多个输入源(如查询和上下文)到链中。
  • 构建复杂工作流,保留原始输入用于后续处理。
  • 简化链式操作,避免手动输入重定向。

与其他 Runnable 对比

  • 普通 Runnable:执行特定逻辑(如模型推理、检索)。
  • RunnablePassthrough:仅传递输入,无逻辑处理。
  • RunnableLambda:执行自定义函数,适合复杂逻辑。

类定义和初始化

以下是 RunnablePassthrough 的定义,基于 LangChain 核心库源码(langchain_core/runnables/passthrough.py)和官方文档(RunnablePassthrough)。

类签名
class RunnablePassthrough(RunnableSerializable[Any, Any]):
    def __init__(self) -> None
  • 参数:无初始化参数,直接创建。
  • 功能
    • 接受任意输入(Any),原样输出。
    • 支持 LCEL 管道(|)和其他 Runnable 的组合。
初始化示例
from langchain_core.runnables import RunnablePassthrough
passthrough = RunnablePassthrough()
静态方法

RunnablePassthrough 提供以下静态方法,便于处理字典输入:

1. assign
@classmethod
def assign(cls, **mapping: Runnable[Dict[str, Any], Any]) -> Runnable[Dict[str, Any], Dict[str, Any]]
  • 功能:将输入字典的字段映射到指定的 Runnable,生成新的字典。
  • 参数mappingDict[str, Runnable]),键为输出字段,值为处理该字段的 Runnable。
  • 返回:新的 Runnable,输出包含原始字段和新映射字段的字典。
  • 示例
    from langchain_core.runnables import RunnablePassthrough
    from langchain_core.prompts import PromptTemplate
    prompt = PromptTemplate.from_template("处理: {input}")
    chain = RunnablePassthrough.assign(output=prompt)
    result = chain.invoke({"input": "测试"})
    # 输出: {"input": "测试", "output": PromptValue(text="处理: 测试")}
    
2. as_tool
@classmethod
def as_tool(cls, *args, **kwargs) -> Any
  • 功能:将 RunnablePassthrough 转换为工具(Tool),用于工具调用场景。
  • 示例
    tool = RunnablePassthrough.as_tool(name="passthrough", description="直接传递输入")
    

工作原理

RunnablePassthrough 的运行逻辑非常简单:

  • 输入:接受任意类型输入(Any)。
  • 处理:不修改输入,直接返回。
  • 输出:与输入完全相同。

在 LCEL 中的作用

  • 在链式操作(|)中,RunnablePassthrough 确保输入流向下一个组件。
  • 通过 assign,支持动态扩展输入字典,添加新字段。

示例流程

input = "什么是 AI?"
chain = RunnablePassthrough() | some_runnable
output = chain.invoke(input)  # input 传递给 some_runnable

常用方法

RunnablePassthrough 继承自 langchain_core.runnables.RunnableSerializable,提供以下核心方法。

1. invoke
def invoke(self, input: Any, config: Optional[RunnableConfig] = None) -> Any
  • 功能:同步调用,返回原始输入。
  • 输入
    • inputAny):任意输入。
    • configOptional[RunnableConfig]):运行配置(如超时)。
  • 输出:与输入相同。
  • 示例
    result = passthrough.invoke("测试")
    print(result)  # 输出: 测试
    
2. ainvoke
async def ainvoke(self, input: Any, config: Optional[RunnableConfig] = None) -> Any
  • 功能:异步调用,返回原始输入。
  • 示例
    result = await passthrough.ainvoke("测试")
    print(result)  # 输出: 测试
    
3. stream / astream
  • 功能:支持流式输出,返回输入的单元素流。
  • 示例
    for chunk in passthrough.stream("测试"):
        print(chunk)  # 输出: 测试
    

使用方式

以下是使用 RunnablePassthrough 的步骤。

1. 安装依赖
pip install --upgrade langchain langchain-openai faiss-cpu
2. 设置 OpenAI API 密钥
export OPENAI_API_KEY="your-api-key"

或在代码中:

import os
os.environ["OPENAI_API_KEY"] = "your-api-key"
3. 初始化 RunnablePassthrough
from langchain_core.runnables import RunnablePassthrough
passthrough = RunnablePassthrough()
4. 构建 RAG 链
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document

# 准备文档
docs = [Document(page_content="人工智能是计算机科学的一个分支,模拟人类智能。")]
vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

# 定义链
llm = ChatOpenAI(model="gpt-3.5-turbo")
prompt = ChatPromptTemplate.from_template("上下文: {context}\n问题: {question}\n回答:")
chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
)
5. 调用链
response = chain.invoke("什么是人工智能?")
print(response.content)

使用 RunnablePassthrough 的示例

以下是一个独立示例,展示如何使用 RunnablePassthrough 结合 ChatOpenAIFAISS 实现人工智能主题的 RAG 问答。RunnablePassthrough 用于传递用户查询到提示模板,简化链式操作。

准备环境

  • 获取 OpenAI API 密钥:OpenAI Platform
  • 设置环境变量:
    export OPENAI_API_KEY="your-api-key"
    
  • 安装依赖:
    pip install --upgrade langchain langchain-openai faiss-cpu
    
  • 创建目录 ai_docs 并添加文件:
    • ai_docs/doc1.txt
      人工智能(AI)是计算机科学的一个分支,旨在模拟人类智能,如学习和推理。
      
    • ai_docs/doc2.txt
      AI 在医疗中的应用包括诊断、药物研发和个性化治疗。
      

代码

from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 初始化 DirectoryLoader
loader = DirectoryLoader(
    path="./ai_docs",
    glob="*.txt",
    loader_cls=TextLoader,
    loader_kwargs={"encoding": "utf-8"},
    show_progress=True
)

# 加载文档
docs = loader.load()

# 初始化向量存储
vectorstore = FAISS.from_documents(docs, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

# 初始化 ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)

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

# 定义输出解析器
parser = StrOutputParser()

# 创建 RAG 链,使用 RunnablePassthrough 传递问题
chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | parser
)

# 测试 RunnablePassthrough 和 RAG 链
print("测试 RunnablePassthrough 和 RAG 链:")
try:
    question = "人工智能是什么?"
    result = chain.invoke(question)
    
    print(f"输入问题: {question}")
    print(f"回答: {result}")
    
    # 显示加载的文档
    print("\n加载的文档:")
    for i, doc in enumerate(docs):
        print(f"文档 {i+1}:")
        print(f"内容: {doc.page_content[:100]}...")  # 截取前 100 字符
        print(f"元数据: {doc.metadata}")
except Exception as e:
    print(f"错误: {e}")

输出示例(实际输出取决于模型和 API 响应):

测试 RunnablePassthrough 和 RAG 链:
输入问题: 人工智能是什么?
回答: 人工智能(AI)是计算机科学的一个分支,旨在模拟人类智能,如学习、推理和问题解决。

加载的文档:
文档 1:
内容: 人工智能(AI)是计算机科学的一个分支,旨在模拟人类智能,如学习和推理。...
元数据: {'source': 'ai_docs/doc1.txt'}
文档 2:
内容: AI 在医疗中的应用包括诊断、药物研发和个性化治疗。...
元数据: {'source': 'ai_docs/doc2.txt'}
代码说明
  1. DirectoryLoader
    • 加载 ai_docs 目录中的 *.txt 文件,使用 TextLoader
    • 启用 show_progress 显示加载进度。
  2. 向量存储
    • 使用 FAISSOpenAIEmbeddings 创建向量存储。
    • 配置 retriever 检索前 2 个最相似文档。
  3. LLM 初始化
    • 使用 ChatOpenAI 调用 gpt-3.5-turbo,设置 temperature=0.7
  4. 提示模板
    • ChatPromptTemplate 组合检索上下文和用户问题。
  5. RAG 链
    • 使用 RunnablePassthrough 传递用户查询到 question 字段。
    • LCEL 链组合 retrieverpromptllmparser
  6. 测试
    • 测试 RAG 链,回答 AI 定义问题。
    • 显示加载的文档,展示 page_contentmetadata
  7. 错误处理
    • 使用 try-except 捕获文件或 API 错误。
  8. 主题
    • 示例聚焦 AI 问答,突出 RunnablePassthrough 的输入传递。
  9. 独立性
    • 不涉及 RunnableWithMessageHistory 或其他上下文。

运行要求

  • 有效的 OpenAI API 密钥:
    export OPENAI_API_KEY="your-api-key"
    
  • 安装依赖:
    pip install --upgrade langchain langchain-openai faiss-cpu
    
  • 准备 ai_docs 目录和文本文件。
  • 网络连接:访问 https://api.openai.com.

注意事项

  1. API 密钥
    • 确保 OPENAI_API_KEY 已设置:
      echo $OPENAI_API_KEY
      
    • 或在代码中设置:
      llm = ChatOpenAI(api_key="your-api-key")
      
  2. 输入格式
    • RunnablePassthrough 接受任意输入:
      result = passthrough.invoke("任意文本")
      
    • 在字典输入中,确保键匹配:
      chain.invoke("问题")  # 直接输入
      chain.invoke({"question": "问题"})  # 字典输入
      
  3. 链组合
    • 使用 | 连接 Runnable:
      chain = passthrough | another_runnable
      
    • 使用 assign 扩展字典:
      chain = RunnablePassthrough.assign(new_field=lambda x: x["input"].upper())
      
  4. 性能优化
    • 异步调用:使用 ainvoke
      result = await chain.ainvoke("问题")
      
    • 流式输出:使用 stream
      for chunk in chain.stream("问题"):
          print(chunk)
      
    • 缓存嵌入:结合 CacheBackedEmbeddings
      from langchain.embeddings import CacheBackedEmbeddings
      cached_embeddings = CacheBackedEmbeddings.from_bytes_store(OpenAIEmbeddings(), LocalFileStore("./cache"))
      vectorstore = FAISS.from_documents(docs, cached_embeddings)
      
  5. 错误调试
    • 输入未传递
      • 检查链结构:
        print(chain)
        
      • 验证输入:
        print(input)
        
    • API 错误
      • 检查密钥:
        print(os.environ.get("OPENAI_API_KEY"))
        
      • 增加超时:
        llm = ChatOpenAI(timeout=30)
        
    • 检索失败
      • 检查文档:
        print(len(docs))
        
      • 调整 k
        retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
        

常见问题

Q1:如何使用 RunnablePassthrough.assign
A:映射新字段:

chain = RunnablePassthrough.assign(
    upper=lambda x: x["input"].upper(),
    length=lambda x: len(x["input"])
)
result = chain.invoke({"input": "测试"})
# 输出: {"input": "测试", "upper": "测试", "length": 2}

Q2:如何与消息历史结合?
A:结合 RunnableWithMessageHistory

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
chain_with_history = RunnableWithMessageHistory(
    runnable={"question": RunnablePassthrough(), "context": retriever} | prompt | llm,
    get_session_history=lambda session_id: ChatMessageHistory(),
    input_messages_key="question",
    history_messages_key="history"
)

Q3:如何支持流式输出?
A:使用 stream

for chunk in chain.stream("什么是 AI?"):
    print(chunk, end="", flush=True)

Q4:如何支持开源模型?
A:使用 ChatOllama

from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3")
chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | parser

总结

langchain_core.runnables.RunnablePassthrough 是 LangChain 中简洁的输入传递工具,核心功能包括:

  • 定义:直接传递输入,无修改。
  • 初始化:无需参数,简单创建。
  • 常用方法invoke(同步)、ainvoke(异步)、assign(字段映射)。
  • 适用场景:RAG 链、输入保留、复杂工作流。
# 导入 langchain 相关的依赖包 from langchain.chat_models import ChatOpenAI # 导入向量数据库(向量存储、查询) from langchain_community.vectorstores import Chroma # 导入 langchain 输出函数(格式化输出) from langchain_core.output_parsers import StrOutputParser # 导入 langchain Prompt 模板, prompt 管理 from langchain_core.prompts import ChatPromptTemplate from langchain_core.pydantic_v1 import BaseModel # 导入 langchainLCEL 解释器包 from langchain_core.runnables import RunnableParallel, RunnablePassthrough # 导入langchain 文本拆分器 from langchain.text_splitter import RecursiveCharacterTextSplitter # 导入 langchain 的文件加载器 (WebBaseLoader的功能是拉取网页数据,解析为 langchain Document 结构) from langchain_community.document_loaders import WebBaseLoader from langchain_community.embeddings import QianfanEmbeddingsEndpoint import os def embedQianfan(): os.environ["QIANFAN_AK"] = "" # 这里需要修改为自己的实际值 os.environ["QIANFAN_SK"] = "" # 这里需要修改为自己的实际值 embed = QianfanEmbeddingsEndpoint() return embed # 加载网页 https://docs.dify.ai/v/zh-hans/getting-started/readme 的数据 raw_documents = WebBaseLoader("https://docs.dify.ai/v/zh-hans/getting-started/readme").load() # raw_documents = WebBaseLoader("https://docs.dify.ai/v/zh-hans/guides/knowledge-base").load() # 将网页数据拆分为 chunk 的大小 text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) all_splits = text_splitter.split_documents(raw_documents) # 将文本编码为向量,并保存为向量 vectorstore = Chroma.from_documents( documents=all_splits, collection_name="`RAG`-chroma", embedding=embedQianfan(), ) retriever = vectorstore.as_retriever() # 构建 `RAG` prompt template = """Answer the question with chinese and based only on the following context: {context} Question: {question} """ prompt = ChatPromptTemplate.from_template(template) # 初始化`LLM ` model = ChatOpenAI(openai_api_base="http://10.66.253.126:11434/v1", # model="deepseek-r1:7b", model="qwen2.5:latest", openai_api_key="ollama") # model = openai`LLM `() # 使用 LCEL 表达式构建 `RAG` chain chain = ( RunnableParallel({"context": retriever, "question": RunnablePassthrough()}) | prompt | model | StrOutputParser() ) # Add typing for input class Question(BaseModel): __root__: str chain = chain.with_types(input_type=Question) # print(chain.invoke("dify 是什么")) print(chain.invoke("dify 能干什么?请用列表形式回答")) print(chain.invoke("dify 可以导入哪些数据?")) print(chain.invoke("dify如何导入 nation 数据?")) 其中千帆的嵌入模型有没有本地化的方式替代
最新发布
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

彬彬侠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值