LCEL 全称 LangChain Expression Language,是 LangChain 推出的组件编排表达式语法,依托 Python 原生语法 + Runnable 统一协议,用 | 管道符串联组件,是目前官方主推的链路构建方案,替代传统 LLMChain

本文从设计理念、底层原理、核心语法、常用组件、实战用法、特性、新旧对比、最佳实践全面讲解。


一、基础认知

1. 核心定位

LCEL 不是独立编程语言,而是一套基于 Python 的语法规范 + 组件的组合范式

  • 目标:用极简、可读的表达式搭建大模型应用工作流
  • 核心符号:管道符 |,表达「数据从左向右流转」;
  • 底层底座:所有组件都实现 Runnable 协议,统一调用、串联规则。

2. 设计目标

  1. 代码即流程图A | B | C 直观体现数据流向,可读性极强;
  2. 统一执行模型同步调用、流式输出、批量推理复用同一条链路;
  3. 低侵入扩展:普通函数、第三方逻辑可轻松接入;
  4. 原生可观测:天然支持 LangSmith 链路追踪、回调、日志、异常捕获
  5. 灵活组合:支持串行、并行、分支、嵌套、条件路由等复杂流程。

3. 适用场景

问答、文本处理、信息抽取、结构化输出、Agent 工具调用、复杂业务工作流等几乎所有 LangChain 应用场景。


二、底层核心原理

1. 两大技术基石

(1)Runnable 统一协议

LCEL 所有可串联组件(Prompt、LLM、解析器、工具、自定义逻辑)都继承自 Runnable 基类,强制实现三套标准执行方法

方法 作用
invoke(input) 单次同步调用,返回完整结果
stream(input) 流式逐块返回(对话打字机效果)
batch(inputs) 批量处理一组输入

所有组件对外接口完全一致,所以能自由拼接

(2)Python 运算符重载

Python 原生 a | b 本意是按位或 / 集合并集;LangChain 重写了 Runnable 的魔法方法 __or__

python

运行

# 语法
组件A | 组件B
# 底层等价调用
组件A.__or__(组件B)

执行逻辑:

  1. 生成一个新的组合 RunnableRunnableSequence 串行链);
  2. 运行时左组件输出 → 右组件输入,自动完成数据流转。

2. 数据流转规则(核心约定)

LCEL 定义了通用数据流转格式,保证组件无缝拼接

  1. 标准输入:Python 字典 dict(键对应模板变量);
  2. Prompt 接收字典 → 渲染为消息列表
  3. LLM 接收消息列表 → 输出消息对象;
  4. 解析器接收消息对象 → 转为字符串 / 字典 / 自定义对象;
  5. 自定义组件按需转换格式。

格式不兼容时,使用 RunnablePassthrough / RunnableLambda / RunnableMap 做适配。


三、LCEL 基础语法与书写规范

1. 基础串行语法(最常用)

python

运行

组件1 | 组件2 | 组件3 | ...

执行顺序:从左到右,上一个输出作为下一个输入。

标准通用链路模板:

python

运行

# 提示词 → 大模型 → 输出解析器
chain = prompt | llm | parser

2. 换行书写规范

链路较长时,用括号包裹实现换行,不影响语法:

python

运行

chain = (
    prompt
    | llm
    | StrOutputParser()
)

3. 调用执行语法

三条统一入口,链路写法完全不变:

python

运行

# 1. 单次调用
result = chain.invoke(输入字典)

# 2. 流式输出
for chunk in chain.stream(输入字典):
    print(chunk, end="")

# 3. 批量调用
results = chain.batch([字典1, 字典2, ...])

四、LCEL 核心内置组件分类

按功能划分,覆盖绝大多数开发场景,附作用与示例。

环境依赖与公共导入

bash

运行

pip install langchain langchain-openai

python

运行

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.output_parsers import (
    StrOutputParser,
    JsonOutputParser,
    PydanticOutputParser
)
from langchain_core.runnables import (
    RunnableLambda,        # 包装普通函数
    RunnablePassthrough,    # 字段透传、赋值
    RunnableMap             # 并行分支
)
from pydantic import BaseModel, Field

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

类别 1:提示词组件(Runnable)

PromptTemplate / ChatPromptTemplate

  • 作用:接收字典变量,渲染成模型可识别的提示词 / 消息列表;
  • 是链路起点

示例:

python

运行

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是专业助手"),
    ("user", "问题:{question}")
])

类别 2:大模型组件(Runnable)

ChatOpenAI / 各类 LLM 封装

  • 作用:接收提示词 / 消息,调用模型生成回复;
  • 位于链路中间。

类别 3:输出解析器(Runnable)

统一将模型消息对象转为业务可用数据,实现结构化输出

  1. StrOutputParser:转为普通字符串(最常用)
  2. JsonOutputParser:转为 Python 字典(JSON 结构化)
  3. PydanticOutputParser:强类型对象(企业级严格约束)

示例:

python

运行

# 基础字符串解析
chain = prompt | llm | StrOutputParser()

# JSON 解析
chain = prompt | llm | JsonOutputParser()

类别 4:Runnable 工具组件(链路数据控制核心)

1. RunnableLambda:包装普通 Python 函数

普通函数不是 Runnable,无法直接用 | 串联,通过它完成适配。

python

运行

# 自定义函数
def handle_text(text: str) -> str:
    return "【结果】" + text

# 包装为 Runnable
func_run = RunnableLambda(handle_text)

# 接入管道
chain = prompt | llm | StrOutputParser() | func_run
2. RunnablePassthrough:字段透传 & 动态赋值

两大核心用法:

  1. 原样透传:保留原始输入字段;
  2. .assign():基于原有字段,新增计算字段。

python

运行

prompt = ChatPromptTemplate.from_template("总结内容:{content}")

# 透传原文 + 新增总结字段
chain = (
    RunnablePassthrough.assign(
        summary = prompt | llm | StrOutputParser()
    )
)

res = chain.invoke({"content": "LCEL 是 LangChain 管道语法"})
# 同时包含 content、summary 两个字段
print(res)
3. RunnableMap:并行分支执行

将同一个输入分发到多个子链路并行执行,最后汇总所有结果,适合多任务同时处理。

python

运行

# 子链路1:总结
chain_sum = prompt | llm | StrOutputParser()
# 子链路2:关键词
prompt_kw = ChatPromptTemplate.from_template("提取关键词:{content}")
chain_kw = prompt_kw | llm | StrOutputParser()

# 并行组合
parallel_chain = RunnableMap({
    "原文": RunnablePassthrough(),
    "总结": chain_sum,
    "关键词": chain_kw
})

res = parallel_chain.invoke({"content": "大模型应用开发"})
print(res)

五、LCEL 典型实战链路示例

示例 1:标准问答链路(最基础)

python

运行

prompt = ChatPromptTemplate.from_messages([
    ("user", "请回答:{q}")
])

# LCEL 管道
chain = prompt | llm | StrOutputParser()

res = chain.invoke({"q": "什么是 LCEL?"})
print(res)

示例 2:JSON 结构化输出链路

python

运行

prompt = PromptTemplate(
    input_variables=["info"],
    template="解析信息,只返回 JSON,字段:name, age \n 内容:{info}"
)

chain = prompt | llm | JsonOutputParser()
data = chain.invoke({"info": "小王,24岁"})
print(type(data), data["name"])

示例 3:嵌套组合(透传 + 自定义函数)

python

运行

def upper_str(s):
    return s.upper()

run_upper = RunnableLambda(upper_str)

chain = (
    RunnablePassthrough()
    | ChatPromptTemplate.from_template("简述:{topic}")
    | llm
    | StrOutputParser()
    | run_upper
)

res = chain.invoke({"topic": "Python 运算符重载"})
print(res)

示例 4:流式输出链路

链路写法不变,仅调用 stream

python

运行

chain = prompt | llm | StrOutputParser()

for chunk in chain.stream({"q": "详细介绍 LangChain"}):
    print(chunk, end="", flush=True)

六、LCEL 高级能力

1. 链路复用与组合

已定义的 chain 本身也是 Runnable,可以继续拼接、嵌套:

python

运行

# 基础链
base_chain = prompt | llm | StrOutputParser()
# 在基础链后继续追加逻辑
full_chain = base_chain | RunnableLambda(lambda x: f"最终答案:{x}")

2. 参数绑定 bind()

提前为组件绑定固定参数,运行时无需重复传入:

python

运行

# 绑定温度、模型等参数
fixed_llm = llm.bind(temperature=0.2)
chain = prompt | fixed_llm | StrOutputParser()

3. 配置与回调 with_config()

附加追踪、回调、超时、标签,对接 LangSmith 监控:

python

运行

chain = prompt | llm | StrOutputParser()
# 添加链路名称、标签
chain_with_config = chain.with_config(
    run_name="问答链路",
    tags=["demo", "lcel"]
)
chain_with_config.invoke({"q": "测试"})

4. 条件分支(结合 Python 流程控制)

LCEL 本身不内置 if,但可和 Python 原生流程控制结合,实现分支路由:

python

运行

input_data = {"type": "总结", "content": "测试文本"}

# 定义两条分支
chain_sum = prompt | llm | StrOutputParser()
chain_kw = prompt_kw | llm | StrOutputParser()

# 原生 if 做分支选择
if input_data["type"] == "总结":
    select_chain = chain_sum
else:
    select_chain = chain_kw

select_chain.invoke(input_data)

七、LCEL vs 传统 LLMChain 对比

对比维度 传统 LLMChain LCEL 管道
语法风格 面向对象实例化,代码偏冗长 表达式风格,` ` 串联,简洁直观
多步组合 需手动取值、传参,嵌套繁琐 直接链式拼接,自动流转数据
流式输出 支持差,需额外改造 原生 stream 完美支持
批量执行 手动循环 原生 batch 方法
并行能力 无原生支持 RunnableMap 原生并行
自定义函数 侵入性强,需继承类 RunnableLambda 轻量包装
可观测性 弱,调试困难 原生对接 LangSmith,全链路追踪
维护成本 复杂链路难以维护 线性结构,易拆解、迭代
官方态度 兼容旧项目,不再主推 官方标准,新项目首选

八、LCEL 核心优缺点

优点

  1. 语法极简:代码和业务流向高度一致,上手快;
  2. 统一运行模式:同步 / 流式 / 批量一套链路复用;
  3. 组合能力极强:串行、并行、嵌套、分支灵活搭建;
  4. 工程化完善:监控、回调、异常、日志原生支持;
  5. 生态统一LangChain 所有组件、Agent、工具全面兼容。

缺点

  1. 依赖 Python 运算符重载,新手需要理解 Runnable 底层逻辑;
  2. 超复杂分支 / 循环场景,单纯表达式可读性下降,需配合 Python 原生流程控制;
  3. 老旧项目仍大量使用 LLMChain,历史代码迁移需要适配。

九、适用场景与最佳实践

✅ 推荐使用 LCEL

  1. 新项目、快速原型开发;
  2. 需要流式对话、前端交互的应用;
  3. 多组件串联、包含自定义函数 / 数据处理的链路;
  4. 线上生产项目,需要监控、追踪、运维;
  5. 信息抽取、结构化输出、Agent 工具调用。

❌ 极简场景可简化

仅单次调用 LLM、无后续处理:直接使用 llm.invoke(),无需构建管道。

最佳实践

  1. 标准链路统一格式:prompt | llm | parser
  2. 长链路用括号换行,提升可读性;
  3. 自定义函数统一使用 RunnableLambda 包装;
  4. 多字段、数据保留场景优先使用 RunnablePassthrough
  5. 并行任务使用 RunnableMap,不手动拼接多链;
  6. 线上项目务必配合 with_config 打标签,对接 LangSmith 排查问题。

十、总结(核心考点速记)

  1. 本质:LCEL 是 LangChain 基于 Runnable 协议 + Python 运算符重载实现的组件编排表达式
  2. 核心符号| 管道符,代表数据从左向右流转;
  3. 三大执行方法invoke(单次)、stream(流式)、batch(批量);
  4. 核心辅助组件
    • RunnableLambda:接入普通函数
    • RunnablePassthrough:字段透传、新增字段
    • RunnableMap:并行分支
  5. 定位:LangChain 新一代标准开发范式,替代传统 LLMChain,是大模型工程化落地的核心语法。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐