[LangChain之链]LangChain的Chain——由Runnable构建的管道

在 LangChain 中,Chain 指的是对多个组件(如 Prompt、LLM和OutputPparser等)的一系列调用流程。它具有两种实现方式,即传统的以LLMChain为代表的"老链"和最新的采用LCEL(LangChain Exppression Language)的"新链"。

1. 传统"老链"

从传统链(Legacy Chains)到 LCEL的演进是一次从"命令式封装"向"声明式组合"的重大飞跃。早期的 LangChain 依靠一系列硬编码的类(如LLMChainRetrievalQA)来构建应用。开发者调用特定的类,内部逻辑在这些类中被高度封装,定制起来比较麻烦。比如 提示词往往隐藏在源代码中,修改不便。接口不统一也是一个硬伤,不同的链可能需要不同的输入输出格式。而最大的问题还是功能扩展难,想要在中间步骤添加自定义逻辑(如中间处理、日志)非常繁琐。

在如下的演示程序中,我们利用LLMChain实现了一个典型的应用场景:传入参数格式化提示词模板,将生成的提示词作为输入调用LLM并解析输出。这个流程涉及的三个步骤需要对应的三个组件,分别是表示提示词模板的ChatPromptTemplateLLM模型(ChatOpenAI)和用于解析输出的StrOutputParser

python 复制代码
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_classic.chains  import LLMChain

prompt = ChatPromptTemplate.from_template("Give me small report about {topic}")
model = ChatOpenAI(
    model="gpt-5.2-chat",
    base_url="base url of your deployed model",
    api_key="your api key"
)
parser = StrOutputParser()
chain = LLMChain(
    prompt=prompt,
    llm=model,
    output_parser=parser
)
result = chain.run(topic="Artificial Intelligence")
print(result)

我们将三个组件作为参数调用构造函数构建LLMChain对象,然后将提示词模板的占位符参数作为输入调用它的run方法,最终就会得到我们希望看到的文体,前面的输出内容如下所示:

csharp 复制代码
**Small Report on Artificial Intelligence**

Artificial Intelligence (AI) is a branch of computer science focused on creating machines and systems capable of performing tasks that normally require human intelligence  result = chain.run(topic="Artificial Intelligence")
...

2. 基于LCEL的"新链"

正如前面所说,整个链作为作为一个黑盒被完全封装到LLMChain中,我们根本不知道背后的链路是什么样子,更不用说对它进行定制了。这种传统的编程模式已经基本没有使用了,取而代之的是如下所示的基于LCEL编写的管道。

python 复制代码
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser

prompt = ChatPromptTemplate.from_template("Give me small report about {topic}")
model = ChatOpenAI(
    model="gpt-5.2-chat",
    base_url="base url of your deployed model",
    api_key="your api key"
)
parser = StrOutputParser()

chain = prompt | model | parser
result = chain.invoke({"topic": "artificial intelligence"})
print(result)

我们无需任何封装链类型,而是直接使用类似于Unix管道命令的方式直接将三个核心组件组成一个链,简单且直观。关键是定制起来也容易,添加、替换和删除管道的组件都很方便。

3. 模拟实现

其实这种基于管道定义链路的实现很简单,它也不是LangChain特有,很多用于处理HTTP请求的Web框架都是采用这种设计。LangChain基于LCEL的链依赖一个名为Runnable的抽象类,它可以表示链路有个关键的组件,链本身也是一个Runnable对象,这是一种典型的"组合"设计模式。Runnable是本系列文章关注的核心,在正式介绍它之前,我们使用最简单的代码定义模拟它的实现。

python 复制代码
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import (Callable, Generic, TypeVar, Any)

Input = TypeVar('Input')
Output = TypeVar('Output')
Other = TypeVar('Other')

class Runnable[Input, Output](ABC):    
    @abstractmethod
    def invoke(self, input: Input) -> Output:
        pass
    def __or__(self, other: Runnable[Output, Other]) -> Runnable[Input, Other]:
        return ComposibleRunnable(self, other)
    
class ComposibleRunnable(Runnable[Input, Output]):
    def __init__(self, first: Runnable[Input, Any], second: Runnable[Any, Output]):
        self.first = first
        self.second = second
    def invoke(self, input: Input) -> Output: 
        return self.second.invoke(self.first.invoke(input))

我们将Runnable定义成抽象的泛型类,Input和Output类型变量表示它的输入和输出类型。既然命名为Runnable,意味着它可以被调用来完成某种任务,这体现在它的抽象方法invoke方法上,它具有一个类型为Input的参数。由于我们需要使用管道符号"|",所以我们将管道操作实现在重写的__or__方法中,另一个与之组合的类型为Runnable[Output, Other],两者组合的链条就是一个Runnable[Input, Other]对象。该方法具体返回的是一个ComposibleRunnable[Input, Other]对象。

如下这个RunnableLamda[Input,Output]类型是对Runnable[Input, Output]这个抽象类的实现,在它重写的invoke方法中,执行的操作是在构造函数中指定的Callable\[Input, Output]对象。我们创建了它的三个实例,分别针对三项基本数学运算:自增、倍乘和以2为底的对数。我们将它们以管道的形式构成一个链,并对传入的参数3实施运算。

python 复制代码
import math  
class RunnableLamda(Runnable,Generic[Input,Output]):
    def __init__(self, func: Callable[[Input], Output]):
        self.func = func
    def invoke(self, input: Input) -> Output:   
        return self.func(input)

increase = RunnableLamda(lambda x: x + 1)
double = RunnableLamda(lambda x: x * 2)
log2 = RunnableLamda(math.log2)

chain = increase | double | log2
result = chain.invoke(3)  
assert result == 3.0 # ((3 + 1) * 2) -> log2(8) -> 3.0
相关推荐
青 春 记 忆6 小时前
Dify Docker Compose 通用无损升级指南:从备份、双版本预演到切换与回滚
运维·人工智能·python·docker·容器
GlueNa2SiO36 小时前
03-Flask模板引擎Jinja2详解
笔记·python·flask
临沂GEO8 小时前
GEO搜索优化科普|正规地理位置流量运营入门指南
大数据·人工智能·python·流量运营
大模型码小白8 小时前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring
荷蒲10 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
数据狐(Datafox)11 小时前
京东商品列表API技术解析与落地应用(含标准 JSON 示例)
java·大数据·前端·人工智能·python·数据分析·json
量化吞吐机11 小时前
2026年下半年手工交易规则走向量化表达,产品该先接住哪一步
人工智能·python
Metaphor69212 小时前
使用 Python 读取和删除 Excel 文件属性
python·excel
迷迭香yy13 小时前
Python构建转融券多空识别系统从接口到因子的全流程 IG50免费开源股票数据API接口
开发语言·python·开源
淼澄研学13 小时前
LangChain结合Pydantic实现非结构化数据抽取实操教程
langchain