阶段:G2-控制流进阶 | 预计耗时:60 分钟 | 前置:G2-01 Command 与动态跳转 代码:要你自己敲成
langgraph-learn/examples/g2_send.py对应 知识全景 2.4 map-reduce——S2 票据项目"批量并行处理"的核心机制
学习目标
- 掌握
Send(node_name, state):运行时动态开出 N 个并行分支(数量事先不知道也行) - 理解 map-reduce 结构在图里的长相:分发 → 并行处理 → Reducer 汇聚
- 实测并行比串行快多少(本机 CPU 上 LLM 调用是网络 IO,并行真提速)
核心知识
1. Send 解决什么问题
- 条件边一次只能去一个节点;Command 一次也只能 goto 一个地方
- "10 篇文章各自摘要"如果逐个循环调用 = 串行 10 次等待;Send 一次分发 10 份,同时开跑
- 关键:分发数量和每份的数据都是运行时决定的——
Send在"路由函数"里返回一个列表
2. 标准三段式(map-reduce)
from langgraph.types import Send
def fan_out(state: State) -> list[Send]: # 路由函数:发多少份
return [Send("summarize", {"article": a}) for a in state["articles"]]
def summarize(payload: dict) -> dict: # 每个并行分支收到自己那份
return {"summaries": [f"摘要:{payload['article'][:20]}..."]}
builder.add_conditional_edges(START, fan_out, ["summarize"]) # Send 用条件边语法挂载
builder.add_edge("summarize", "reduce") # 全部完成后汇聚
- 每个 Send 携带独立的小状态(不是共享大 State)——天然隔离
summaries字段必须有追加型 Reducer,否则 10 个分支互相覆盖只剩 1 份(G1-02 大坑的并行版)
3. 与"循环逐个处理"的本质区别
循环 = 时间换空间,一个节点跑 N 次;Send = 空间换时间,N 个分支同时跑。但有副作用顺序要求时(比如流水号),并行反而添乱——这个判断进 G5 反模式篇。
实验任务
任务 1:纯逻辑版(不需要 LLM,20 分钟)
上面骨架里的 summarize 用假函数即可,10 篇"文章"用 10 个字符串。重点观察:
- summaries 是不是 10 份全在?(Reducer 功劳)
- 把 Reducer 拿掉再跑——剩几份?
任务 2:真 LLM 并行版(25 分钟,花极少额度)
summarize 里换成真实调用(Qwen3.5-4B,max_tokens=50),10 篇短文摘要: - 记录并行总耗时;再改成 for 循环逐篇调用的串行总耗时 - 对比写进观察笔记(预期:并行约为串行的 1/4~1/6)
任务 3:混合分发(15 分钟)
fan_out 里按文章长度走不同分支:短的进 summarize_fast、长的进 summarize_deep——体会 Send 能按内容路由到不同节点,这是固定条件边做不到的。
验收标准
衔接下一阶段
会横向铺开了 → G2-03 Subgraph 子图:把复杂流程分层封装。