LangGraph批量并行处理:使用Send实现高效笑话生成器
在构建复杂的AI工作流时,我们经常需要对多个相似的任务进行并行处理。LangGraph框架提供了Send
机制,允许我们优雅地实现批量并行执行。本文将通过一个笑话生成器的例子,详细介绍如何使用LangGraph的Send功能实现高效的并行处理。
项目概述
本项目实现了一个批量笑话生成器,具备以下特点:
- 并行处理:使用Send机制同时为多个主题生成笑话
- 状态管理:通过TypedDict定义清晰的数据结构
- 动态分发:根据输入主题数量动态创建并行任务
- 结果聚合:自动收集并合并所有并行任务的结果
核心依赖与导入
import operator
from typing import Annotated, TypedDict
from langgraph.constants import Send
from langgraph.graph import END, START, StateGraph
关键导入说明:
operator.add
:用于状态更新时的列表合并操作Annotated
:为类型注解添加额外的元数据信息Send
:LangGraph的批量执行机制StateGraph
:状态图的核心类
状态结构设计
class OverallState(TypedDict):
sub: list[str]
jokes: Annotated[list[str], operator.add]
状态结构解析:
sub字段
- 类型:
list[str]
- 用途:存储需要生成笑话的主题列表
- 示例:
["cats", "dogs", "programming"]
jokes字段
- 类型:
Annotated[list[str], operator.add]
- 用途:存储生成的笑话结果
- 关键特性:使用
operator.add
注解,表示当多个节点返回jokes时,会自动进行列表合并操作
Annotated的作用:
# 当两个节点都返回jokes时:
# 节点1返回:{"jokes": ["Joke about cats"]}
# 节点2返回:{"jokes": ["Joke about dogs"]}
# 最终状态:{"jokes": ["Joke about cats", "Joke about dogs"]}
批量分发逻辑
def continue_to_jokes(state: OverallState):
# 这个函数将会被调用来生成笑话,会触发生成笑话的节点,使用Send批量执行节点
return [Send('generate_joke', {"sub": s}) for s in state['sub']]
函数功能分析:
Send机制原理
Send
是LangGraph中用于批量并行执行的核心机制:
- 第一个参数:目标节点名称(‘generate_joke’)
- 第二个参数:传递给目标节点的状态数据
批量创建过程
# 假设输入状态:{"sub": ["cats", "dogs"]}
# 函数执行过程:
[
Send('generate_joke', {"sub": "cats"}), # 为cats主题创建任务
Send('generate_joke', {"sub": "dogs"}) # 为dogs主题创建任务
]
并行执行优势
- 性能提升:多个笑话同时生成,而不是串行处理
- 资源利用:充分利用系统的并行处理能力
- 扩展性:主题数量增加时,自动创建相应的并行任务
构建状态图
创建图实例
but = StateGraph(OverallState)
添加笑话生成节点
but.add_node("generate_joke", lambda state: {"jokes": [f"Joke about {state['sub']}"]})
节点函数解析:
- 接收包含单个主题的状态
- 生成格式化的笑话文本
- 返回包含jokes的字典,会被自动合并到整体状态中
配置图的连接关系
# 添加条件边:从START到批量分发函数
but.add_conditional_edges(START, continue_to_jokes)
# 添加普通边:从生成笑话节点到END
but.add_edge("generate_joke", END)
边的类型说明:
- 条件边(conditional_edges):根据函数返回值动态决定下一步执行
- 普通边(edge):固定的连接关系
执行流程与结果
编译和执行
g = but.compile()
result = g.invoke({"sub": ["cats", "dogs"]})
print(result)
执行流程详解
-
初始状态:
{"sub": ["cats", "dogs"], "jokes": []}
-
分发阶段:
continue_to_jokes
函数创建两个Send任务[ Send('generate_joke', {"sub": "cats"}), Send('generate_joke', {"sub": "dogs"}) ]
-
并行执行:两个
generate_joke
节点同时执行- 任务1:生成
{"jokes": ["Joke about cats"]}
- 任务2:生成
{"jokes": ["Joke about dogs"]}
- 任务1:生成
-
结果合并:由于使用了
operator.add
注解,结果自动合并 -
最终状态:
{ "sub": ["cats", "dogs"], "jokes": ["Joke about cats", "Joke about dogs"] }
可视化图表生成
graph_png = g.get_graph().draw_mermaid_png(max_retries=5)
with open("send_case.png", "wb") as f:
f.write(graph_png)
这段代码生成工作流的可视化图表,帮助理解执行流程:
- 使用Mermaid格式生成PNG图片
- 设置最大重试次数为5,提高生成成功率
- 保存为本地文件便于查看和分享
高级应用场景
1. 数据处理管道
# 批量处理不同数据源
def process_data_sources(state):
return [Send('process_source', {"source": src}) for src in state['sources']]
2. 多模型推理
# 使用不同模型并行推理
def multi_model_inference(state):
models = ["gpt", "claude", "gemini"]
return [Send('inference', {"model": m, "query": state['query']}) for m in models]
3. A/B测试执行
# 并行执行多个测试变体
def run_ab_tests(state):
variants = state['test_variants']
return [Send('execute_variant', {"variant": v}) for v in variants]
性能优化建议
1. 合理控制并发数量
# 对大量任务进行分批处理
def batch_process(state, batch_size=10):
items = state['items']
batches = [items[i:i+batch_size] for i in range(0, len(items), batch_size)]
return [Send('process_batch', {"batch": batch}) for batch in batches]
2. 错误处理机制
def robust_generate_joke(state):
try:
return {"jokes": [f"Joke about {state['sub']}"]}
except Exception as e:
return {"jokes": [f"Failed to generate joke for {state['sub']}: {str(e)}"]}
3. 资源管理
- 监控内存使用情况
- 设置合理的超时时间
- 实现优雅的降级策略
总结
LangGraph的Send机制为并行处理提供了强大而灵活的解决方案。通过本文的示例,我们学习了:
核心概念
- Send机制:实现批量并行任务分发
- Annotated类型:定义状态合并策略
- 条件边:根据状态动态创建执行路径
设计模式
- 分离关注点:分发逻辑与执行逻辑独立
- 状态驱动:通过状态变化控制工作流
- 结果聚合:自动收集并合并并行结果
实际价值
- 提升性能:并行处理显著减少总执行时间
- 增强扩展性:轻松处理动态数量的任务
- 简化代码:框架自动处理复杂的并行协调逻辑
这种设计模式在处理批量数据、多模型推理、分布式计算等场景中具有广泛的应用价值,为构建高效的AI工作流提供了坚实的基础。