【Agent的革命之路——LangGraph】工作流中的 Command 模式

news/2025/2/23 13:42:32

这篇文章我们将 LangGraph中的控制流(边)和状态更新(节点)结合起来使用。比如,我们希望同时执行状态更新并决定下一步要转到哪个节点,且这些操作在同一个节点中完成。而正好 LangGraph 提供了一种方法,可以通过从节点函数返回一个 Command 对象来实现这一点。

python">def my_node(state: State) -> Command[Literal["my_other_node"]]:
    return Command(
        # state update
        update={"foo": "bar"},
        # control flow
        goto="my_other_node"
    )

如果我们使用的是 subgraphs(子图) 我们还可以通过 Command 中指定 graph=Command.PARENT这种方式,实现节点从一个子图导航到其他子图。

python">def my_node(state: State) -> Command[Literal["my_other_node"]]:
    return Command(
        update={"foo": "bar"},
        goto="other_subgraph",  # where `other_subgraph` is a node in the parent graph
        graph=Command.PARENT
    )

当我们从子图节点向父图节点发送更新的时候,且更新的键同时存在于父图和子图的状态模式中时,我们必须为父图状态中正在更新的键定义一个 reducer。
下面我们开始我们的例子。

定义节点和State

定义 graph State:

python">class State(TypedDict):
    foo: uiltin">str

定义节点A,

python">def node_a(state: State) -> Command[Literal["node_b", "node_c"]]:
    print("Called A")
    value = random.choice(["a", "b"])
    # 判断应该跳转到哪个节点
    if value == "a":
        goto = "node_b"
    else:
        goto = "node_c"

    # Command 允许我们同时更新图状态并路由到下一个节点。
    return Command(
        # 更新图中的状态
        update={"foo": value},
        # 替换edge
        goto=goto,
    )

定义节点b或者c:

python">def node_b(state: State):
    print("Called B")
    return {"foo": state["foo"] + "b"}


def node_c(state: State):
    print("Called C")
    return {"foo": state["foo"] + "c"}

定义好了上面节点之后,现在,我们可以使用上述节点创建 StateGraph。需要注意的是,该图没有定义条件的路由edge!这是因为控制流是用 Command 内部 node_a 定义的。

python">builder = StateGraph(State)
builder.add_edge(START, "node_a")
builder.add_node(node_a)
builder.add_node(node_b)
builder.add_node(node_c)

graph = builder.uiltin">compile()

不知道大家注意到没有,我们的 node_a 使用了 Command 作为返回类型注解,例如 Command[Literal[“node_b”, “node_c”]]。这一步对于图的渲染是非常必要的,它会告诉 LangGraph:node_a 可以导航到 node_b 和 node_c。
现在我们看看图的·1展示效果:

python">from IPython.display import display, Image

display(Image(graph.get_graph().draw_mermaid_png()))

在这里插入图片描述
我们来试试多次调用graph:

python">graph.invoke({"foo": ""})

将会看到,graph会根据节点A中的随机选择,采用不同的路径(A-> B或A-> C)。

导航到 parent graph 中的节点

现在,让我们演示如何从 subgraph 内部导航到 parent graph 中的不同节点。我们将通过将上述示例中的 node_a 转换为一个单节点图来实现这一点,然后将其作为子图添加到 parent graph 中。

python">import operator
from typing_extensions import Annotated


class State(TypedDict):
    # N定义一个reducer 自动附加消息
    foo: Annotated[uiltin">str, operator.add]


def node_a(state: State):
    print("Called A")
    value = random.choice(["a", "b"])
    if value == "a":
        goto = "node_b"
    else:
        goto = "node_c"

    # Command 允许我们同时更新图状态并路由到下一个节点。
    return Command(
        update={"foo": value},
        goto=goto,
        # 这会告诉 LangGraph 导航到父图中的 node_b 或 node_c 这将导航到相对于子图的最近的父图
        graph=Command.PARENT,
    )

subgraph = StateGraph(State).add_node(node_a).add_edge(START, "node_a").uiltin">compile()

def node_b(state: State):
    print("Called B")
    # 由于我们已经定义了一个 reducer,因此不需要手动将新字符附加到现有的 foo 值上。相反,reducer 会自动(通过 operator.add)将这些值附加到现有值中。
    return {"foo": "b"}


def node_c(state: State):
    print("Called C")
    return {"foo": "c"}

然后我们来构造graph:

builder = StateGraph(State)
builder.add_edge(START, "subgraph")
builder.add_node("subgraph", subgraph)
builder.add_node(node_b)
builder.add_node(node_c)

graph = builder.compile()

查看效果:

Called A
Called C
{'foo': 'bc'}

到这里我们的代码就结束了,我们通过这种方式展示了如何使用 LangGraph 的 Command 对象在子图中更新状态并导航到父图中的不同节点。并通过 reducer 简化状态管理,就是自动保存消息链。


http://www.niftyadmin.cn/n/5863442.html

相关文章

3D Gaussian Splatting 数学原理与推导

概述 3D Gaussian Splatting源于EWA的理论基础,本文重点从EWA模型出发,详细推导3D GS中的数学原理,加深对鱼眼相机畸变场景下3D GS渲染结果的理解。 引言 神经辐射场(NeRF)的出现,在新视图合成领域引起了广泛关注,显著提高了合成结果的质量。然而,NeRF 训练和渲染速…

EasyX图形库使用教程

文章目录 EasyX 图形库基础使用教程(快速上手)前言:本文简单详细的介绍了EasyX图形库的常用函数和操作,帮助EasyX的快速上手1、绘制简单的图形窗口1.1头文件1.2创建图形化窗口initgraphy函数closegraph函数 1.3窗口坐标 2、设置图…

go 环境准备

配置路径: GOROOT:D:\GoGOPATH:go的工作目录 D:\workspacego 验证版本:go version 配置第三方仓库: GO111MODULE:开启mod模式GOPROXY:go语言三方库地址GOSUMDB:go语言软件包的M…

【ORB-SLAM3】鲁棒核函数的阈值设置

问题背景 阈值 δ \delta δ 是 Huber 鲁棒核函数的重要参数。首先给出结论,在ORB-SLAM系列中,该阈值选取的原则为: 单目情况下,根据95%置信水平下两自由度卡方检验的临界值, δ \delta δ 设置为 5.991 \sqrt{5.9…

Qt中QRadioButton的样式设置

在 Qt 中,可以使用 Qt 样式表(QSS) 来自定义 QRadioButton 的外观。样式表类似于 CSS,允许你设置控件的颜色、字体、边框、背景等属性。 以下是如何为 QRadioButton 设置样式表的详细说明和示例。 1. 基本样式设置 你可以通过 s…

基于SpringBoot的线上汽车租赁系统的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…

React - ajax 配置代理

React 本身只关注于界面,并不包含发送 ajax 请求的代码;前端应用需要通过 ajax 请求与后台进行交互(json 数据);React 应用中需要集成第三方 ajax 库(或者自己封装)。 方法一 在 package.json …

【JT/T 808协议】808 协议开发笔记 ② ( 终端注册 | 终端注册应答 | 字符编码转换网站 )

文章目录 一、消息头 数据1、消息头拼接2、消息 ID 字段3、消息体属性 字段4、终端手机号 字段5、终端流水号 字段 二、消息体 数据三、校验码计算四、最终计算结果五、终端注册应答1、分解终端应答数据2、终端应答 消息体 数据 六、字符编码转换网站 一、消息头 数据 1、消息头…