📌 目标
- • 理解
add_messages 在对话场景中的应用
1️⃣ 为什么需要 Reducer?
默认行为的问题
默认情况下,节点返回的值会直接覆盖 State 中对应的字段:
class GameState(TypedDict): score: intdef level1(state: GameState): return {"score": 10}def level2(state: GameState): return {"score": 20}# 执行流程:level1 → level2# 最终 score = 20(被覆盖)# 期望:score = 10 + 20 = 30
Reducer 解决方案
┌─────────────────────────────────────────────────────────┐│ Reducer 核心作用 │├─────────────────────────────────────────────────────────┤│ 无 Reducer: state["score"] = node_return["score"] ││ 有 Reducer: state["score"] = reducer(current, new) │└─────────────────────────────────────────────────────────┘
2️⃣ 使用 Annotated 定义 Reducer
语法格式
from typing import Annotatedimport operatorclass MyState(TypedDict): score: Annotated[int, operator.add] # 累加 actions: Annotated[list[str], operator.add] # 列表合并
完整示例
对应代码:graph_node_edge2.py
from langgraph.graph import StateGraph, START, ENDfrom typing_extensions import TypedDictfrom typing import Annotatedimport operatorclass ScoreState(TypedDict): player_name: str score: Annotated[int, operator.add] actions: Annotated[list[str], operator.add]def level1_node(state: ScoreState): return {"score": 10, "actions": ["完成第一关"]}def level2_node(state: ScoreState): return {"score": 20, "actions": ["完成第二关"]}# 构建图workflow = StateGraph(ScoreState)workflow.add_node("level1", level1_node)workflow.add_node("level2", level2_node)workflow.add_edge(START, "level1")workflow.add_edge("level1", "level2")workflow.add_edge("level2", END)app = workflow.compile()result = app.invoke({"player_name": "玩家小明", "score": 0, "actions": []})print(f"总分: {result['score']}") # 输出: 30 (10+20)
3️⃣ 内置 Reducer
operator.add(最常用)
class MyState(TypedDict): total: Annotated[int, operator.add] # 整数累加 items: Annotated[list[str], operator.add] # 列表合并
operator.mul
class MyState(TypedDict): product: Annotated[int, operator.mul] # 累乘: 2 * 3 * 4 = 24
4️⃣ 自定义 Reducer
基本格式
def my_reducer(current: T, new: T) -> T: return merged_value
示例:取最大值
def max_reducer(current: int, new: int) -> int: return max(current, new)class GameScore(TypedDict): high_score: Annotated[int, max_reducer] # 保持最高分
示例:字典合并
def merge_dicts(current: dict, new: dict) -> dict: return {**current, **new}class ConfigState(TypedDict): config: Annotated[dict, merge_dicts]
5️⃣ add_messages:对话专用 Reducer
MessagesState
LangGraph 提供了专门用于对话场景的 State:
from langgraph.graph import MessagesStateclass ChatState(MessagesState): user_name: str # 自动包含 messages 字段,使用 add_messages Reducer
add_messages 功能
┌─────────────────────────────────────────────────────────┐│ add_messages 功能 │├─────────────────────────────────────────────────────────┤│ 1. 添加新消息 ││ 2. 更新消息(相同 ID) ││ 3. 删除消息(ID 对应的消息内容为 None) │└─────────────────────────────────────────────────────────┘
📖 完整示例请参见 05_完整应用案例 中的聊天机器人案例
6️⃣ Reducer 选择指南
| | |
|---|
| operator.add | |
| operator.add | |
| | |
| add_messages | |
| | |
| | |
📊 对比总结
┌─────────────────────────────────────────────────────────┐│ 有无 Reducer 对比 │├─────────────────────────────────────────────────────────┤│ ││ 无 Reducer: ││ ┌─────────────────────────────────────────────┐ ││ │ State: {"score": 10} │ ││ │ 节点返回: {"score": 20} │ ││ │ 结果: {"score": 20} ← 覆盖 │ ││ └─────────────────────────────────────────────┘ ││ ││ 有 Reducer (operator.add): ││ ┌─────────────────────────────────────────────┐ ││ │ State: {"score": 10} │ ││ │ 节点返回: {"score": 20} │ ││ │ Reducer: 10 + 20 = 30 │ ││ │ 结果: {"score": 30} ← 累加 │ ││ └─────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────┘
🎯 小结
| |
|---|
| Reducer | |
| Annotated | |
| operator.add | |
| add_messages | |
| 自定义 Reducer | |