一、基础通用规则

1. 返回值类型要求

节点函数必须返回一个字典(dict,不允许返回列表、字符串、数字、None 等其他类型。

  • 合法:{"key": value}
  • 非法:[]"hello"123None

2. 核心原则:只返回增量更新,不返回完整 State

节点无需把整个全局状态原样返回,仅写出当前节点需要修改 / 新增的字段

  1. 未在返回字典中出现的字段:保持原有状态值,不会被改动
  2. 出现的字段:按照该字段预设的更新规则(归约函数)合并新旧值。

例:State 有 loop_timeslogsearch_result 三个字段 节点只改 log 仅返回 {"log": ["xxx"]},另外两个字段维持原值。


二、字段更新规则(由 Annotated + 归约函数 决定)

这是 LangGraph 最核心的规则,分两大类,由状态 TypedDict 定义时的注解控制。

1. 默认规则:直接覆盖(Overwrite)

Annotated 注解的普通字段,默认更新逻辑:新值直接替换旧值。

  • 适用场景:数字、字符串、布尔、普通对象等单值字段;
  • 状态定义示例:

    python

    运行

    class LoopState(TypedDict):
        loop_times: int       # 普通字段,默认覆盖
        content: str
    
  • 执行过程: 原状态:{"loop_times": 0} 节点返回:{"loop_times": 1} 最终状态:{"loop_times": 1}(旧值被覆盖)

2. 自定义规则:归约函数(Reducer)

通过 Annotated[类型, 归约函数] 显式指定合并逻辑,最常用在列表场景

(1)内置常用:operator.add → 列表追加

专门用于对话记录、执行日志、消息列表等需要保留历史、向后追加的场景。

  • 状态定义:

    python

    运行

    log: Annotated[list[str], operator.add]
    
  • 执行过程: 原状态:{"log": ["第0轮思考完成"]} 节点返回:{"log": ["第1轮思考完成"]} 最终状态:{"log": ["第0轮思考完成", "第1轮思考完成"]}(新旧列表拼接)
(2)自定义归约函数

支持自己编写合并逻辑(保留最新、去重、条件合并等),规则同样作用于返回值。 函数格式:def 自定义函数(旧值, 新值) -> 合并后的值


三、多节点同时更新同一个字段的规则

多个节点、或单次执行中多次更新同一个字段时,统一遵循该字段绑定的归约规则:

  1. 普通字段:后执行的节点值覆盖前面的值;
  2. operator.add 列表字段:所有更新内容依次追加;
  3. 自定义归约字段:按自定义逻辑合并。

四、特殊场景规则

1. 节点无任何字段需要修改

如果当前节点仅做计算、打印日志,不需要修改任何状态

  • 写法:返回空字典 {}
  • 效果:全局状态完全不变,直接进入下一条边的路由逻辑。

python

运行

def empty_node(state: LoopState) -> dict:
    print("仅执行逻辑,不修改状态")
    return {}  # 合法写法

2. 字段值类型必须匹配

返回字典里的 key: value值的类型必须和 State 定义保持一致

  • State 定义 log: list[str] → 返回必须是列表 ["内容"],不能传字符串 "内容"
  • 类型不匹配不会直接报错(Python 动态类型),但会造成业务异常、IDE 类型校验失败,属于不规范写法。

3. 不允许新增 State 以外的字段

返回字典中的 key必须是 TypedDict 中预先定义好的字段名

  • 随意新增未定义字段,运行时不会报错,但违背状态结构约束,IDE 提示异常、后期难以维护,生产环境禁止。

五、结合之前代码举例复盘

状态定义

python

运行

class LoopState(TypedDict):
    content: str
    search_result: str
    loop_times: int
    log: Annotated[list[str], operator.add]

节点返回值解析

python

运行

def think_step(state: LoopState) -> dict:
    cnt = state["loop_times"]
    print(f"【第 {cnt} 轮大模型的思考】")
    # 只更新 log 字段
    return {"log": [f"第{cnt}轮思考完成"]}
  1. 返回 dict,符合类型要求;
  2. 仅返回增量字段 logcontent/search_result/loop_times 保持不变;
  3. log 带有 operator.add,执行列表追加,历史日志不丢失;
  4. 值为字符串列表 [xxx],和状态类型匹配。

六、完整规则速记(精简版)

  1. 格式:节点必须返回 dict
  2. 内容:只写需要修改的字段,增量更新,不返回完整状态;
  3. 更新逻辑
    • 普通字段 → 新值覆盖旧值;
    • Annotated + operator.add 列表 → 新内容追加
  4. 边界情况:无需改状态 → 返回空字典 {}
  5. 约束:键名、值类型必须和 TypedDict 定义一致,不随意增删字段
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐