LCEL(LangChain Expression Language)语法全解
LCEL 是 LangChain 推出的声明式链式编排语法 ,基于统一的 Runnable 接口标准,通过管道符 | 拼接提示词、大模型、输出解析器、工具等组件,构建完整的大模型应用链路。它替代了早期 LLMChain、SequentialChain 等面向对象的繁琐写法,是当前 LangChain 构建应用的官方标准范式。

一、核心底层原理
- 统一 Runnable 接口
LangChain 中所有可执行组件(提示词、模型、解析器、工具、自定义函数)都实现了Runnable基类,拥有完全一致的调用规范,这是组件可以自由拼接的底层基础。 - 管道符
|语法糖
它本质是对 Python__or__方法的重载,作用是将左侧组件的输出,自动作为右侧组件的输入,和 Unix Shell 的管道逻辑完全一致,数据流严格从左向右流动。
二、基础语法:标准三段式链路
最经典、最常用的 LCEL 结构是「提示词模板 → 大模型 → 输出解析器」,对应「填模板→调用模型→解析结果」的标准流程。
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 1. 定义单个组件
prompt = ChatPromptTemplate.from_template("请用通俗语言解释{concept},不超过100字")
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
parser = StrOutputParser()
# 2. LCEL 管道拼接成链
chain = prompt | model | parser
# 3. 统一调用
result = chain.invoke({"concept": "LCEL"})
print(result)
执行逻辑:
- 输入字典
{"concept": "LCEL"}传入提示词模板,填充变量生成完整 Prompt - 填充后的 Prompt 自动传入大模型,生成 AI 消息对象
- 消息对象传入字符串解析器,提取纯文本内容作为最终结果
三、统一调用方法
所有 LCEL 链都共享同一套调用接口,无需针对不同组件修改写法:
| 调用方法 | 说明 | 适用场景 |
|---|---|---|
.invoke(input) |
同步单次调用,阻塞等待完整结果 | 常规问答、短文本生成 |
.stream(input) |
流式逐块返回结果,无需等待全文 | 聊天界面、长文本生成 |
.batch([input1, input2]) |
批量并发处理多个输入 | 批量数据处理、批量生成 |
.ainvoke(input) |
异步单次调用 | 高并发 Web 服务 |
.astream(input) |
异步流式调用 | 异步聊天接口 |
四、常用基础组件
1. 输入透传:RunnablePassthrough
原样保留输入内容,常用于并行链路中传递原始参数,是 RAG 等场景的必备组件。
python
from langchain_core.runnables import RunnablePassthrough
# 原始输入会被原样透传
chain = RunnablePassthrough() | (lambda x: f"收到:{x}")
print(chain.invoke("你好"))
# 输出:收到:你好
2. 自定义逻辑:RunnableLambda
将普通 Python 函数包装为 Runnable 组件,无缝接入管道,实现自定义数据清洗、格式转换等逻辑。
python
from langchain_core.runnables import RunnableLambda
def clean_text(text):
return text.strip().replace("\n", "")
# 自定义清洗 → 提示词 → 模型 → 解析器
chain = RunnableLambda(clean_text) | prompt | model | parser
3. 结构化输出解析
将模型输出转为结构化数据,三类最常用的解析器:
StrOutputParser:提取模型输出的纯文本,日常开发最常用JsonOutputParser:将模型输出解析为字典/JSON 格式PydanticOutputParser:强制输出符合 Pydantic 定义的数据结构
五、进阶编排语法
1. 并行执行:RunnableParallel
同时运行多个独立子链路,最终将所有分支结果合并为字典返回,常用于 RAG、多维度分析场景。支持两种写法,字典简写更常用:
python
# 写法1:字典简写(LCEL 会自动转为 RunnableParallel)
parallel_chain = {
"双倍": lambda x: x["num"] * 2,
"加十": lambda x: x["num"] + 10
}
print(parallel_chain.invoke({"num": 5}))
# 输出: {'双倍': 10, '加十': 15}
典型 RAG 场景用法:
python
# 检索分支 + 原始问题透传,两路结果一同传给提示词
rag_chain = {
"context": retriever, # 分支1:检索知识库返回上下文
"question": RunnablePassthrough() # 分支2:保留原始用户问题
} | prompt | model | parser
2. 条件分支:RunnableBranch
根据输入内容动态选择执行链路,实现路由逻辑,类似代码里的 if-elif-else 结构。
python
from langchain_core.runnables import RunnableBranch
route_chain = RunnableBranch(
(lambda x: x["type"] == "数学", lambda x: f"数学题:{x['question']}"),
(lambda x: x["type"] == "语文", lambda x: f"语文题:{x['question']}"),
lambda x: f"通用问题:{x['question']}" # 默认兜底分支
)
3. 容错降级:with_fallbacks
给主链路添加备用方案,主链路调用失败时自动切换到备用链路,是生产环境提升稳定性的标准手段。
python
main_model = ChatOpenAI(model="gpt-4")
backup_model = ChatOpenAI(model="gpt-3.5-turbo")
# 主模型调用失败时,自动走备用模型
reliable_chain = prompt | main_model.with_fallbacks([backup_model]) | parser
4. 工具调用绑定
通过 bind_tools 将工具函数绑定到模型,让模型可以自主判断是否调用工具、生成调用参数。
python
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气"""
return f"{city}今日晴,气温25℃"
# 绑定工具到模型
model_with_tools = model.bind_tools([get_weather])
# 接入管道
tool_chain = prompt | model_with_tools
六、核心优势总结
- 声明式、可读性强:代码即数据流,从左到右一目了然,比嵌套类写法更易维护
- 组件高度解耦:所有组件遵循统一标准,可自由替换、组合,无需修改链路代码
- 原生工程能力:零额外开发,天然支持流式、批量、异步、容错降级
- 可观测性友好:无缝对接 LangSmith 调试平台,自动追踪每一步执行状态
- 生产级适配:可直接通过 LangServe 部署为 API 接口,快速落地
python
import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
# ===================== 1. 基础配置 =====================
# 替换为自己的 API Key;兼容所有 OpenAI 协议的模型
os.environ["OPENAI_API_KEY"] = "sk-你的API密钥"
# 如需使用本地/第三方兼容模型,可配置代理地址
# os.environ["OPENAI_BASE_URL"] = "https://你的接口地址/v1"
# ===================== 2. 准备知识库文档 =====================
# 模拟业务知识库,可替换为 PDF/Word/网页等读取的文本内容
raw_knowledge = """
MLCC(片式多层陶瓷电容器)是被动元件的核心品类,被称为"电子大米",核心作用是稳压、滤波、储能,广泛应用于消费电子、汽车电子、AI服务器等领域。
MLCC的核心上游材料是陶瓷粉体、金属内电极浆料,国内主流厂商包括风华高科、三环集团、火炬电子等。
PCB(印制电路板)是电子元器件的承载基板,负责电气连接,是所有电子设备的基础部件,被称为"电子系统之母"。
PCB按层数可分为单面板、双面板、多层板,高阶HDI、封装载板是当前高端PCB的核心方向,国内主流厂商包括沪电股份、深南电路、鹏鼎控股。
CPO(共封装光学)是一种光封装技术,将光引擎与交换芯片共同封装在同一块基板上,可大幅降低功耗、提升传输密度,是AI高速互联的核心技术路线。
CPO产业链中,PCB作为高速承载基板,规格要求显著升级;MLCC作为电源滤波元件,单机用量随功耗提升而增加。
"""
# 文档切片:将长文本切分为适合检索的小块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200, # 单块最大字符数
chunk_overlap=30, # 块间重叠字符数,避免语义断裂
separators=["\n\n", "\n", "。", " "] # 优先按语义分割
)
doc_splits = text_splitter.create_documents([raw_knowledge])
# ===================== 3. 构建向量库与检索器 =====================
# 使用 Chroma 内存向量库,无需额外安装数据库;如需持久化可加 persist_directory 参数
vectorstore = Chroma.from_documents(
documents=doc_splits,
embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
# 创建检索器,召回最相关的 Top 3 文档片段
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 工具函数:将检索到的文档对象拼接为纯文本,供大模型读取
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# ===================== 4. 定义提示词与模型组件 =====================
# RAG 专用提示词模板,强制基于上下文回答,抑制幻觉
rag_prompt = ChatPromptTemplate.from_template("""
你是专业的电子行业知识库助手,仅可基于下方提供的上下文回答问题。
如果上下文中没有对应答案,请直接回答「知识库中暂无相关信息」,禁止编造内容。
【参考上下文】
{context}
【用户问题】
{question}
【正式回答】
""")
# 初始化大模型与输出解析器
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
output_parser = StrOutputParser()
# ===================== 5. 核心:LCEL 搭建 RAG 链路 =====================
rag_chain = (
# 并行执行两路分支,最终合并为字典传入提示词
{
"context": retriever | RunnableLambda(format_docs), # 分支1:检索+格式化上下文
"question": RunnablePassthrough() # 分支2:原样透传用户原始问题
}
| rag_prompt # 填充提示词模板
| llm # 调用大模型生成答案
| output_parser # 提取纯文本结果
)
# ===================== 6. 调用测试 =====================
if __name__ == "__main__":
# 方式1:同步阻塞调用,一次性返回完整结果
question_1 = "MLCC和PCB在CPO产业链中分别有什么作用?"
result = rag_chain.invoke(question_1)
print("=== 同步调用结果 ===")
print(result)
print("\n" + "-"*60 + "\n")
# 方式2:流式调用,逐字返回,适配聊天界面场景
question_2 = "国内MLCC的主流厂商有哪些?"
print("=== 流式调用结果 ===")
for chunk in rag_chain.stream(question_2):
print(chunk, end="", flush=True)