LangChain LCEL学习笔记
本来第三章应该学一下Tools和Agent的,后来发现这两个东西是核心,越学东西越多,于是就让G老师帮忙重新列了一下学习路线,按照G老师的路线,这一段应该学习LCEL相关的信息。回归正题,本文将带您深入探索 LCEL 与 Runnable 接口的奥秘。
1. 引言:从"链"说起
1.1 为什么要了解 LCEL?
在 LangChain 的世界里,有一个词贯穿始终------Chain(链)。
想象一下:用户输入一段文字,你需要先做情感分析,再根据情感结果选择不同的回复策略,最后将结果格式化输出。如果不用 LCEL,你可能需要写一堆嵌套函数、回调逻辑,代码像意大利面条一样纠缠不清。
但有了 LCEL,一切变得清晰优雅:
python
chain = (
sentiment_prompt # 第一步:情感分析
| llm # 第二步:LLM 处理
| output_parser # 第三步:格式化输出
)
这就是 LCEL 的魅力所在------用管道操作符声明式地组合数据流,让复杂的 AI 工作流变得像搭积木一样简单。
1.2 LCEL 的核心优势
LCEL(LangChain Expression Language)不仅仅是一个语法糖,它为 LangChain 带来了质的飞跃:
| 优势 | 说明 |
|---|---|
| 声明式 | 用管道操作符清晰表达数据流,阅读代码如读业务流程 |
| 组合性 | 轻松组合提示词、模型、输出解析器构建复杂链 |
| 流式支持 | 原生支持流式输出,用户体验更佳 |
| 并行执行 | 支持批量和并行处理,性能与效率兼得 |
| 错误处理 | 内置错误处理机制,链更健壮 |
| 追踪支持 | 与 LangSmith 无缝集成,调试不再抓狂 |
2. LCEL 基础:一切的开始
2.1 核心概念:Runnable 接口
LCEL 的核心是 Runnable 接口。你可以把它想象成 LangChain 世界的"统一身份证"------无论是提示词模板、LLM 模型,还是输出解析器,都实现了这个接口。
这意味着它们都有共同的方法:
python
# 基本调用
chain.invoke(input) # 同步调用
chain.batch(inputs) # 批量调用
chain.stream(input) # 流式调用
# 异步调用
await chain.ainvoke(input) # 异步调用
await chain.abatch(inputs) # 异步批量调用
async for chunk in chain.astream(input): # 异步流式调用
这就是 Runnable 的核心方法三剑客:invoke、batch、stream。无论你组合什么样的链,都用这几种方式调用。
2.2 第一个 LCEL 链
让我们用一个完整的例子来感受 LCEL 的魅力:
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 初始化模型
base_url = "https://api.minimax.chat/v1"
api_key = "your-api-key"
model = ChatOpenAI(
model="MiniMax-M2.7",
base_url=base_url,
api_key=api_key,
)
# 构建链
prompt = ChatPromptTemplate.from_template("用{language}翻译:{text}")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
# 调用链
result = chain.invoke({
"language": "中文",
"text": "Hello, World!"
})
print(result) # 你好,世界!
这就是一个完整的 LCEL 链。数据像水流一样经过三个处理单元:prompt → model → output_parser。
数据流向是这样的:
输入 {"language": "中文", "text": "Hello, World!"}
↓
prompt.invoke() → ChatMessage
↓
model.invoke() → AIMessage
↓
output_parser.invoke() → String
↓
输出 "你好,世界!"
3. Runnable 家族详解
这是本文的重点之一。LangChain 提供了丰富的 Runnable 组件,它们像乐高积木一样可以任意组合。本章节深入了解每一个组件。
3.1 基础构建块
3.1.1 RunnableLambda:将函数变为可组合的组件
RunnableLambda 是最基础的工具,它的作用只有一个------把普通 Python 函数转换成 Runnable 对象。
python
from langchain_core.runnables import RunnableLambda
# 定义一个普通函数
def add_ten(x: int) -> int:
return x + 10
# 转换为 Runnable
r = RunnableLambda(add_ten)
# 调用
result = r.invoke(5) # 15
你也可以使用匿名函数:
python
r = RunnableLambda(lambda x: x + 10)
result = r.invoke(5) # 15
为什么要转换为 Runnable?
因为转换后,这个函数就可以和其他 Runnable 用管道操作符连接了
python
def multiply_ten(x: int) -> int:
return x * 10
# 链接两个 Runnable
chain = RunnableLambda(add_ten) | RunnableLambda(multiply_ten)
result = chain.invoke(1) # (1 + 10) * 10 = 110
3.1.2 管道操作符 |:像搭积木一样组装链
管道操作符是 LCEL 的灵魂。它的作用很直观:把左侧的输出传给右侧作为输入。
python
# 等价于:result = step2(step1(input))
chain = step1 | step2
# 多步管道
chain = step1 | step2 | step3 | step4
除了 | 操作符,还有 .pipe() 方法可以实现同样的效果:
python
chain = r1.pipe(r2).pipe(r3)
两者完全等价,看个人喜好选择。
3.1.3 三种调用方式:invoke / batch / stream
| 方法 | 说明 | 适用场景 |
|---|---|---|
invoke |
单个输入 → 单个输出 | 实时交互、简单场景 |
batch |
列表输入 → 列表输出 | 批量处理、效率优先 |
stream |
单个输入 → 迭代器输出 | 流式输出、用户体验 |
python
# invoke: 单个输入 -> 单个输出
result = chain.invoke(5)
# batch: 批量输入 -> 列表输出(并行执行)
results = chain.batch([1, 2, 3, 4, 5])
# stream: 流式输出(迭代器)
for item in chain.stream(5):
print(item) # 逐步输出
batch会自动并行执行,非常适合需要处理大量数据的场景。如果担心 API 限流,可以用config={"max_concurrency": 3} 参数控制并发数,后面会讲一下config操作
3.2 并行与条件
3.2.1 RunnableParallel:多任务并行处理
有时候我们需要同时执行多个任务 ,比如同时获取翻译、总结、情感分析结果。RunnableParallel 就是为此而生。
python
from langchain_core.runnables import RunnableParallel, RunnableLambda
def add_ten(x: int) -> int:
return x + 10
def multiply_ten(x: int) -> int:
return x * 10
def square(x: int) -> int:
return x * x
# 字典形式定义并行任务
parallel = RunnableParallel({
"add": RunnableLambda(add_ten),
"mul": RunnableLambda(multiply_ten),
"square": RunnableLambda(square),
})
# 调用 - 所有任务并行执行
result = parallel.invoke(5)
# {'add': 15, 'mul': 50, 'square': 25}
也可以用关键字参数的形式:
python
parallel2 = RunnableParallel(
add=RunnableLambda(add_ten),
mul=RunnableLambda(multiply_ten),
)
实际应用场景 :在 Agent 开发中,你可以用
RunnableParallel同时调用多个工具(比如同时查询天气、股票、新闻),然后汇总结果。
3.2.2 RunnableBranch:条件分支(if-elif-else)
RunnableBranch 允许根据条件选择不同的处理路径。它的工作方式类似 if-elif-else 逻辑。
python
from langchain_core.runnables import RunnableBranch, RunnableLambda
def to_positive(x: int) -> str:
return f"{x} 是正数"
def to_negative(x: int) -> str:
return f"{x} 是负数"
def to_zero(x: int) -> str:
return f"{x} 是零"
# 条件分支 - 最后一个参数作为默认分支
branch = RunnableBranch(
(lambda x: x > 0, RunnableLambda(to_positive)),
(lambda x: x < 0, RunnableLambda(to_negative)),
RunnableLambda(to_zero), # 默认分支
)
# 调用
print(branch.invoke(5)) # '5 是正数'
print(branch.invoke(-3)) # '-3 是负数'
print(branch.invoke(0)) # '0 是零'
在 Agent 开发中的应用:这是 Agent 决策的核心组件!根据用户输入或中间结果,Branch 可以决定:
- 使用哪个工具
- 调用哪个模型
- 走哪个处理流程
RunnableBranch 的多种路由场景
下面展示 6+ 种实用的路由场景:
场景一:分数等级路由
python
score_branch = RunnableBranch(
(lambda x: x >= 90, RunnableLambda(lambda x: f"等级A: {x}分")),
(lambda x: x >= 80, RunnableLambda(lambda x: f"等级B: {x}分")),
(lambda x: x >= 70, RunnableLambda(lambda x: f"等级C: {x}分")),
(lambda x: x >= 60, RunnableLambda(lambda x: f"等级D: {x}分")),
RunnableLambda(lambda x: f"等级F: {x}分"), # 默认分支
)
scores = [95, 82, 75, 63, 45]
for s in scores:
print(f" {s}分 -> {score_branch.invoke(s)}")
# 输出:
# 95分 -> 等级A: 95分
# 82分 -> 等级B: 82分
# 75分 -> 等级C: 75分
# 63分 -> 等级D: 63分
# 45分 -> 等级F: 45分
场景二:类型路由
python
user_chain = RunnableLambda(lambda x: f"用户处理: {x['name']}")
product_chain = RunnableLambda(lambda x: f"商品处理: {x['name']}")
order_chain = RunnableLambda(lambda x: f"订单处理: {x['name']}")
type_router = RunnableBranch(
(lambda x: x.get("type") == "user", user_chain),
(lambda x: x.get("type") == "product", product_chain),
(lambda x: x.get("type") == "order", order_chain),
RunnableLambda(lambda x: f"默认处理: {x}"),
)
print(type_router.invoke({"type": "user", "name": "Alex"}))
# 输出:用户处理: Alex
场景三:数值范围路由
python
high_chain = RunnableLambda(lambda x: f"高值处理: {x * 10}")
medium_chain = RunnableLambda(lambda x: f"中值处理: {x * 5}")
low_chain = RunnableLambda(lambda x: f"低值处理: {x * 2}")
value_router = RunnableBranch(
(lambda x: x > 100, high_chain),
(lambda x: x > 50, medium_chain),
low_chain,
)
print(value_router.invoke(60)) # 中值处理: 300
print(value_router.invoke(150)) # 高值处理: 1500
场景四:字符串前缀路由
python
string_processor = RunnableBranch(
(lambda x: x.startswith("upper:"),
RunnableLambda(lambda x: x[6:].upper())),
(lambda x: x.startswith("lower:"),
RunnableLambda(lambda x: x[6:].lower())),
(lambda x: x.startswith("cap:"),
RunnableLambda(lambda x: x[4:].capitalize())),
RunnableLambda(lambda x: f"[默认] {x}"),
)
print(string_processor.invoke("upper:hello")) # HELLO
print(string_processor.invoke("lower:HELLO")) # hello
print(string_processor.invoke("cap:hello")) # Hello
3.2.3 RouterRunnable:简单 key→handler 映射
有时候只需要简单的 key 映射,不需要复杂的条件判断。RouterRunnable 就是为这种情况设计的。
python
from langchain_core.runnables.router import RouterRunnable
# 创建路由表:key -> Runnable
router = RouterRunnable(runnables={
"add": RunnableLambda(lambda x: x + 10),
"multiply": RunnableLambda(lambda x: x * 10),
"square": RunnableLambda(lambda x: x ** 2),
})
# 输入必须是字典,包含 "key" 和 "input"
result = router.invoke({"key": "add", "input": 5})
print(result) # 15
RouterRunnable vs RunnableBranch 对比
| 特性 | RouterRunnable |
RunnableBranch |
|---|---|---|
| 路由方式 | 根据 key 字段选择 | 根据条件函数返回 True/False |
| 输入格式 | 必须是 {"key": "...", "input": ...} |
任意类型 |
| 灵活性 | 低,key 必须是字符串 | 高,可自定义任意条件 |
| 复杂度 | 简单,适合 handler 映射 | 复杂,适合 if-elif-else |
选择建议:
- 需要简单的 key → handler 映射?用
RouterRunnable - 需要复杂的条件判断?用
RunnableBranch
3.3 数据流转神器
3.3.1 RunnablePassthrough:透传与字段选择
RunnablePassthrough 有两个主要用途:透传输入 和字段操作。
基本透传
python
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
def multiply_ten(x: int) -> int:
return x * 10
# 透传当前值,同时进行其他处理
chain = RunnableLambda(multiply_ten) | {
"original": RunnablePassthrough(), # 透传原始输入
"processed": RunnableLambda(lambda x: x * 10),
}
result = chain.invoke(5)
# {'original': 5, 'processed': 50}
使用 .pick() 选择字段
python
# 从字典中选择单个字段
pick_name = RunnablePassthrough().pick("name")
result = pick_name.invoke({"name": "Alex", "age": 30})
# 'Alex'
# 从字典中选择多个字段
pick_multi = RunnablePassthrough().pick(["name", "age"])
result = pick_multi.invoke({"name": "Alex", "age": 30})
# {'name': 'Alex', 'age': 30}
使用 .assign() 添加字段
python
# assign - 添加字段
chain = (
RunnableLambda(lambda x: {"input": x})
| RunnablePassthrough.assign(added=lambda x: x["input"] + 10)
)
result = chain.invoke(5)
# {'input': 5, 'added': 15}
# assign 多个字段
chain_multi_assign = (
RunnableLambda(lambda x: {"original": x})
| RunnablePassthrough.assign(
doubled=lambda x: x["original"] * 2,
squared=lambda x: x["original"] ** 2,
)
)
result = chain_multi_assign.invoke(5)
# {'original': 5, 'doubled': 10, 'squared': 25}
3.3.2 RunnableSequence:显式序列创建
RunnableSequence 用于显式创建有序的 Runnable 序列,等同于使用 | 管道操作符。
python
from langchain_core.runnables import RunnableSequence, RunnableLambda
def add_ten(x: int) -> int:
return x + 10
def multiply_ten(x: int) -> int:
return x * 10
def square(x: int) -> int:
return x * x
def to_string(x: int) -> str:
return f"结果是: {x}"
# 显式创建序列
sequence = RunnableSequence(
first=RunnableLambda(add_ten),
middle=[RunnableLambda(multiply_ten), RunnableLambda(square)],
last=RunnableLambda(to_string),
)
result = sequence.invoke(5)
# 5 -> add_ten -> 15 -> multiply_ten -> 150 -> square -> 22500 -> to_string -> "结果是: 22500"
3.4 批量处理
3.4.1 RunnableEach:序列元素逐个处理
RunnableEach 对输入序列中的每个元素执行同一个 Runnable。
python
from langchain_core.runnables import RunnableLambda, RunnableEach
def add_ten(x: int) -> int:
return x + 10
def multiply_ten(x: int) -> int:
return x * 10
# 基本用法 - 对每个元素执行 add_ten
each_add = RunnableEach(bound=RunnableLambda(add_ten))
result = each_add.invoke([1, 2, 3])
# [11, 12, 13]
# 链式组合
chain_each = (
RunnableEach(bound=RunnableLambda(add_ten))
| RunnableEach(bound=RunnableLambda(multiply_ten))
)
result = chain_each.invoke([1, 2, 3])
# [1, 2, 3] -> add_ten -> [11, 12, 13] -> multiply_ten -> [110, 120, 130]
3.4.2 batch_as_completed:完成即返回
batch_as_completed 会在任务完成时立即返回,而不是等待所有任务完成。
python
from langchain_core.runnables import RunnableLambda
def slow_op(x):
import time
time.sleep(x * 0.1)
return x * 10
chain = RunnableLambda(slow_op)
# 普通 batch - 按顺序返回
results = chain.batch([1, 2, 3, 4])
# batch_as_completed - 哪个完成返回哪个
for result in chain.batch_as_completed([1, 2, 3, 4]):
print(result) # 按完成顺序打印结果
4. 进阶功能:让链更健壮
这一章是让 LCEL 链真正用于生产环境的关键。想象一下:你的链在凌晨三点因为网络波动失败了,你希望它自动重试吗?你希望优雅地降级到备用模型吗?这一章告诉你怎么做。
4.1 容错机制
4.1.1 with_fallbacks:备用方案
with_fallbacks 为 Runnable 添加备用方案。当主 Runnable 抛出异常时,自动切换到备用方案。
python
from langchain_core.runnables import RunnableLambda
def always_fail(x):
raise RuntimeError(f"处理 {x} 时出错")
def fallback_handler(x):
return f"备用方案处理: {x}"
def call_gpt4(x):
return f"GPT-4: {x}"
def call_gpt35(x):
return f"GPT-3.5: {x}"
def call_local(x):
return f"Local: {x}"
# 基本用法 - 单个备用
chain = RunnableLambda(always_fail).with_fallbacks(
fallbacks=[RunnableLambda(fallback_handler)]
)
result = chain.invoke(5) # '备用方案处理: 5'
# 模型降级示例 - 多级备用
model_chain = (
RunnableLambda(call_gpt4)
.with_fallbacks(fallbacks=[RunnableLambda(call_gpt35)])
.with_fallbacks(fallbacks=[RunnableLambda(call_local)])
)
result = model_chain.invoke("Hello")
# 依次尝试 GPT-4 -> GPT-3.5 -> Local
4.1.2 with_retry:重试机制
with_retry 为 Runnable 添加重试机制,适用于处理网络请求临时故障等场景。
python
from langchain_core.runnables import RunnableLambda
def might_fail_once(x: int) -> int:
"""模拟可能失败一次的函数"""
if not hasattr(might_fail_once, 'called'):
might_fail_once.called = True
print("失败一次")
raise RuntimeError("第一次调用失败")
return x + 10
def selective_fail(x):
if x < 0:
raise ConnectionError("连接失败")
return x * 2
# 基本用法
retry_once = RunnableLambda(might_fail_once).with_retry()
result = retry_once.invoke(5) # 10
# 指定重试次数和异常类型
retry_connection = RunnableLambda(selective_fail).with_retry(
retry_if_exception_type=(ConnectionError,),
stop_after_attempt=3,
)
# 指数退避 + 随机抖动
retry_with_jitter = RunnableLambda(might_fail_once).with_retry(
wait_exponential_jitter=True,
stop_after_attempt=5,
)
# 链式组合中的重试
chain_with_retry = (
RunnableLambda(add_ten)
| RunnableLambda(might_fail_once).with_retry()
| RunnableLambda(multiply_ten)
)
参数详解:
retry_if_exception_type:指定需要重试的异常类型stop_after_attempt:最大重试次数wait_exponential_jitter:指数退避+随机抖动,避免惊群效应
4.2 配置与监控
4.2.1 RunnableConfig:执行配置
RunnableConfig 用于控制 Runnable 的执行行为,可以传递给 invoke、batch、stream 等方法。
python
from langchain_core.runnables import RunnableLambda, RunnableConfig
def add_ten(x: int) -> int:
return x + 10
chain = RunnableLambda(add_ten)
# tags - 标签管理
config_tags = {"tags": ["user", "premium", "urgent"]}
result = chain.invoke(5, config=config_tags)
# max_concurrency - 最大并发数
config_concurrency = {"max_concurrency": 2}
results = chain.batch([1, 2, 3, 4], config=config_concurrency)
# with_config 方法 - 在链中设置配置
chain_with_config = (
RunnableLambda(add_ten).with_config(run_name="add_ten_step")
| RunnableLambda(lambda x: x * 10).with_config(run_name="multiply_step")
)
RunnableConfig 常用参数:
| 参数 | 说明 | 典型用法 |
|---|---|---|
tags |
标签管理 | 区分生产/开发/测试环境 |
metadata |
元数据 | 追踪请求来源、用户ID |
max_concurrency |
最大并发数 | 控制 API 调用的并发度 |
recursion_limit |
递归深度限制 | 防止无限循环 |
callbacks |
回调函数 | 集成监控、日志 |
run_name |
运行名称 | 方便调试时识别 |
4.2.2 with_listeners:生命周期监听器
with_listeners 允许在 Runnable 执行的不同阶段(开始,结束,失败)插入自定义逻辑。
python
from langchain_core.runnables import RunnableLambda
def on_start_callback(run):
print(f"[on_start] 开始执行 run_id={run.id}")
def on_end_callback(run):
print(f"[on_end] 执行完成 run_id={run.id}")
def on_error_callback(run):
print(f"[on_error] 出错: run_id={run.id}")
chain = RunnableLambda(add_ten).with_listeners(
on_start=on_start_callback,
on_end=on_end_callback,
on_error=on_error_callback,
)
result = chain.invoke(5)
# 输出:
# [on_start] 开始执行 run_id=...
# [on_end] 执行完成 run_id=...
5. Runnable 在 Agent 开发中的应用
这一章我们来聊聊 Runnable 在 LangChain Agent 中的应用。Agent 是 LangChain 最强大的特性之一,而它背后的核心正是 Runnable 接口。
5.1 Agent 与 Runnable 的关系
想象一下 Agent 的工作流程:感知用户输入 → 决策下一步做什么 → 执行动作 → 获取结果 → 继续决策...
这个流程中的每一步,都可以用 Runnable 来表示:
python
# Agent 的简化工作流
agent_chain = (
prompt # 1. 感知:解析用户输入
| llm.with_tools(tools) # 2. 决策:选择要执行的工具
| output_parser # 3. 解析:解析工具调用
| execute_tools # 4. 执行:运行选择的工具
| ... # 循环往复
)
本质上,Agent 就是一条复杂的 Runnable 链。它之所以能灵活地处理各种任务,正是因为它建立在 Runnable 的组合能力之上。
5.2 动态路由在 Agent 中的应用
在 Agent 中,最常见的场景是根据条件决定下一步操作。
5.2.1 工具选择的条件判断
python
from langchain_core.runnables import RunnableBranch, RunnableLambda
# 根据用户意图选择不同工具
def classify_intent(x):
return x.get("intent", "unknown")
intent_router = RunnableBranch(
(lambda x: x.get("intent") == "weather",
RunnableLambda(lambda x: "调用天气API")),
(lambda x: x.get("intent") == "search",
RunnableLambda(lambda x: "调用搜索API")),
(lambda x: x.get("intent") == "calculator",
RunnableLambda(lambda x: "调用计算器")),
RunnableLambda(lambda x: "调用通用对话模型"),
)
5.2.2 多模型切换
python
# 根据任务类型选择不同模型
def select_model(x):
task_type = x.get("task_type", "general")
if task_type == "code":
return "gpt-4"
elif task_type == "creative":
return "claude-3"
return "gpt-3.5-turbo"
model_router = RunnableBranch(
(lambda x: x.get("task_type") == "code",
llm.bind(model="gpt-4")),
(lambda x: x.get("task_type") == "creative",
llm.bind(model="claude-3")),
llm.bind(model="gpt-3.5-turbo"),
)
5.3 工具调用的容错处理
Agent 免不了要和外部工具打交道,而外部工具可能会失败。Runnable 的容错机制在这里大显身手。
5.3.1 工具失败时的备用方案
python
from langchain_core.runnables import RunnableLambda
def call_primary_api(x):
# 可能失败的 API 调用
raise ConnectionError("主API不可用")
def call_backup_api(x):
return f"备用API返回: {x}"
# 使用 with_fallbacks 处理工具失败
safe_tool_chain = (
RunnableLambda(call_primary_api)
.with_fallbacks(fallbacks=[RunnableLambda(call_backup_api)])
)
result = safe_tool_chain.invoke("test")
# 输出:备用API返回: test
5.3.2 网络/API 临时故障重试
python
def call_unreliable_api(x):
import random
if random.random() < 0.3:
raise ConnectionError("网络波动")
return f"成功: {x}"
# 使用 with_retry 处理临时故障
retryable_tool = (
RunnableLambda(call_unreliable_api)
.with_retry(
retry_if_exception_type=(ConnectionError,),
stop_after_attempt=3,
wait_exponential_jitter=True,
)
)
6. 实战案例
让我们通过几个实际案例,把学到的知识串联起来。
6.1 多语言翻译助手
这个案例展示如何使用 .batch() 批量处理多个翻译请求。
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 初始化模型
base_url = "https://api.minimax.chat/v1"
api_key = "your-api-key"
model = ChatOpenAI(
model="MiniMax-M2.7",
base_url=base_url,
api_key=api_key,
)
# 创建翻译链
translation_prompt = ChatPromptTemplate.from_template(
"将以下句子翻译成{language}:{text}"
)
output_parser = StrOutputParser()
translation_chain = translation_prompt | model | output_parser
# 批量翻译
batch_inputs = [
{"language": "中文", "text": "Hello, how are you?"},
{"language": "日语", "text": "Hello, how are you?"},
{"language": "法语", "text": "Hello, how are you?"},
{"language": "德语", "text": "Hello, how are you?"},
]
results = translation_chain.batch(batch_inputs)
# 返回: ["你好,你怎么样?", "こんにちは、お元気ですか?", "Bonjour", "Wie geht es dir?"]
6.2 多功能消息处理系统
这个案例展示如何使用 RunnableParallel 同时执行多个任务。
python
from langchain_core.runnables import RunnableParallel
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 初始化模型
model = ChatOpenAI(model="MiniMax-M2.7", base_url=base_url, api_key=api_key)
output_parser = StrOutputParser()
# 创建多任务链
multi_task_chain = RunnableParallel({
"translation": (
ChatPromptTemplate.from_template("翻译成中文:{text}")
| model
| StrOutputParser()
),
"summary": (
ChatPromptTemplate.from_template("用一句话总结:{text}")
| model
| StrOutputParser()
),
"sentiment": (
ChatPromptTemplate.from_template("分析情感(正面/负面/中性):{text}")
| model
| StrOutputParser()
),
})
# 一次调用,同时获得翻译、总结和情感分析
result = multi_task_chain.invoke({
"text": "LangChain is an amazing framework for building LLM applications!"
})
# 返回:
# {
# "translation": "LangChain 是一个用于构建 LLM 应用的惊人框架!",
# "summary": "介绍 LangChain 框架的强大功能",
# "sentiment": "正面"
# }
6.3 带验证的 AI 对话链
这个案例展示如何使用 RunnableLambda 添加输入验证。
python
from langchain_core.runnables import RunnableLambda
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
model = ChatOpenAI(model="MiniMax-M2.7", base_url=base_url, api_key=api_key)
def validate_input(input_dict):
if not input_dict.get("topic"):
raise ValueError("topic 不能为空")
if len(input_dict["topic"]) > 100:
raise ValueError("topic 长度不能超过100")
return input_dict
validation_chain = (
RunnableLambda(validate_input)
| ChatPromptTemplate.from_template("写一首关于{topic}的诗")
| model
| StrOutputParser()
)
# 正常调用
result = validation_chain.invoke({"topic": "春天"})
# 验证失败会抛出 ValueError
# validation_chain.invoke({"topic": ""}) # ValueError: topic 不能为空
7. 总结
7.1 Langchain中内置Runnable 类型
核心 Runnable 类
| 类/方法 | 功能 |
|---|---|
RunnableLambda |
将普通函数转为 Runnable |
RunnableParallel |
并行执行多个 Runnable |
RunnablePassthrough |
透传输入或选择字段 |
RunnableBranch |
条件分支与动态路由 |
RouterRunnable |
基于 key 的简单路由 |
RunnableSequence |
显式创建有序序列 |
RunnableEach |
对序列中每个元素执行 Runnable |
.with_fallbacks() |
添加容错备用方案 |
.with_retry() |
添加重试机制 |
.with_listeners() |
添加生命周期监听器 |
.with_config() |
设置执行配置 |
.pick() |
从字典中选择字段 |
.assign() |
向字典添加字段 |
提示模板与输出解析器
| 类型 | 说明 | 导入方式 |
|---|---|---|
ChatPromptTemplate |
聊天提示模板 | from langchain_core.prompts import ChatPromptTemplate |
PromptTemplate |
文本提示模板 | from langchain_core.prompts import PromptTemplate |
StrOutputParser |
字符串输出解析器 | from langchain_core.output_parsers import StrOutputParser |
JsonOutputParser |
JSON 输出解析器 | from langchain_core.output_parsers import JsonOutputParser |
XMLOutputParser |
XML 输出解析器 | from langchain_core.output_parsers import XMLOutputParser |
LLM 模型
| 类型 | 说明 | 导入方式 |
|---|---|---|
ChatOpenAI |
OpenAI 聊天模型 | from langchain_openai import ChatOpenAI |
OpenAI |
OpenAI 文本模型 | from langchain_openai import OpenAI |
ChatAnthropic |
Anthropic 聊天模型 | from langchain_anthropic import ChatAnthropic |
ChatVertexAI |
Google Vertex AI 模型 | from langchain_google_vertexai import ChatVertexAI |
📝 个人笔记:本文档基于 LangChain 1.2.0 版本测试实验,碎碎念一下,今天和同事聊天,我感觉AI大模型时代其实应该了解Agent开发,同事说现在CC,opencode结合大模型这么厉害,Agent的开发也可以使用CC,opencode生成。使得我有点道心破碎不知所措,最近在工作中感觉JAVA的开发慢慢可以使用大模型代替导致有点看不到未来的路,后来想想既然学习了就学到底吧,有始有终,但行好事莫问前程。