一文全面解读LangGraph中Reducer的概念作用与原理使用详解
Reducer是LangGraph中定义State字段更新规则的函数,默认采用覆盖方式,后执行的节点会替换前一个节点的值;通过指定Reducer可改为合并或累加,其参数为当前值与新值,返回合并结果。由于图存在分支与循环,Reducer能确保数据累积而非丢失,内置operator add、add_messages等常用归约器。
前情回顾:LangGraph 入门要点速览
1. LangGraph 基础入门与核心概念全解析

Reducer(归约器),本质上就是一个函数,专门解决一个核心难题:当多个节点都想修改 State 中的同一个字段时,究竟该如何合并才算公平合理?
说得更通俗一点:Reducer 决定了 State 中某个字段的更新策略——是简单粗暴地直接覆盖?还是规规矩矩地追加到末尾?或者累加到一起?甚至可以完全自定义一套规则。
从实际问题出发,深入理解 LangGraph Reducer
核心矛盾:多个节点同时写入同一个字段
在 LangGraph 的架构中,多个节点按顺序依次执行,每个节点都可能顺手修改一下 State。这就引出了一个经典矛盾:
假设有两个答案摆在你面前:
- 2(后一个节点把前一个彻底覆盖掉)—— 这是 LangGraph 默认的处理方式
- 3(1 + 2 累加起来,谁也别想抹掉谁)—— 这是使用 Reducer 之后的结果
Reducer 就是那个帮你做出明智选择的关键工具。
默认行为 vs Reducer 行为:对比分析
默认行为:覆盖(Override)
如果不指定 Reducer,LangGraph 的默认机制非常霸道:后来者居上,后写入的值直接覆盖前者。
class MyState(TypedDict):counter: int# 没有 Reducername: str # 没有 Reducer
执行流程示意如下:
初始状态:counter = 0节点 A 返回:{"counter": 1}→ 状态变为:counter = 1节点 B 返回:{"counter": 2}→ 状态变为:counter = 2(节点 A 的贡献直接被抹掉)最终结果:counter = 2
这种默认模式适合什么场景?就是那些只需要最终值的场合,比如用户名、当前状态标志等,谁最后写入就用谁的,没问题。
Reducer 行为:合并(Merge)
一旦指定了 Reducer,整个处理逻辑就变了:
class MyState(TypedDict):counter: Annotated[int, operator.add] # 使用加法 Reducer
执行流程变成了这样:
初始状态:counter = 0节点 A 返回:{"counter": 1}→ 状态变为:counter = 1节点 B 返回:{"counter": 2}→ 状态变为:counter = 3(1 + 2 = 3,把前面的贡献累加上了!)最终结果:counter = 3
这种模式适合需要累积的场景,比如计数器、总分、消息列表——每个节点的贡献都不可或缺。
Reducer 的本质:一个普普通通的函数
Reducer 其实没什么魔法,它就是 Python 里一个普通的函数,接收两个参数:
def reducer_func(current_value, new_value) -> final_value:"""current_value:当前 State 中该字段的值new_value:节点返回的该字段的新值return:合并后的最终值"""# 在这里定义你的合并逻辑return merged_result
LangGraph 已经内置了几个常用的 Reducer,直接拿来用很方便:
| Reducer 函数 | 效果 | 等价于 |
|---|---|---|
operator.add | 数值相加 | lambda a, b: a + b |
operator.set | 集合合并 | lambda a, b: a | b |
add_messages | 消息列表追加 | 特殊处理,含 ID 去重 |
| 不指定 | 覆盖 | lambda a, b: b |
用生活实例彻底搞懂 Reducer 的用法
场景:记账本
你和室友共用一本账本,记录每天的开销。这个场景够熟悉吧?
没有 Reducer(覆盖模式):
周一:小明记了"吃饭 50元" → 账本:[吃饭 50元]周二:小红记了"打车 30元" → 账本:[打车 30元]← 周一的内容被覆盖了!周三:你们吵架了,因为周一的账找不到了
有 Reducer(追加模式):
周一:小明记了"吃饭 50元" → 账本:[吃饭 50元]周二:小红记了"打车 30元" → 账本:[吃饭 50元, 打车 30元]← 追加在后面周三:月底算账,清清楚楚
在这个例子中,add 就是一个 Reducer,它的规则很简单:新来的记录追加到旧记录的后面,而不是替换掉它。
为什么 LangGraph 需要 Reducer?
原因一:图并非线性执行
在 LangGraph 中,图可能存在分支和循环,路径不是一条直线那么简单:
┌→ 节点 B ─→┐START ─→┤├→ 节点 D └→ 节点 C ─→┘
节点 B 和节点 C 都可能修改同一个字段。如果没有 Reducer,后执行的节点会毫不留情地抹掉前一个节点的修改。有了 Reducer,两者的贡献就能共存,谁也不会被覆盖。
原因二:循环需要累积
节点 A → 条件判断 → 未满足 → 回到节点 A(循环)
在循环中,每次经过节点 A 都可能产生新数据。Reducer 确保这些数据是累积的,而不是每次循环都重置一遍,否则循环就失去了意义。
原因三:可预测的状态变化
Reducer 让状态变化变得透明且可预测。你清楚每个字段的更新规则是什么,不会出现“咦,这个值怎么不见了?”的困惑。
实战演练:自定义 Reducer
当内置 Reducer 不能满足需求时,自己动手写一个即可:
示例1:限制列表长度
def last_n_reducer(n: int):"""只保留最近 n 条记录"""def reducer(old: list, new: list) -> list:combined = old + newreturn combined[-n:]return reducerclass MyState(TypedDict):recent_logs: Annotated[List, last_n_reducer(10)]# 只保留最近10条
示例2:字典合并
def dict_merge(old: dict, new: dict) -> dict:"""合并两个字典,新值覆盖旧值"""merged = old.copy()merged.update(new)return mergedclass MyState(TypedDict):metadata: Annotated[Dict, dict_merge]# 字典合并
示例3:取最大值
def max_reducer(old: float, new: float) -> float:"""保留最大值"""return max(old, new)class MyState(TypedDict):max_score: Annotated[float, max_reducer]# 保留最高分
Reducer 的完整工作流程
为了让你彻底搞懂,这里把 LangGraph 内部处理 Reducer 的完整流程拆解开来:
1. 节点执行完毕,返回一个字典,比如 {"counter": 5}2. LangGraph 遍历这个字典的每个键值对3. 对于每个键(比如 "counter"): a. 查找 State 定义中这个键有没有 Reducer b. 如果有 Reducer:- 获取当前 State 中 "counter" 的旧值(比如 3)- 调用 Reducer 函数:reducer(旧值=3, 新值=5)- 把 Reducer 的返回值(比如 8)写入 State c. 如果没有 Reducer:- 直接用新值(5)覆盖旧值(3)4. 更新后的 State 传递给下一个节点
面试级总结
| 问题 | 答案 |
|---|---|
| Reducer 是什么? | 一个定义 State 字段更新规则的函数 |
| 默认行为是什么? | 覆盖(新值替换旧值) |
| Reducer 改变什么? | 从"覆盖"变成"合并/累加/自定义" |
| Reducer 的参数? | (current_value, new_value) → merged_value |
| 为什么需要它? | 因为图有分支和循环,需要累积而非覆盖 |
| 内置的有哪些? | operator.add、operator.set、add_messages |
| 能自己写吗? | 能,任何符合签名的函数都可以 |
一句话记住
现在你应该能理解,为什么 messages: Annotated[List, add_messages] 这么重要了吧?没有它,你的聊天 Agent 永远只能记住最后一句话。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。
CAD从入门到项目交付:绘图、标注、图块与实战工作流
掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。
Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。
Claude Code 文件修改前的权限模式配置与命令审批指南
本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。
Claude Code接入VS Code后先测扩展和终端命令
在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-09-01 16:53
2026-09-01 16:52
2026-09-01 14:27
2026-09-01 14:12
2026-09-01 14:10
2026-09-01 14:07
2026-09-01 13:55
2026-09-01 13:47
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

