147.Agent-LangGraph归并函数

**摘要:**本文介绍LangGraph中的归并函数(Reducer)机制。在LangGraph中,节点返回的内容是局部更新,Reducer用于将多个节点的输出合并,并将局部更新写入全局State。如果没有归并函数,并行执行的多个节点同时修改同一字段时,后执行的节点会覆盖先执行的节点,导致数据丢失。通过声明Annotated类型的变量并指定写入方式(如add追加),可以实现对State中内容的追加而非覆盖。文章通过完整代码示例演示了如何使用Annotated和add归约函数,实现messages字段的追加更新,并构建、编译、运行一个包含两个节点的工作流图。

内容参考于:图灵AI大模型全栈

归并函数(Reducer)

在LangGraph中节点它返回的内容是局部的更新,Reducer是用来把多个节点输出合并起来,然后再把每个节点局部更新的状态写入进全局的State中

如果没有Reducer当一个问题要执行两个节点,这两个节点是并行执行(一起执行),然后两个节点都修改了messages这个字段,那么两个节点执行完,最终State的messages字段的值是最后一个节点的内容,第一个节点的内容会被覆盖导致丢失,我们需要的是对State中的内容做追加,而不是覆盖

如果有归并函数它就会把节点的返回值已追加的方式写到State中

未添加归并函数的效果,如下图红框,它最终的内容是node2的返回值

下图蓝框加入归并函数,通过声明Annotated类型的变量,Annotated第一个值是真实的数据类型,第二个值是写入方式(add默认往后添加),下图红框效果图,messages的值就不会被覆盖了,而是依次往后添加内容

代码

python 复制代码
# 从 typing 模块导入类型工具
# Annotated:用于给类型附加元数据,LangGraph 用它来指定字段的「更新策略」
# TypedDict:定义结构化字典类型,约束状态的字段和数据类型
from typing import Annotated, TypedDict

# 导入 LangGraph 核心类 StateGraph,用于构建有状态的工作流图
from langgraph.graph import StateGraph

# 从 Python 标准库 operator 导入 add 函数
# 作用:作为列表的「归约合并函数」,实现状态字段的「追加更新」而非默认的「覆盖更新」
from operator import add


# ===================== 1. 定义全局状态结构 =====================
class MyState(TypedDict):
    # messages 字段:带归约策略的字符串列表
    # 语法格式:Annotated[字段数据类型, 归约函数]
    # 归约函数 add:指定该字段的更新规则为「追加合并」
    # 每次节点返回该字段时,新列表会被追加到原有列表末尾,而不是直接覆盖整个列表
    # 典型适用场景:对话历史、操作日志、执行轨迹等需要累计保留全量数据的场景
    messages: Annotated[list[str], add]
    
    # question 字段:普通字符串,使用默认更新策略
    # 默认策略:节点返回该字段时,新值直接覆盖旧值
    question: str
    
    # answer 字段:普通字符串,默认覆盖更新
    answer: str


# ===================== 2. 定义工作流节点函数 =====================
# 节点1:执行第一段业务逻辑,向 messages 追加一条记录
# 参数 state:当前完整的状态字典,节点通过它读取所有历史数据
def node1(state: MyState):
    print("执行节点1")
    # 返回需要更新的状态字段
    # 注意:因为 messages 配置了 add 归约,这里的列表会追加到原列表末尾
    # 如果没有 Annotated[..., add],这里会直接清空并替换整个 messages 列表
    return {"messages": ["这是节点1"]}


# 节点2:执行第二段业务逻辑,追加消息并生成最终答案
def node2(state: MyState):
    print("执行节点2")
    # messages:追加一条新记录(归约模式)
    # answer:直接赋值覆盖(默认替换模式)
    return {"messages": ["这是节点2"], "answer": "这是答案"}


# ===================== 3. 构建工作流图 =====================
# 初始化状态图,指定整个工作流使用 MyState 作为数据结构
# 参数 state_schema:状态结构定义,用于校验节点返回值、规范数据格式
graph = StateGraph(state_schema=MyState)

# 向图中注册节点
# 参数1:节点唯一ID(字符串),后续添加边、设置入口都通过这个ID引用节点
# 参数2:节点执行函数,工作流运行到该节点时自动调用
graph.add_node("node1", node1)
graph.add_node("node2", node2)

# 设置工作流入口点
# 参数:节点ID,指定工作流启动后第一个执行的节点
graph.set_entry_point("node1")

# 添加有向边,定义节点执行顺序
# 参数1:起点节点ID;参数2:终点节点ID
# 含义:起点节点执行完成后,工作流自动流向终点节点
graph.add_edge("node1", "node2")

# 添加指向结束节点的边
# __end__ 是 LangGraph 内置的终止标记节点,到达后工作流停止并返回最终状态
graph.add_edge("node2", "__end__")


# ===================== 4. 编译并运行工作流 =====================
# 编译图结构
# 作用:校验图的合法性(入口、边、节点引用是否正确),生成可执行的运行时实例
my_graph = graph.compile()

# 启动并执行工作流
# 参数:初始状态字典,作为入口节点的输入数据
# 返回值:工作流执行完成后的完整最终状态
result = my_graph.invoke({"messages": ["这是问题?"], "question": "问题"})

# 打印最终结果
print(result)

相关推荐
思绪漂移1 小时前
人机交互(人和AI)中的流程设计原则之一
人工智能·人机交互
IT_Octopus1 小时前
【零基础入门 LLM 开发 · Day 15】:LangChain 注册工具——告别手写 JSON Schema
java·langchain·json
磁场转动100万匹1 小时前
PyTorch 版 YOLOv3 源码逐段详解:从配置文件到训练、评估、检测(定制自己的数据集)
人工智能·深度学习·yolo
Elastic 中国社区官方博客1 小时前
教程:监控 Java 应用
java·开发语言·数据库·elasticsearch·搜索引擎·全文检索
ShineWinsu1 小时前
对于JavaScript:初识JavaScript以及输入输出
java·开发语言·前端·javascript·css·面试·html
小小安于乱1 小时前
linux中java的springboot程序获取IP地址间歇性10秒卡顿问题
java·开发语言
小白学大数据1 小时前
逆向分析短视频平台 a_bogus 参数:从 JavaScript 混淆到 Python 复现
javascript·python·音视频
lisw051 小时前
“科学前沿极限突破计划”述评!
大数据·人工智能·学习
戴西软件1 小时前
企业如何搭建自己的AI智能体?戴西智能体管理与戴西SKILL管理实战解析
运维·jvm·人工智能·自动化·rpa