LangChain LCEL Chain 零基础入门指南
面向新手,由浅入深梳理 LCEL 链式开发 ,从概念、基础用法、进阶组件到实战避坑,全程配套可运行代码,循序渐进掌握 | 管道串联思维。
前置说明:本文统一使用硅基流动 + DeepSeek-V3 模型演示,所有示例环境配置统一,减少重复工作量;代码均经过精简优化,新手可直接复制运行。
一、开篇:为什么要学 Chain / LCEL?
1.1 传统写法的痛点
在接触 Chain 之前,我们调用大模型需要分步执行:拼接提示词 → 调用模型 → 解析结果,每一步都要手动调用方法,代码零散、复用性差。
传统分步示例(冗余写法)
Python
# 1. 拼接提示词
prompt_msg = prompt.invoke({"city": "北京"})
# 2. 调用大模型
llm_result = llm.invoke(prompt_msg)
# 3. 解析结果
final_data = parser.invoke(llm_result)
1.2 LCEL Chain 的优势
LCEL (LangChain Expression Language,LangChain 表达式语言)是官方推出的组件组合语法,核心是用 |(管道符)把多个功能组件拼接成一条流水线(Chain)。
改写后一行完成全流程:
Python
chain = prompt | llm | parser
final_data = chain.invoke({"city": "北京"})
核心价值:
-
代码简洁:多步逻辑合并为一条链路,可读性强;
-
统一接口 :整条链自带
invoke/stream/batch等方法,调用方式一致; -
灵活扩展:支持串行、并行、条件分支、自定义逻辑,适配复杂业务;
-
高复用性:一条链可当作独立组件,嵌套组合到其他链路中。
1.3 学完本文你能掌握
-
理解
Runnable、LCEL、Chain 核心概念; -
掌握最常用的串行链路(80% 业务场景);
-
学会并行、条件分支、数据透传、自定义函数四大进阶组件;
-
实现流式输出、批量处理、结构化数据解析;
-
避开新手高频报错,独立编写业务链路。
二、前置准备:环境统一配置
2.1 安装依赖
首先安装 LangChain 核心库与环境变量管理工具:
Bash
pip install langchain python-dotenv
2.2 环境变量配置
在项目根目录新建 .env 文件,填入你的硅基流动密钥和接口地址:
Plaintext
# .env 文件内容
SILICON_KEY=你的硅基API密钥
SILICON_BASE_URL=https://api.siliconflow.cn/v1
2.3 全局初始化 LLM(所有示例复用)
新建基础代码文件,统一初始化模型,后续所有示例无需重复写环境配置:
Python
# base_config.py 全局基础配置
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
# 加载 .env 环境变量
load_dotenv()
# 映射硅基接口为 OpenAI 兼容格式
os.environ["OPENAI_API_KEY"] = os.getenv("SILICON_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("SILICON_BASE_URL")
# 初始化 DeepSeek-V3 模型(全局通用)
llm = init_chat_model("openai:deepseek-ai/DeepSeek-V3")
后续所有代码,默认从
base_config导入llm。
三、核心概念:通俗解读(新手必看)
先搞懂 3 个核心术语,再写代码事半功倍:
| 术语 | 通俗解释 | 举例 |
|---|---|---|
| Runnable | LangChain 中所有可执行的组件 (流水线零件),统一支持 invoke/stream 等调用方法 |
提示词模板、大模型、结果解析器、自定义函数 |
| **` | ` 管道符** | 数据传输通道:前一个组件的输出 = 后一个组件的输入 |
| Chain(链) | 多个 Runnable 用 ` | ` 组合而成的完整流水线,本身也是 Runnable |
一句话总结:LCEL 就是用管道符,把一个个"功能零件"组装成一条自动化流水线。
四、第一阶段:基础串行链(入门核心,必掌握)
串行(RunnableSequence)是 LCEL 最基础、使用最多的形态:组件1 | 组件2 | 组件3,按顺序一步步执行。
标准通用结构:
Plaintext
提示词模板(ChatPromptTemplate) → 大模型(LLM) → 结果解析器(OutputParser)
4.1 最简串行链:文本问答
实现功能:传入问题 → 模型回答 → 输出纯文本。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1. 定义提示词模板(Runnable 组件)
prompt = ChatPromptTemplate.from_messages([
("system", "你是小学老师,用两三句话通俗解释知识点"),
("human", "请讲解:{question}"), # {question} 是占位符,调用时传参
])
# 2. 组装链路:提示词 → 模型 → 文本解析器
chain = prompt | llm | StrOutputParser()
# 3. 调用链:传入字典,key 对应模板占位符
result = chain.invoke({"question": "什么是数独?"})
# 输出结果(纯字符串)
print(result)
print("结果类型:", type(result))
代码解读
-
ChatPromptTemplate:拼接对话提示词,{question}是动态占位符; -
StrOutputParser:将模型返回的消息对象,转为普通字符串; -
chain.invoke(字典):字典的键必须和模板占位符一一对应,否则会报错; -
整条链路自动完成「填模板→调模型→转文本」全流程。
4.2 更换解析器:输出 JSON 格式
如果需要结构化数据(字典),只需替换最后一个解析器,链路主体不变。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
# 提示词模板(要求模型输出标准JSON)
prompt = ChatPromptTemplate.from_messages([
("system", "只输出JSON,不要多余文字,字段:name(城市名)、population(人口)"),
("human", "描述城市:{city}"),
])
# 链路:提示词 → 模型 → JSON解析器
chain = prompt | llm | JsonOutputParser()
# 调用
result = chain.invoke({"city": "北京"})
print(result)
print("结果类型:", type(result)) # <class 'dict'>
4.3 固定模板变量:partial 方法
如果提示词中有固定不变的内容 (如角色、规则),可以用 partial 提前绑定,调用时无需重复传参。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "你是{role},回答简洁明了"),
("human", "{question}"),
])
# 提前固定 role=数学老师,后续调用只需要传 question
fixed_prompt = prompt.partial(role="数学老师")
chain = fixed_prompt | llm | StrOutputParser()
# 多次调用,无需再传 role
print(chain.invoke({"question": "什么是余数?"}))
print(chain.invoke({"question": "什么是分数?"}))
4.4 生产级用法:结构化输出(Pydantic)
正式项目中,推荐使用 with_structured_output 替代传统解析器:结合 Pydantic 做强类型校验,自动解析+格式校验,稳定性更高。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
# 1. 用 Pydantic 定义数据结构(约束字段类型、说明)
class Animal(BaseModel):
name: str = Field(description="动物名称")
emoji: str = Field(description="对应表情符号")
age: int = Field(description="平均寿命")
# 2. 让模型绑定结构化输出(内部自动解析、校验)
structured_llm = llm.with_structured_output(Animal)
# 3. 组装链路(无需额外解析器)
prompt = ChatPromptTemplate.from_messages([
("system", "按照要求描述动物"),
("human", "介绍一种{type}动物"),
])
chain = prompt | structured_llm
# 调用
result = chain.invoke({"type": "家养宠物"})
print(result.model_dump()) # 转为字典输出
4.5 流式输出:实现聊天打字机效果
聊天界面、实时对话场景必备,使用 chain.stream() 逐块返回内容,模拟逐字输出。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "你是小学老师,通俗讲解知识点"),
("human", "讲解:{question}"),
])
chain = prompt | llm | StrOutputParser()
# 流式遍历输出(逐块打印)
print("回答:", end="")
for chunk in chain.stream({"question": "天空为什么是蓝色的?"}):
print(chunk, end="", flush=True)
4.6 批量处理:batch 批量执行任务
离线场景(批量翻译、批量摘要)使用 chain.batch(),一次性处理多个独立请求。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "用一句话解释知识点"),
("human", "{question}"),
])
chain = prompt | llm | StrOutputParser()
# 批量输入:多个独立请求列表
batch_inputs = [
{"question": "什么是比喻?"},
{"question": "什么是光合作用?"},
{"question": "什么是加减法?"},
]
# 批量调用
batch_results = chain.batch(batch_inputs)
# 打印所有结果
for idx, res in enumerate(batch_results, 1):
print(f"问题{idx}:{batch_inputs[idx-1]['question']}")
print(f"答案:{res}\n")
4.7 链路复用:封装为通用函数
Chain 本身是可复用组件,可封装成函数,在项目中反复调用。
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "你是{subject}老师,简短回答"),
("human", "{question}"),
])
chain = prompt | llm | StrOutputParser()
# 封装函数
def ask_teacher(subject: str, question: str) -> str:
return chain.invoke({"subject": subject, "question": question})
# 调用
print(ask_teacher("语文", "什么是成语?"))
print(ask_teacher("科学", "水为什么会结冰?"))
五、第二阶段:进阶组件(复杂业务必备)
掌握串行链后,学习 4 个高频进阶组件,解决并行执行、条件判断、数据透传、自定义逻辑四大场景。
5.1 并行执行 RunnableParallel
场景
同一份输入,同时执行多条链路(例如:同一个主题,同时生成笑话+诗歌),多条链路并发运行,最终结果整合为字典。
两种等价写法:
-
显式写法:
RunnableParallel(键=链路)(推荐新手); -
隐式写法:直接写字典
{"键": 链路},LCEL 自动识别为并行。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel
# 定义两条独立子链路
joke_chain = ChatPromptTemplate.from_template("写一个{topic}相关的冷笑话") | llm | StrOutputParser()
poem_chain = ChatPromptTemplate.from_template("写两句{topic}相关的短诗") | llm | StrOutputParser()
# 并行组装:key 为结果标识
parallel_chain = RunnableParallel(
joke=joke_chain,
poem=poem_chain
)
# 调用:输入共享给所有子链路
result = parallel_chain.invoke({"topic": "晚霞"})
# 按 key 取结果
print("冷笑话:", result["joke"])
print("短诗:", result["poem"])
5.2 条件分支 RunnableBranch
场景
类似代码中的 if/elif/else:根据输入内容,自动路由到不同链路(例如:用户问笑话走幽默链路,问知识走科普链路)。
语法规则
Python
RunnableBranch(
(条件函数1, 分支链路1),
(条件函数2, 分支链路2),
默认链路 # 所有条件都不满足时执行
)
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableBranch
# 1. 定义条件判断函数
def need_joke(input_dict: dict) -> bool:
"""判断用户是否想要笑话"""
question = input_dict["question"]
return any(word in question for word in ["笑话", "搞笑", "段子"])
def need_poem(input_dict: dict) -> bool:
"""判断用户是否想要诗歌"""
question = input_dict["question"]
return any(word in question for word in ["诗", "诗歌", "写一首"])
# 2. 定义不同分支的链路
joke_chain = ChatPromptTemplate.from_messages([
("system", "你是脱口秀演员,简短讲笑话"),
("human", "{question}")
]) | llm | StrOutputParser()
poem_chain = ChatPromptTemplate.from_messages([
("system", "你是诗人,写4行以内短诗"),
("human", "{question}")
]) | llm | StrOutputParser()
default_chain = ChatPromptTemplate.from_messages([
("system", "你是知识助手,通俗解答问题"),
("human", "{question}")
]) | llm | StrOutputParser()
# 3. 组装条件分支路由
router = RunnableBranch(
(need_joke, joke_chain),
(need_poem, poem_chain),
default_chain # 默认分支
)
# 测试不同问题
print(router.invoke({"question": "讲一个程序员的笑话"}))
print(router.invoke({"question": "写一首关于春天的诗"}))
print(router.invoke({"question": "地球是什么形状?"}))
5.3 数据透传 RunnablePassthrough
场景
核心作用:保留原有输入数据,同时追加新字段,是 RAG(检索增强生成)、多链路数据拼接的核心组件。
重点方法:RunnablePassthrough.assign(新字段=链路)
-
保留原始输入字典;
-
自动执行指定链路,将结果作为新 key 追加到字典中。
模拟 RAG 场景(检索+问答)
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
# 模拟检索器:根据问题返回参考资料(真实项目替换为向量库检索)
mock_retriever = RunnableLambda(lambda x: f"【参考资料】关于{x['question']}的科普内容")
# 组装链路:透传原有问题 + 追加检索结果 → 传入提示词
rag_chain = (
RunnablePassthrough.assign(docs=mock_retriever) # 新增字段 docs=检索结果
| ChatPromptTemplate.from_messages([
("system", "根据参考资料回答用户问题:{docs}"),
("human", "{question}")
])
| llm
| StrOutputParser()
)
# 调用:原始输入只有 question,链路自动追加 docs
result = rag_chain.invoke({"question": "数独是什么?"})
print(result)
5.4 自定义逻辑 RunnableLambda
场景
将普通 Python 函数转为 Runnable 组件,嵌入链路中,实现数据清洗、格式转换、外部接口调用等自定义逻辑。
完整代码
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
# 自定义处理函数:加工输入数据
def process_input(input_dict: dict) -> dict:
new_dict = dict(input_dict)
# 追加格式化字段
new_dict["subject_label"] = f"【{new_dict['subject']}】"
return new_dict
# 链路:自定义函数 → 提示词 → 模型 → 解析器
chain = (
RunnableLambda(process_input)
| ChatPromptTemplate.from_messages([
("system", "你是{subject_label}老师"),
("human", "讲解:{question}")
])
| llm
| StrOutputParser()
)
# 调用
result = chain.invoke({"subject": "数学", "question": "什么是几何图形?"})
print(result)
六、综合实战:多组件组合案例
真实业务中会嵌套组合多个组件,下面两个案例覆盖主流组合玩法。
案例1:并行生成 + 汇总总结(并行 + 透传 + 串行)
需求:输入一个主题,同时生成笑话和诗歌,再基于两者内容做总结。
Python
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 子链路
joke_chain = ChatPromptTemplate.from_template("{topic}的冷笑话") | llm | StrOutputParser()
poem_chain = ChatPromptTemplate.from_template("{topic}的两句短诗") | llm | StrOutputParser()
# 第一步:并行生成内容,并透传原始 topic
step1 = RunnablePassthrough.assign(
joke=joke_chain,
poem=poem_chain
)
# 第二步:汇总总结
summary_prompt = ChatPromptTemplate.from_messages([
("human", "主题:{topic}\n笑话:{joke}\n诗歌:{poem}\n简单总结一下内容")
])
# 完整链路
full_chain = step1 | summary_prompt | llm | StrOutputParser()
# 执行
print(full_chain.invoke({"topic": "大海"}))
案例2:智能路由问答(分支 + 串行)
基于用户输入自动分发到不同人设,前面分支示例已演示,可直接复用扩展。
七、新手高频踩坑&解决方案
整理开发中最常见的报错,按现象分类,快速排错:
| 报错现象 | 根因 | 解决方案 |
|---|---|---|
KeyError: xxx 找不到字段 |
invoke 传入的字典 key 和模板占位符不匹配 |
核对 {占位符} 和入参字典的键名,保持一致 |
输出是 AIMessage 对象,不是字符串 |
链路末尾缺少解析器 | 追加 StrOutputParser() / JsonOutputParser() |
| 流式输出打印对象而非文本 | 链路末尾是 LLM,无解析器 | 链路最后加上解析器;或手动取 chunk.content |
| 并行结果取值为空 | 并行链路输出是字典,直接打印会报错 | 必须通过 结果["key"] 取对应分支内容 |
| 分支永远走默认链路 | 条件函数返回值始终为 False |
打印输入内容,检查判断逻辑、关键词匹配规则 |
assign 后模板找不到新字段 |
assign 定义的字段名 和 模板 {字段} 不一致 |
统一字段名称 |
八、知识总结 & 学习路线
8.1 核心语法速查表
| 功能 | 写法 | 适用场景 |
|---|---|---|
| 串行执行 | `A | B |
| 并行执行 | RunnableParallel(k1=A, k2=B) / {k1:A, k2:B} |
一输入多输出 |
| 条件分支 | RunnableBranch((条件,链路), 默认链路) |
路由分发、多逻辑分支 |
| 数据透传 | RunnablePassthrough.assign(k=链路) |
RAG、追加字段、数据拼接 |
| 自定义逻辑 | RunnableLambda(函数) |
数据预处理、外部调用 |
8.2 解析器选型建议(生产环境)
-
纯文本输出:
StrOutputParser()(通用问答); -
JSON 字典输出:优先
llm.with_structured_output(Pydantic模型)(强校验),其次JsonOutputParser; -
复杂结构化数据:统一使用 Pydantic + with_structured_output。
8.3 新手循序渐进学习路线
-
吃透 串行链 (
prompt | llm | 解析器),掌握invoke/stream/batch/partial; -
练习
RunnableLambda嵌入自定义函数; -
学习
RunnableParallel并行链路; -
掌握
RunnablePassthrough.assign(为 RAG 打基础); -
最后学习
RunnableBranch条件分支; -
组合多个组件,开发综合业务链路。
九、附录:最小可运行完整代码
新建 demo.py,搭配 base_config.py 和 .env 文件,直接运行验证环境:
Python
# demo.py
from base_config import llm
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 基础问答链
prompt = ChatPromptTemplate.from_messages([
("system", "你是小学老师,简短回答问题"),
("human", "{question}")
])
chain = prompt | llm | StrOutputParser()
# 调用
res = chain.invoke({"question": "什么是人工智能?"})
print(res)