Reducer 是什么?多个节点如何安全更新状态
上一篇文章里,我们讨论了 Edge 和 Conditional Edge。
一个重要结论是:
Edge 让流程可以分支,也可以回到前面的节点。
但一旦流程变复杂,就会遇到另一个问题:
多个节点如何安全更新同一份 State?
这就是 Reducer 要解决的事。
很多人第一次接触 LangGraph 时,会把 Reducer 看成一个不太重要的细节。
实际上,它非常关键。
因为它决定了状态更新是覆盖、追加,还是按规则合并。
State 更新为什么需要规则?
最简单的状态更新,看起来像这样:
旧值 + 新值 = 新状态但真实流程里,情况没有这么简单。
比如:
一个节点追加消息 一个节点更新检索结果 一个节点记录工具调用 一个节点补充人工审核意见如果这些更新都写到同一个 State,系统必须知道:
哪些字段该覆盖 哪些字段该追加 哪些字段该合并 哪些字段不该丢失没有这个规则,状态就会乱。
Reducer 的作用是什么?
Reducer 的作用,就是定义 State 某个字段的合并方式。
简单说:
当新更新到来时,如何和旧状态合并如果没有 reducer,很多字段默认可能是覆盖。
但在 Agent Workflow 里,覆盖并不总是你想要的。
比如消息列表通常应该追加,而不是覆盖。
而某些标量字段,比如:
task_status retry_count need_human_review则可能更适合覆盖或替换。
所以不同字段需要不同更新策略。
追加型字段和覆盖型字段
可以先把 State 字段分成两类。
第一类是追加型字段。
比如:
messages logs tool_calls retrieval_candidates notes这些字段通常需要保留历史。
第二类是覆盖型字段。
比如:
current_step task_status need_human_review final_answer retry_count这些字段更像当前值。
Reducer 的价值,就是让这两类字段都能按正确方式更新。
为什么消息列表最常见?
LangGraph 里最常见的 reducer 场景,就是消息列表。
比如一个对话 Agent,每次模型输出都要追加到消息历史里。
如果没有合适的合并规则,后面的节点只会看到最后一条消息。
那系统就失去上下文了。
所以消息类字段通常需要“追加”语义。
这也是为什么很多 Agent 应用都非常重视 history、messages 或 events 这类字段。
它们不是一次性输入,而是持续积累的过程。
并行更新时,Reducer 更重要
Reducer 最关键的场景,往往出现在并行或多来源更新。
比如:
一个节点返回检索结果 另一个节点返回工具结果 还有一个节点返回人工补充信息这些更新可能都要写回同一个 State。
这时候就必须清楚:
是追加? 是合并? 还是后者覆盖前者?如果规则不明确,结果就不可预测。
Reducer 让这种合并变得确定。
Reducer 让 State 更稳定
很多 Agent 出问题,不是因为模型不行,而是状态被更新乱了。
比如:
检索结果被覆盖 消息历史被截断 工具结果被误删 中间步骤没留下痕迹有了合并规则,系统会更稳定。
因为每个字段都知道自己应该怎么更新。
这对复杂图尤其重要。
Reducer 不等于随便拼接
一个常见误区是:
认为 reducer 就是简单把内容拼起来。
不是。
Reducer 的核心不是“拼”,而是“按规则合并”。
有些字段适合追加。
有些字段适合取最新值。
有些字段适合做去重。
有些字段适合保留最大值或最可信值。
不同字段的语义不同,合并策略也不同。
如果把所有东西都当成字符串拼接,State 很快会变脏。
一个简单例子
假设有一个字段叫messages。
每次节点输出一条消息,应该追加到列表里。
而如果有一个字段叫attempt_count,每次重试应该加一。
再比如final_answer,通常只在最后写入一次。
这三种字段明显不该用同一种更新逻辑。
Reducer 的意义,就是把这种差异化更新表达出来。
Reducer 和可观测性有关
Reducer 不只是状态合并问题。
它也关系到排查问题。
因为如果状态字段被覆盖或丢失,你会很难知道是哪一步改掉了什么。
如果 reducer 规则清楚,后面你就能更容易复盘:
哪个节点更新了什么 哪个字段被追加 哪个字段被覆盖 哪个状态是最终生效的这会显著降低调试成本。
Reducer 设计要和 State 一起做
Reducer 不是事后补丁。
它应该和 State 设计一起完成。
当你设计一个字段时,就应该想清楚:
这个字段是追加型还是覆盖型? 多个节点会不会同时写它? 如果会,应该怎么合并? 这个字段要不要保留历史?如果这些问题一开始没想清楚,后面很容易返工。
常见误区
第一个误区,是以为 reducer 不重要。
其实它是 State 正确更新的核心。
第二个误区,是所有字段都用同一种更新方式。
不同字段语义不同,规则也应不同。
第三个误区,是只考虑单节点写入,不考虑并行或回边。
流程一复杂,状态冲突就很常见。
第四个误区,是 reducer 规则不透明。
看不懂规则,图就难维护。
第五个误区,是把 reducer 当成字符串拼接器。
它应该是状态合并规则,而不是简单连字符。
总结
Reducer 的作用,是定义多个节点更新 State 时的合并规则。
它让:
消息可以追加 日志可以保留 结果可以覆盖 计数可以累加一个复杂 Agent 图,如果没有合适的 reducer,状态很容易乱。
而状态一乱,路由判断、调试和恢复都会变得很困难。
所以 LangGraph 里,State 不只是字段集合,Reducer 也不是小细节。
它们一起决定了图的稳定性。
下一篇文章,可以继续讨论 Tool Node。
因为 Agent 真正变得有用,很大一部分来自工具调用。
