首页 › G LangGraph Agent 编排 › G1 核心心智模型 › 02

G1-02 · State 与 Reducer:图的记忆怎么合并

入门阶段约 180 分钟阶段:G1 核心心智模型

阶段:G1-核心心智模型 | 预计耗时:60 分钟 | 前置:G1-01 第一个 Agent 解剖 代码:要你自己敲成 langgraph-learn/examples/g1_state_reducer.py 本篇知识点对应 知识全景 1.1、1.2——1.2 Reducer 是全框架头号大坑

学习目标

  1. 理解 State = 图的内存:节点之间不直接传话,全靠读写 State
  2. 亲眼看到覆盖 vs 合并的差异,掌握 Annotated[list, add_messages] 与自定义 Reducer
  3. 知道为什么"两个节点写同一个字段"必须想清楚合并规则

核心知识

1. State 就是 TypedDict

from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages

class State(TypedDict):
    messages: Annotated[list, add_messages]   # 有合并规则的字段
    draft: str                                 # 无注解 = 默认规则:覆盖
    revisions: int                             # 覆盖

节点返回 {"draft": "新诗", "revisions": 1} 是增量(delta),LangGraph 负责按规则合进总 State。

2. Reducer:同名字段的合并规则

写法 合并行为 适用
无注解 覆盖(后写赢) 计数器、当前草稿
Annotated[list, add_messages] 追加(且处理消息 ID 去重) 对话历史——Agent 的标配
自定义函数 Annotated[dict, merge_dict] 你说了算 多节点合写一个字典

3. 头号大坑现场:没有 Reducer 会怎样

两个并行节点都返回 {"log": ["A"]} 和 {"log": ["B"]}——没有 Reducer 时只剩一个(谁后到谁赢),另一个的结果凭空消失。并发节点越多,丢得越安静。这就是官方示例里 messages 都要挂 add_messages 的原因。

实验任务

任务 1:敲出对比脚本(25 分钟)

# g1_state_reducer.py —— 覆盖 vs 追加,一图看清
from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, START, END

def add_log(l: list[str], r: list[str]) -> list[str]:
    return l + r                              # 自定义 Reducer:追加

class State(TypedDict):
    log_plain: list[str]                      # 无 Reducer(覆盖)
    log_red: Annotated[list[str], add_log]    # 有 Reducer(追加)

def node_a(_s: State) -> dict:
    return {"log_plain": ["A"], "log_red": ["A"]}

def node_b(_s: State) -> dict:
    return {"log_plain": ["B"], "log_red": ["B"]}

g = StateGraph(State)
g.add_node("a", node_a); g.add_node("b", node_b)
g.add_edge(START, "a"); g.add_edge("a", "b"); g.add_edge("b", END)

print(g.compile().invoke({"log_plain": [], "log_red": []}))
# 预测:两个 log 字段各是什么?(先写下来再跑!)

任务 2:串行没问题、并行才要命(15 分钟)

把两条边改成并行分发(g.add_edge(START, "a"); g.add_edge(START, "b"))——Node A、B 同时写。运行后读 log_plain 还剩几个?这就是并发丢数据的现场。

任务 3:消息版 Reducer(15 分钟)

把 log_red 换成 Annotated[list, add_messages],节点返回 {"log_red": [("a", "来自A")]}——观察 add_messages 给消息自动分配 ID 的行为(m.id)。

验收标准

衔接下一阶段

State 记忆会管了 → G1-03 手写 StateGraph 与条件边:亲手搭 create_agent 藏起来的那张循环图。

小测验(3 题)

答完立即判分,错题给解析;得分存本地。实验与实操题不上网测:回对话找老师验收。