为什么 Runnable 会逐渐取代传统的 Chain
传统 Chain 基于继承封装流程,Runnable + LCEL 面向接口协议组合计算单元。本文讲清传统 Chain 的先天缺陷、Runnable 的核心改进与选型取舍。

如果你接触过早期 LangChain,一定写过 LLMChain、RetrievalQA。在新版本中,传统 Chain 被标记为废弃,Runnable + LCEL(LangChain Expression Language)成为整个框架底层标准。
很多人第一感觉只是语法变化:把 LLMChain(prompt, llm) 换成 prompt | llm | parser。但这远不止语法糖,是一次底层架构范式的重构。本文由浅入深讲清楚:传统 Chain 有什么先天缺陷、Runnable 解决了什么核心问题,以及二者的取舍。
澄清概念:
- 传统 Chain(Legacy Chain):LangChain 早期面向继承的类体系:LLMChain、SequentialChain、RetrievalQA;需要继承基类,重写
_call()方法实现自定义链路。 - Runnable:一套接口协议,所有组件(Prompt、LLM、Parser、检索器、自定义函数、甚至 LangGraph 图)实现同一套契约;通过管道符
|组合得到执行管道,等价于新版的“链”。 - LCEL:是组合 Runnable 的表达式语法,
|、RunnableParallel、RunnableBranch 都属于 LCEL 能力。
一、传统 Chain 的先天痛点
早期 Chain 设计思想:用面向类继承的方式封装工作流。当业务走向生产环境、复杂 Agent/RAG 场景,缺陷被持续放大。
1. 接口不统一,各组件调用方式割裂
旧版中每个组件拥有自己的调用入口:
- Prompt:
.format() - LLM:
.generate() - Parser:
.parse() - Chain:
.run()/.__call__()
想要串联一条流程,开发者必须手写大量适配胶水代码。自定义 Chain 需要继承 BaseChain 并重写私有方法,门槛高。
2. 流式、异步、批处理支持残缺
- 传统 Chain 流式能力依赖全局 Callbacks 回调,侵入业务代码
- 原生没有 ainvoke 异步、batch 批处理接口
- 想要拿到中间流式输出,需要写大量回调钩子,生产环境极易出错
3. 组合能力弱,很难实现并行、分支
- SequentialChain 只支持简单串行
- 想要并行执行多条子任务、条件分支路由,自定义成本极高
- 很难做到“链里面嵌入子链,子链再嵌套子链”的灵活组合
4. 输入输出强绑定,扩展性差
每一个 Chain 预先写死输入 key、输出 key;如果链路中间需要透传原始参数、追加中间变量,非常别扭。
5. 可观测性差,调试困难
回调系统是全局的,很难给单条链路附加独立 tags、metadata;LangSmith 追踪链路经常断裂,子步骤追踪不完整,排障困难。
6. 和后续生态割裂
后期官方推出 LangGraph,LangGraph 图本身是 Runnable;旧 Chain 无法直接作为 LangGraph 的节点,两套体系互不互通,生态分裂。
举一个直观对比代码
旧版 LLMChain:
from langchain.chains import LLMChain
chain = LLMChain(prompt=prompt, llm=llm)res = chain.run(topic="Runnable")新版 Runnable LCEL:
chain = prompt | llm | parserres = chain.invoke({"topic": "Runnable"})看起来只是写法不同,但底层执行模型完全不一样。
二、Runnable 协议带来的核心改进
Runnable 的核心思想:定义统一契约,万物皆可 Runnable。
只要实现 Runnable 接口,对象自动拥有这 5 套标准方法:
invoke():同步单次调用ainvoke():异步单次调用stream():同步流式输出astream():异步流式输出batch()/abatch():批量并发处理
1. 统一接口,消除组件之间的胶水代码
Prompt、大模型、解析器、检索器、普通 Python 函数(包装为 RunnableLambda)全部遵循同一套协议。用管道符 | 直接拼接,不用关心每个组件内部怎么调用。自定义逻辑不再需要继承类,普通函数套一层 RunnableLambda 即可接入链路。
2. 开箱即用流式、异步、批处理
整条链路任意节点,都可以直接调用 .stream() 拿到逐段输出;异步接口原生支持高并发后端服务;batch 内置并发控制,不用自己维护线程池。不需要写任何回调代码,能力是协议自带的。
3. 极强的可组合性:串行、并行、条件分支
|管道:顺序执行(RunnableSequence)- RunnableParallel:同时并行执行多条分支
- RunnableBranch:条件路由,根据输入选择不同子链路
Runnable 可以无限嵌套:一条 Runnable 可以作为另一条 Runnable 内部的子单元。
RAG 典型示例:
rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser())4. 配置透传:RunnableConfig 统一上下文
通过 config 参数统一传递回调、标签、元数据、超时、重试、会话 ID。每一段子 Runnable 都可以拿到配置,方便日志、监控、LangSmith 追踪,链路完整不中断。
5. 和 LangGraph 生态打通
LangGraph 编译后的 Graph 本身就是 Runnable 实例。你可以把 LCEL 写好的 Runnable 直接作为图中的节点;也可以把 Graph 嵌入 LCEL 管道。实现简单链路用 LCEL,复杂状态机、多轮 Agent 交给 LangGraph,两套体系无缝互通。
6. 更好的可测试性
每一个 Runnable 单元都可以独立执行 invoke() 做单元测试,不用 mock 整条链路。调试时可以在链路中间插入 RunnableLambda 打印中间变量。
三、新旧体系对比表
| 维度 | Legacy Chain(传统链) | Runnable + LCEL |
|---|---|---|
| 编程模型 | 继承 BaseChain,重写 _call | 接口协议,管道符组合;普通函数包装即可接入 |
| 调用入口 | run() / call() | 统一 invoke/ainvoke/stream/astream/batch |
| 流式支持 | 依赖全局 callback,侵入高 | 原生支持整条链路流式输出 |
| 异步能力 | 有限支持,需要手动处理 | 原生完整异步 |
| 并行 / 分支 | 实现复杂,需要大量自定义 | 原生提供 RunnableParallel / RunnableBranch |
| 可观测性 | 全局回调,链路容易断裂 | RunnableConfig 携带 tags、metadata,LangSmith 完整追踪 |
| 生态兼容 | 无法嵌入 LangGraph | 可直接作为 LangGraph 节点,双向嵌入 |
| 自定义成本 | 高,需要继承重写类 | 低,RunnableLambda 包装普通 Python 函数 |
四、常见误区澄清
误区 1:Runnable 只是语法糖,底层还是 Chain
不完全对。 旧 Chain 是业务类;Runnable 是一套通用接口契约。LCEL 组合出来的 RunnableSequence 可以理解为新版 Chain,但它不是通过继承实现,而是组合模式,设计理念完全不同。官方不再鼓励手写继承 BaseChain。
误区 2:用 Runnable 之后,就不需要 LangGraph
恰恰相反。
- LCEL(Runnable):适合无状态、DAG 式管道,适合 RAG、简单抽取、摘要;不适合循环、状态回写、人工介入 HITL
- LangGraph:适合带状态、循环、多步 Agent、人工审核;编译后的 Graph 本身就是 Runnable,可以嵌入 LCEL 链路
二者互补,不是替代关系。简单管道写 LCEL;有状态循环交给 LangGraph。
误区 3:老项目必须全部立刻迁移
官方没有强制瞬间迁移,只是标记旧 API Deprecation。存量 Chain 还可以运行;新项目直接优先使用 Runnable LCEL。长期维护的项目建议逐步迁移,旧版 API 未来版本会移除。
五、什么时候选择什么方案
✅ 使用 Runnable (LCEL)
- RAG、文档摘要、结构化抽取、简单多步无状态工作流
- 需要流式输出、后端高并发异步服务
- 需要单元测试各个子步骤
- 后续有可能接入 LangGraph
❌ LCEL 不擅长
- 需要循环执行、保存会话状态、人工介入 HITL
- 复杂 Agent 思考循环
这类场景直接上 LangGraph。
六、写在最后
传统 Chain 的问题不是“不好用”,而是它诞生于大模型应用早期,只解决简单串联场景。当 LLM 应用走向生产,流式、并发、并行分支、可观测、和状态图编排生态互通成为硬性需求,基于继承的 Chain 架构就遇到天花板。
- 传统 Chain:面向类继承,封装一套完整业务流程
- Runnable:面向接口协议,把每一步计算变成可自由拼接的积木
Runnable 取代传统 Chain,本质是一次架构升级:从面向“流程封装”转向面向“可组合的计算单元”,为后续 LangGraph、复杂 Agent 生态打下统一底层基础。


