[智能体-187]:LCEL(LangChain Expression Language)完整详解
python运行chain = (| ChatPromptTemplate.from_template("简述:{topic}")| llmres = chain.invoke({"topic": "Python 运算符重载"})print(res)本质:LCEL 是 LangChain 基于Runnable协议 + Python 运算符重载实现的组件编排表达式;核心符号管道符,代表数据从左向右流
LCEL 全称 LangChain Expression Language,是 LangChain 推出的组件编排表达式语法,依托 Python 原生语法 + Runnable 统一协议,用 | 管道符串联组件,是目前官方主推的链路构建方案,替代传统 LLMChain。
本文从设计理念、底层原理、核心语法、常用组件、实战用法、特性、新旧对比、最佳实践全面讲解。
一、基础认知
1. 核心定位
LCEL 不是独立编程语言,而是一套基于 Python 的语法规范 + 组件的组合范式。
- 目标:用极简、可读的表达式搭建大模型应用工作流;
- 核心符号:管道符
|,表达「数据从左向右流转」; - 底层底座:所有组件都实现
Runnable协议,统一调用、串联规则。
2. 设计目标
- 代码即流程图:
A | B | C直观体现数据流向,可读性极强; - 统一执行模型:同步调用、流式输出、批量推理复用同一条链路;
- 低侵入扩展:普通函数、第三方逻辑可轻松接入;
- 原生可观测:天然支持 LangSmith 链路追踪、回调、日志、异常捕获;
- 灵活组合:支持串行、并行、分支、嵌套、条件路由等复杂流程。
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)
执行逻辑:
- 生成一个新的组合
Runnable(RunnableSequence串行链); - 运行时左组件输出 → 右组件输入,自动完成数据流转。
2. 数据流转规则(核心约定)
LCEL 定义了通用数据流转格式,保证组件无缝拼接:
- 标准输入:Python 字典
dict(键对应模板变量); - Prompt 接收字典 → 渲染为消息列表;
- LLM 接收消息列表 → 输出消息对象;
- 解析器接收消息对象 → 转为字符串 / 字典 / 自定义对象;
- 自定义组件按需转换格式。
格式不兼容时,使用 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)
统一将模型消息对象转为业务可用数据,实现结构化输出。
StrOutputParser:转为普通字符串(最常用)JsonOutputParser:转为 Python 字典(JSON 结构化)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:字段透传 & 动态赋值
两大核心用法:
- 原样透传:保留原始输入字段;
.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 核心优缺点
优点
- 语法极简:代码和业务流向高度一致,上手快;
- 统一运行模式:同步 / 流式 / 批量一套链路复用;
- 组合能力极强:串行、并行、嵌套、分支灵活搭建;
- 工程化完善:监控、回调、异常、日志原生支持;
- 生态统一:LangChain 所有组件、Agent、工具全面兼容。
缺点
- 依赖 Python 运算符重载,新手需要理解
Runnable底层逻辑; - 超复杂分支 / 循环场景,单纯表达式可读性下降,需配合 Python 原生流程控制;
- 老旧项目仍大量使用
LLMChain,历史代码迁移需要适配。
九、适用场景与最佳实践
✅ 推荐使用 LCEL
- 新项目、快速原型开发;
- 需要流式对话、前端交互的应用;
- 多组件串联、包含自定义函数 / 数据处理的链路;
- 线上生产项目,需要监控、追踪、运维;
- 信息抽取、结构化输出、Agent 工具调用。
❌ 极简场景可简化
仅单次调用 LLM、无后续处理:直接使用 llm.invoke(),无需构建管道。
最佳实践
- 标准链路统一格式:
prompt | llm | parser; - 长链路用括号换行,提升可读性;
- 自定义函数统一使用
RunnableLambda包装; - 多字段、数据保留场景优先使用
RunnablePassthrough; - 并行任务使用
RunnableMap,不手动拼接多链; - 线上项目务必配合
with_config打标签,对接 LangSmith 排查问题。
十、总结(核心考点速记)
- 本质:LCEL 是 LangChain 基于
Runnable协议 + Python 运算符重载实现的组件编排表达式; - 核心符号:
|管道符,代表数据从左向右流转; - 三大执行方法:
invoke(单次)、stream(流式)、batch(批量); - 核心辅助组件:
RunnableLambda:接入普通函数RunnablePassthrough:字段透传、新增字段RunnableMap:并行分支
- 定位:LangChain 新一代标准开发范式,替代传统
LLMChain,是大模型工程化落地的核心语法。
更多推荐
所有评论(0)