【LangChain】什么是 Runnable

在 LangChain 中,Runnable 是一个核心概念,指的是可以被执行(“run”)的对象,通常用于构建和组合工作流。Runnable 是 LangChain Expression Language(LCEL)的基石,允许开发者通过声明式的方式将提示(Prompt)、语言模型(LLM)、解析器(Output Parser)等组件组合成链(Chain),并以统一的方式调用。以下基于 LangChain 0.3.x,详细解释 Runnable 的定义、作用、类型及使用场景,并提供代码示例。


什么是 Runnable

Runnable 是 LangChain 中定义在 langchain_core.runnables 模块中的抽象接口,代表一个可执行的单元。任何实现 Runnable 接口的对象都可以通过标准方法(如 invokestreambatch)运行,处理输入并生成输出。Runnable 的设计目标是:

  1. 统一接口:为不同组件(如 LLM、提示模板、解析器)提供一致的调用方式。
  2. 模块化组合:通过 LCEL(如 | 运算符)将多个 Runnable 组合成复杂工作流。
  3. 灵活性:支持同步、异步、流式和批量调用,适配各种场景。

核心方法

  • invoke(input):同步调用,处理单个输入,返回输出。
  • ainvoke(input):异步调用,适合高并发场景。
  • stream(input):流式调用,逐步返回输出(例如逐字生成)。
  • batch(inputs):批量调用,处理多个输入。
  • abatch(inputs):异步批量调用。

典型实现

  • 提示模板(ChatPromptTemplate
  • 语言模型(ChatOpenAIChatOllama
  • 解析器(StrOutputParserJsonOutputParser
  • 检索器(VectorStoreRetriever
  • 自定义链(RunnableLambdaRunnableSequence

Runnable 的作用

Runnable 在 LangChain 中的作用包括:

  1. 组件抽象:将复杂功能(LLM 调用、提示生成、数据检索)封装为可复用的模块。
  2. 链式组合:通过 LCEL 的 | 运算符,将多个 Runnable 串联,形成从输入到输出的完整工作流。
  3. 扩展性:支持自定义 Runnable,允许开发者实现特定逻辑。
  4. 一致性:提供统一的调用接口,简化调试和维护。
  5. 动态处理:支持条件分支(RunnableBranch)、并行执行(RunnableParallel)等高级功能。

Runnable 的主要类型

LangChain 提供了多种 Runnable 实现,位于 langchain_core.runnables 模块,常见类型包括:

1. RunnableSequence
  • 功能:按顺序执行多个 Runnable,前一个的输出作为后一个的输入。
  • 表示:通过 | 运算符创建(如 prompt | llm | parser)。
  • 适用场景:线性工作流,如提示生成 → LLM 调用 → 输出解析。
  • 示例
    chain = prompt | llm | StrOutputParser()
    
2. RunnableParallel
  • 功能:并行执行多个 Runnable,将结果组合为字典。
  • 表示:使用字典定义并行任务(如 RunnableParallel({"key1": runnable1, "key2": runnable2}))。
  • 适用场景:需要同时执行多个任务,如检索文档和处理用户输入。
  • 示例
    chain = RunnableParallel({"context": retriever, "question": RunnablePassthrough()})
    
3. RunnablePassthrough
  • 功能:直接传递输入,不做任何修改。
  • 适用场景:占位符或保持输入不变,如在并行链中传递原始问题。
  • 示例
    chain = {"question": RunnablePassthrough()} | prompt
    
4. RunnableLambda
  • 功能:将 Python 函数封装为 Runnable,支持自定义逻辑。
  • 适用场景:需要插入自定义处理逻辑,如数据清洗、格式转换。
  • 示例
    from langchain_core.runnables import RunnableLambda
    runnable = RunnableLambda(lambda x: x.upper())
    
5. RunnableBranch
  • 功能:根据条件选择执行不同的 Runnable 分支。
  • 适用场景:动态路由,如根据输入类型选择不同处理路径。
  • 示例
    branch = RunnableBranch(
        (lambda x: "question" in x, question_chain),
        (lambda x: "command" in x, command_chain),
        default_chain
    )
    
6. RunnableWithMessageHistory
  • 功能:为链添加对话历史管理,跟踪多轮对话。
  • 适用场景:聊天机器人、上下文相关的问答。
  • 示例
    chain = RunnableWithMessageHistory(runnable, get_session_history)
    
7. 内置组件的 Runnable
  • 提示模板(如 ChatPromptTemplate):将输入格式化为消息。
  • 语言模型(如 ChatOpenAI):生成文本或结构化输出。
  • 解析器(如 StrOutputParser):将 LLM 输出转换为特定格式。
  • 检索器(如 VectorStoreRetriever):从向量存储中检索文档。

使用场景与代码示例

以下展示 Runnable 在不同场景中的应用,使用 langchain_openai.ChatOpenAI 和 LCEL 构建链。

1. 简单问答(RunnableSequence

构建一个简单的问答链,组合提示、LLM 和解析器。

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

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

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

# 定义提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个助手,请准确回答问题。"),
    ("human", "{question}")
])

# 创建 RunnableSequence
chain = prompt | llm | StrOutputParser()

# 调用链
response = chain.invoke({"question": "什么是人工智能?"})
print(response)

输出示例

人工智能(AI)是计算机科学的一个分支,旨在创建能够执行需要人类智能的任务的系统,例如学习、推理、问题解决和决策。

说明

  • prompt | llm | StrOutputParser 创建一个 RunnableSequence
  • 每个组件(ChatPromptTemplateChatOpenAIStrOutputParser)都是 Runnable
  • 输入为字典,输出为字符串。
2. 语义搜索(RunnableParallel

结合向量存储和并行处理,构建语义搜索链。

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

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_community.vectorstores import Chroma
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

# 初始化嵌入模型和 LLM
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(temperature=0, model="gpt-4o-mini")

# 准备文档
docs = [
    "人工智能(AI)是计算机科学的一个分支,专注于创建智能系统。",
    "机器学习是 AI 的子领域,涉及从数据中学习模型。"
]

# 创建向量存储
vectorstore = Chroma.from_texts(docs, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 1})

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

# 创建 RunnableParallel
chain = (
    RunnableParallel({"context": retriever, "question": RunnablePassthrough()})
    | prompt
    | llm
    | StrOutputParser()
)

# 调用链
response = chain.invoke("什么是人工智能?")
print(response)

输出示例

人工智能(AI)是计算机科学的一个分支,专注于创建能够执行需要人类智能的任务的系统。

说明

  • RunnableParallel 并行执行检索(retriever)和输入传递(RunnablePassthrough)。
  • 结果作为字典({"context": ..., "question": ...})传入 prompt
3. 对话历史(RunnableWithMessageHistory

使用 RunnableWithMessageHistory 构建支持对话历史的链。

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

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory

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

# 定义提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个助手,请根据对话历史回答问题。"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

# 创建 RunnableSequence
runnable = prompt | llm

# 初始化消息历史存储
store = {}
def get_session_history(session_id: str) -> ChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

# 创建 RunnableWithMessageHistory
chain = RunnableWithMessageHistory(
    runnable,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history"
)

# 调用链
session_id = "user1"
response = chain.invoke(
    {"input": "我叫鲍勃"},
    config={"configurable": {"session_id": session_id}}
)
print(response.content)

response = chain.invoke(
    {"input": "我的名字是什么?"},
    config={"configurable": {"session_id": session_id}}
)
print(response.content)

输出示例

你好,鲍勃!很高兴认识你!有什么可以帮助你的?
你的名字是鲍勃。

说明

  • RunnableWithMessageHistory 包装 RunnableSequence,自动管理对话历史。
  • ChatMessageHistory 存储消息,session_id 区分会话。
4. 自定义逻辑(RunnableLambda

使用 RunnableLambda 插入自定义处理逻辑。

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

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda

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

# 定义提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个助手。"),
    ("human", "{question}")
])

# 定义自定义 Runnable
uppercase = RunnableLambda(lambda x: x.upper())

# 创建链
chain = prompt | llm | StrOutputParser() | uppercase

# 调用链
response = chain.invoke({"question": "什么是人工智能?"})
print(response)

输出示例

人工智能(AI)是计算机科学的一个分支,旨在创建能够执行需要人类智能的任务的系统,例如学习、推理、问题解决和决策。

说明

  • RunnableLambda 将输出转换为大写,作为链的最后一步。
  • 自定义逻辑可处理任意 Python 函数。

注意事项

  1. API 密钥安全
    • 避免硬编码密钥,推荐使用 .env 文件:
      from dotenv import load_dotenv
      load_dotenv()  # 加载 OPENAI_API_KEY
      
    • 确保密钥支持指定模型(如 gpt-4o-mini)。
  2. 输入输出格式
    • Runnable 的输入和输出需匹配链中各组件的期望格式。
    • 使用 prompt.format()chain.input_schema 调试输入格式。
  3. 性能
    • 流式调用(stream)适合实时交互,异步调用(ainvoke)适合高并发。
    • 批量调用(batch)可提高效率。
  4. Ollama 支持
    • langchain_ollama.ChatOllama 也是 Runnable,可替换 ChatOpenAI,无需 OpenAI API:
      from langchain_ollama import ChatOllama
      llm = ChatOllama(model="qwen3:1.7b")
      
  5. 调试
    • 设置 verbose=True 或使用 chain.get_graph() 可视化链结构。
    • 打印中间结果(如 prompt.invoke(input))检查问题。

常见问题

Q1:Runnable 和传统链有什么区别?
A:传统链(如 LLMChain)是旧 API,代码复杂且不灵活。Runnable 使用 LCEL,提供统一接口、模块化组合和高级功能(如流式、并行)。

Q2:如何调试 Runnable 链?
A:使用 verbose=True 查看执行日志,或单独调用每个 Runnable(如 prompt.invoke()llm.invoke())检查中间输出。

Q3:Runnable 支持工具调用吗?
A:支持,ChatOpenAI 等模型通过 bind_tools 实现工具调用,链中可解析 tool_calls

Q4:可以自定义 Runnable 吗?
A:可以,使用 RunnableLambda 或继承 Runnable 实现自定义逻辑。


总结

Runnable 是 LangChain 的核心抽象,表示可执行的组件,支持 LCEL 构建复杂工作流。主要类型包括:

  • RunnableSequence:顺序执行(| 运算符)。
  • RunnableParallel:并行执行(字典形式)。
  • RunnablePassthrough:传递输入。
  • RunnableLambda:自定义逻辑。
  • RunnableBranch:条件分支。
  • RunnableWithMessageHistory:对话历史管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

彬彬侠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值