2.4 Runnable 组合与链式调用(pipe、assign、RunnableLambda 等组合方式,1.2.7 版本语法适配)

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 为整个组合链配置统一参数(如 tagscallbacks 等)

实战案例:文本生成→翻译→摘要的线性流程

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())

执行逻辑解析

  1. generator 接收 topic 输入,生成英文短文并通过 StrOutputParser 解析为字符串

  2. 该字符串作为 translatortext 输入,生成中文翻译

  3. 中文翻译作为 summarizertext 输入,生成最终摘要

  4. 整个流程的输入输出自动传递,无需手动处理中间结果

<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

  • 组合链支持直接调用 ainvokeabatch 等异步方法,无需手动适配

<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 版本新增)

    python 复制代码
    from langchain_core.runnables import RunnableWithCache
    
    cached_generator = RunnableWithCache(generator, cache_key="prompt:{topic}")
<2.4.6.3> 错误处理机制
  • 全局异常处理:通过 with_config 为组合链配置统一异常处理器

    python 复制代码
    def exception_handler(exc) -> str:
        return f"执行出错:{str(exc)}"
    
    chain_with_exception_handler = chain.with_config(
        exception_handlers={Exception: exception_handler}
    )
  • 局部异常处理:为关键组件单独配置异常处理,不影响整个流程

    python 复制代码
    safe_generator = generator.with_config(
        exception_handlers={Exception: lambda exc: "生成失败,请重试"}
    )
<2.4.6.4> 扩展性设计
  • 组件注册:通过工厂模式封装组件创建逻辑,便于替换与扩展

    python 复制代码
    def create_llm_component(model="gpt-3.5-turbo") -> Runnable:
        return ChatOpenAI(model=model) | StrOutputParser()
    
    # 替换模型时,仅需修改工厂函数参数
    llm_component = create_llm_component(model="gpt-4")
  • 配置注入:通过 configurable 动态配置组件参数,无需修改组合逻辑

    python 复制代码
    from 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}}
    )
相关推荐
九.九10 小时前
ops-transformer:AI 处理器上的高性能 Transformer 算子库
人工智能·深度学习·transformer
春日见10 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
恋猫de小郭10 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
deephub11 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
大模型RAG和Agent技术实践11 小时前
从零构建本地AI合同审查系统:架构设计与流式交互实战(完整源代码)
人工智能·交互·智能合同审核
老邋遢11 小时前
第三章-AI知识扫盲看这一篇就够了
人工智能
互联网江湖11 小时前
Seedance2.0炸场:长短视频们“修坝”十年,不如AI放水一天?
人工智能
PythonPioneer11 小时前
在AI技术迅猛发展的今天,传统职业该如何“踏浪前行”?
人工智能
冬奇Lab12 小时前
一天一个开源项目(第20篇):NanoBot - 轻量级AI Agent框架,极简高效的智能体构建工具
人工智能·开源·agent
阿里巴巴淘系技术团队官网博客12 小时前
设计模式Trustworthy Generation:提升RAG信赖度
人工智能·设计模式