2.4 Runnable 组合与链式调用(pipe、assign、RunnableLambda 等组合方式,1.2.7 版本语法适配)
2.4.1 组合与链式调用核心概念
Runnable 作为 langchain 1.2.7 版本的核心执行单元,其组合能力是实现复杂工作流的基础。链式调用通过将多个 Runnable 组件按逻辑顺序串联或并行组合,形成结构化执行流程,支持输入输出的自动传递与数据格式适配。langchain 1.2.7 版本中,Runnable 组合体系以 langchain-core 为底层支撑,提供 pipe(线性串联)、assign(并行数据补充)、RunnableLambda(自定义逻辑嵌入)等标准化组合方式,且完全兼容 1.2.7 版本的语法规范,解决了低版本中组合逻辑分散、兼容性差的问题。
核心设计目标:
-
实现组件的解耦与复用,降低复杂流程的维护成本
-
提供统一的组合语法,兼容同步/异步执行场景
-
支持动态配置透传与异常边界处理
-
适配 1.2.7 版本的 Runnable 核心方法(invoke/ainvoke、batch/abatch 等)
2.4.2 环境准备与依赖说明
需确保以下依赖版本严格匹配,避免版本冲突导致的语法报错:
Bash
pip install langchain==1.2.7
pip install langchain-core==1.2.7
pip install langchain-community==0.4.1
pip install langchain-openai==1.1.7
pip install langchain-classic==1.0.1
pip install openai==1.13.3 # 适配 langchain-openai 1.1.7 的兼容版本
基础导入语句(后续案例统一依赖此导入):
python
from langchain_core.runnables import (
Runnable,
RunnableParallel,
RunnableLambda,
RunnableSequence,
)
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
import os
# 环境配置(确保 OpenAI API 密钥有效)
os.environ["OPENAI_API_KEY"] = "your-openai-api-key"
2.4.3 核心组合方式详解(1.2.7 版本语法)
<2.4.3.1> pipe:线性串联组合
pipe 是 Runnable 最基础的组合方式,实现多个组件的线性执行:前一个组件的输出作为后一个组件的输入,形成"输入→组件1→组件2→...→输出"的流水线结构。1.2.7 版本中,pipe 支持同步/异步调用,且与 RunnableSequence 完全兼容,语法更简洁。
语法规范:
python
# 基础语法:组件1 | 组件2 | 组件3(等价于 RunnableSequence.from_sequence([组件1, 组件2, 组件3]))
combined_runnable = component1 | component2 | component3
# 同步执行
result = combined_runnable.invoke(input_data)
# 异步执行(1.2.7 版本原生支持)
result_async = await combined_runnable.ainvoke(input_data)
关键参数说明:
-
参与
pipe组合的组件必须实现Runnable协议(即具备invoke方法) -
前序组件的输出格式必须与后序组件的输入格式兼容(如无兼容,需通过
RunnableLambda进行格式转换) -
支持通过
with_config为整个组合链配置统一参数(如tags、callbacks等)
实战案例:文本生成→翻译→摘要的线性流程
python
# 1. 定义单个 Runnable 组件
prompt = ChatPromptTemplate.from_template("生成一篇关于 {topic} 的英文短文(100词左右)")
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
translator_prompt = ChatPromptTemplate.from_template("将以下英文文本翻译成中文:{text}")
summarizer_prompt = ChatPromptTemplate.from_template("对以下中文文本生成30字以内的摘要:{text}")
# 组件初始化(均遵循 Runnable 协议)
generator = prompt | llm | StrOutputParser()
translator = translator_prompt | llm | StrOutputParser()
summarizer = summarizer_prompt | llm | StrOutputParser()
# 2. 使用 pipe 组合成链式流程
chain = generator | translator | summarizer
# 3. 同步执行
input_data = {"topic": "人工智能在医疗领域的应用"}
result = chain.invoke(input_data)
print("最终结果:", result)
# 4. 异步执行示例
# import asyncio
# async def run_chain():
# result_async = await chain.ainvoke(input_data)
# print("异步执行结果:", result_async)
# asyncio.run(run_chain())
执行逻辑解析:
-
generator接收topic输入,生成英文短文并通过StrOutputParser解析为字符串 -
该字符串作为
translator的text输入,生成中文翻译 -
中文翻译作为
summarizer的text输入,生成最终摘要 -
整个流程的输入输出自动传递,无需手动处理中间结果
<2.4.3.2> assign:并行数据补充组合
assign 用于在执行流程中并行补充多个数据字段,核心作用是"在不中断主流程的前提下,为上下文添加额外信息"。1.2.7 版本中,assign 被封装为 RunnableParallel 的简化语法,支持字典格式配置,输出为包含所有字段的统一字典。
语法规范:
python
# 基础语法:RunnableParallel({key1: component1, key2: component2, ...})
# 等价于 component.assign(key1=component1, key2=component2)(1.2.7 版本新增简化语法)
parallel_runnable = RunnableParallel(
main=main_component, # 主流程组件
extra1=extra_component1, # 并行补充组件1
extra2=extra_component2 # 并行补充组件2
)
# 或简化为(推荐用法)
parallel_runnable = main_component | RunnableParallel(
extra1=extra_component1,
extra2=extra_component2
)
关键特性:
-
所有组件并行执行(基于 asyncio 实现,提升执行效率)
-
输入数据会同时传递给所有并行组件,各组件独立计算
-
输出为字典,包含所有组件的执行结果(key 为配置名,value 为组件输出)
-
支持与
pipe组合使用,实现"并行补充→线性执行"的混合流程
实战案例:文本生成+关键词提取+情感分析的并行补充
python
# 1. 定义主组件与并行补充组件
main_prompt = ChatPromptTemplate.from_template("写一段关于 {product} 的用户评价(50字左右)")
keyword_prompt = ChatPromptTemplate.from_template("提取以下文本的3个核心关键词:{text}")
sentiment_prompt = ChatPromptTemplate.from_template("分析以下文本的情感倾向(正面/负面/中性):{text}")
main_chain = main_prompt | llm | StrOutputParser()
keyword_chain = keyword_prompt | llm | StrOutputParser()
sentiment_chain = sentiment_prompt | llm | StrOutputParser()
# 2. 使用 assign 组合并行流程
# 方式1:直接通过 RunnableParallel 定义
parallel_chain = RunnableParallel(
original_text=main_chain, # 主流程:生成用户评价
keywords=keyword_chain, # 并行:提取关键词
sentiment=sentiment_chain # 并行:情感分析
)
# 方式2:主流程后接 assign(更符合线性思维)
parallel_chain = main_chain | RunnableParallel(
keywords=lambda x: keyword_chain.invoke({"text": x}),
sentiment=lambda x: sentiment_chain.invoke({"text": x})
)
# 3. 执行并获取结果
input_data = {"product": "无线蓝牙耳机"}
result = parallel_chain.invoke(input_data)
print("并行组合结果:", result)
# 输出格式:{"original_text": "xxx", "keywords": "xxx", "sentiment": "xxx"}
进阶用法:并行结果作为后续流程输入
python
# 组合并行流程与后续处理流程
final_prompt = ChatPromptTemplate.from_template(
"用户评价:{original_text}\n"
"关键词:{keywords}\n"
"情感倾向:{sentiment}\n"
"请基于以上信息,生成一份产品改进建议(30字左右)"
)
final_chain = parallel_chain | final_prompt | llm | StrOutputParser()
result = final_chain.invoke(input_data)
print("最终改进建议:", result)
<2.4.3.3> RunnableLambda:自定义逻辑嵌入组合
RunnableLambda 用于将普通函数(同步/异步)封装为 Runnable 组件,实现自定义逻辑的嵌入,是连接 Runnable 生态与原生 Python 逻辑的核心桥梁。1.2.7 版本中,RunnableLambda 支持自动适配同步/异步函数,且与其他组合方式完全兼容。
语法规范:
python
# 同步函数封装
def sync_func(input_data):
# 自定义逻辑处理
return processed_data
runnable_lambda = RunnableLambda(sync_func)
# 异步函数封装(1.2.7 版本原生支持,无需额外适配)
async def async_func(input_data):
# 异步逻辑处理(如异步 API 调用、文件读写等)
return processed_data
async_runnable_lambda = RunnableLambda(async_func)
# 匿名函数简化写法
runnable_lambda = RunnableLambda(lambda x: x.upper()) # 字符串大写转换示例
关键参数与约束:
-
输入参数:函数接收的参数为前序组件的输出(若为字典,可直接通过键名访问)
-
输出要求:函数返回值需与后序组件的输入格式兼容
-
异常处理:函数内部抛出的异常会被 Runnable 体系的异常处理机制捕获(可通过
with_config配置exception_handlers) -
异步支持:异步函数封装后,可通过
ainvoke异步执行,同步函数则自动适配同步/异步调用
实战案例1:数据格式转换与清洗
python
# 定义数据清洗函数(同步)
def clean_text(text):
"""去除文本中的特殊字符,统一换行符"""
import re
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s]", "", text)
text = re.sub(r"\n+", "\n", text).strip()
return {"cleaned_text": text}
# 封装为 RunnableLambda
cleaner = RunnableLambda(clean_text)
# 组合流程:生成文本→清洗→摘要
generate_chain = ChatPromptTemplate.from_template("写一段关于 {theme} 的短文") | llm | StrOutputParser()
summarize_chain = ChatPromptTemplate.from_template("摘要:{cleaned_text}") | llm | StrOutputParser()
final_chain = generate_chain | cleaner | summarize_chain
# 执行
result = final_chain.invoke({"theme": "环境保护"})
print("清洗后摘要:", result)
实战案例2:异步逻辑嵌入(异步 API 调用)
待补充。
2.4.4 组合方式的混合使用与复杂流程构建
1.2.7 版本的 Runnable 组合体系支持多种方式嵌套使用,可构建复杂的业务流程。核心设计思路是"以 pipe 为骨架,assign 实现并行补充,RunnableLambda 处理自定义逻辑"。
实战案例:复杂问答流程(检索→生成→校验→优化)
python
# 1. 定义各环节组件
# 检索组件(模拟向量数据库检索)
def retrieve_relevant_documents(query):
"""模拟检索相关文档"""
docs = [
"Langchain 1.2.7 版本中,Runnable 协议支持多组件组合",
"RunnableParallel 可实现组件并行执行,提升效率",
"RunnableLambda 支持同步/异步函数封装"
]
return {"query": query, "relevant_docs": "\n".join(docs)}
retriever = RunnableLambda(retrieve_relevant_documents)
# 生成组件(基于检索结果生成回答)
generate_prompt = ChatPromptTemplate.from_template(
"基于以下参考文档回答问题:\n{relevant_docs}\n问题:{query}\n回答:"
)
generator = generate_prompt | llm | StrOutputParser()
# 校验组件(校验回答是否符合要求)
def validate_answer(answer):
"""校验回答长度是否大于50字"""
return {
"answer": answer,
"is_valid": len(answer) > 50,
"reason": "长度符合要求" if len(answer) > 50 else "长度不足50字"
}
validator = RunnableLambda(validate_answer)
# 优化组件(若回答无效,重新生成)
optimize_prompt = ChatPromptTemplate.from_template(
"原回答:{answer}\n问题:{query}\n参考文档:{relevant_docs}\n"
"要求:回答长度需大于50字,请重新生成"
)
optimizer = optimize_prompt | llm | StrOutputParser()
# 2. 组合复杂流程(pipe + assign + RunnableLambda)
chain = (
# 第一步:检索相关文档
retriever
# 第二步:并行执行生成与额外信息补充(此处补充文档长度)
| RunnableParallel(
original_answer=generator,
doc_length=RunnableLambda(lambda x: len(x["relevant_docs"]))
)
# 第三步:校验回答有效性
| RunnableLambda(lambda x: {**x, "validation": validator.invoke(x["original_answer"])})
# 第四步:根据校验结果决定是否优化(条件分支逻辑)
| RunnableLambda(
lambda x: optimizer.invoke({
"answer": x["validation"]["answer"],
"query": x["query"],
"relevant_docs": x["relevant_docs"]
}) if not x["validation"]["is_valid"] else x["validation"]["answer"]
)
)
# 3. 执行流程
result = chain.invoke({"query": "Langchain 1.2.7 中 Runnable 有哪些组合方式?"})
print("最终回答:", result)
2.4.5 1.2.7 版本语法适配与兼容性说明
<2.4.5.1> 与低版本的核心差异
-
废弃了低版本的
Chain类,统一使用Runnable组合替代(如SimpleSequentialChain可通过pipe实现) -
RunnableParallel替代了低版本的ParallelChain,语法更简洁,支持字典格式配置 -
RunnableLambda支持异步函数封装,无需额外使用AsyncRunnable类 -
组合链支持直接调用
ainvoke、abatch等异步方法,无需手动适配
<2.4.5.2> 常见兼容性问题解决方案
| 低版本用法 | 1.2.7 版本替代方案 |
|---|---|
SimpleSequentialChain(chains=[c1, c2]) |
`c1 |
ParallelChain(chains=[c1, c2], output_keys=["k1", "k2"]) |
RunnableParallel(k1=c1, k2=c2) |
LambdaChain(func=sync_func) |
RunnableLambda(sync_func) |
AsyncLambdaChain(func=async_func) |
RunnableLambda(async_func) |
2.4.6 最佳实践与设计模式
<2.4.6.1> 组件拆分原则
-
单一职责:每个 Runnable 组件仅负责一项核心功能(如生成、解析、清洗、检索)
-
粒度适中:避免过细拆分导致组合复杂,避免过粗拆分降低复用性
-
格式统一:组件间输入输出优先使用字典格式,便于
assign组合与参数传递
<2.4.6.2> 性能优化建议
-
并行优先:多个独立组件(无依赖关系)优先使用
RunnableParallel并行执行,提升效率 -
异步适配:I/O 密集型操作(如 API 调用、数据库查询)优先封装为异步函数,通过
ainvoke执行 -
缓存策略:重复执行的组件可通过
RunnableWithCache缓存结果(1.2.7 版本新增)pythonfrom langchain_core.runnables import RunnableWithCache cached_generator = RunnableWithCache(generator, cache_key="prompt:{topic}")
<2.4.6.3> 错误处理机制
-
全局异常处理:通过
with_config为组合链配置统一异常处理器pythondef exception_handler(exc) -> str: return f"执行出错:{str(exc)}" chain_with_exception_handler = chain.with_config( exception_handlers={Exception: exception_handler} ) -
局部异常处理:为关键组件单独配置异常处理,不影响整个流程
pythonsafe_generator = generator.with_config( exception_handlers={Exception: lambda exc: "生成失败,请重试"} )
<2.4.6.4> 扩展性设计
-
组件注册:通过工厂模式封装组件创建逻辑,便于替换与扩展
pythondef create_llm_component(model="gpt-3.5-turbo") -> Runnable: return ChatOpenAI(model=model) | StrOutputParser() # 替换模型时,仅需修改工厂函数参数 llm_component = create_llm_component(model="gpt-4") -
配置注入:通过
configurable动态配置组件参数,无需修改组合逻辑pythonfrom langchain_core.runnables import configurable @configurable(fields={"temperature": float}) def generate_with_temperature(input_data, temperature=0.7): prompt = ChatPromptTemplate.from_template("生成关于 {topic} 的文本") return (prompt | ChatOpenAI(temperature=temperature) | StrOutputParser()).invoke(input_data) configurable_generator = RunnableLambda(generate_with_temperature) # 执行时动态指定配置 result = configurable_generator.invoke( {"topic": "AI"}, config={"configurable": {"temperature": 0.3}} )