langchain_core.runnables.RunnablePassthrough
类是 LangChain 核心库中用于在工作流中直接传递输入的工具类,属于 LangChain Expression Language (LCEL) 的一部分,旨在构建灵活的链式操作。
本文基于 LangChain 0.3.x ,详细介绍 RunnablePassthrough
的定义、参数、方法和典型场景,并提供一个独立示例,展示如何使用 RunnablePassthrough
结合 ChatOpenAI
和 FAISS
实现人工智能主题的 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,生成新的字典。
- 参数:
mapping
(Dict[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
- 功能:同步调用,返回原始输入。
- 输入:
input
(Any
):任意输入。config
(Optional[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
结合 ChatOpenAI
和 FAISS
实现人工智能主题的 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'}
代码说明
- DirectoryLoader:
- 加载
ai_docs
目录中的*.txt
文件,使用TextLoader
。 - 启用
show_progress
显示加载进度。
- 加载
- 向量存储:
- 使用
FAISS
和OpenAIEmbeddings
创建向量存储。 - 配置
retriever
检索前 2 个最相似文档。
- 使用
- LLM 初始化:
- 使用
ChatOpenAI
调用gpt-3.5-turbo
,设置temperature=0.7
。
- 使用
- 提示模板:
ChatPromptTemplate
组合检索上下文和用户问题。
- RAG 链:
- 使用
RunnablePassthrough
传递用户查询到question
字段。 - LCEL 链组合
retriever
、prompt
、llm
和parser
。
- 使用
- 测试:
- 测试 RAG 链,回答 AI 定义问题。
- 显示加载的文档,展示
page_content
和metadata
。
- 错误处理:
- 使用
try-except
捕获文件或 API 错误。
- 使用
- 主题:
- 示例聚焦 AI 问答,突出
RunnablePassthrough
的输入传递。
- 示例聚焦 AI 问答,突出
- 独立性:
- 不涉及
RunnableWithMessageHistory
或其他上下文。
- 不涉及
运行要求:
- 有效的 OpenAI API 密钥:
export OPENAI_API_KEY="your-api-key"
- 安装依赖:
pip install --upgrade langchain langchain-openai faiss-cpu
- 准备
ai_docs
目录和文本文件。 - 网络连接:访问
https://api.openai.com
.
注意事项
- API 密钥:
- 确保
OPENAI_API_KEY
已设置:echo $OPENAI_API_KEY
- 或在代码中设置:
llm = ChatOpenAI(api_key="your-api-key")
- 确保
- 输入格式:
RunnablePassthrough
接受任意输入:result = passthrough.invoke("任意文本")
- 在字典输入中,确保键匹配:
chain.invoke("问题") # 直接输入 chain.invoke({"question": "问题"}) # 字典输入
- 链组合:
- 使用
|
连接 Runnable:chain = passthrough | another_runnable
- 使用
assign
扩展字典:chain = RunnablePassthrough.assign(new_field=lambda x: x["input"].upper())
- 使用
- 性能优化:
- 异步调用:使用
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)
- 异步调用:使用
- 错误调试:
- 输入未传递:
- 检查链结构:
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 链、输入保留、复杂工作流。