Command简述

        在 LangGraph 中,Command 是一个极具实用性的功能,它能够将控制流(边)和状态更新(节点)巧妙地结合起来。这意味着开发者可以在同一个节点中,既执行状态更新操作,又决定下一个要前往的节点,为工作流的构建带来了极大的灵活性。

Command基本用法

Command 允许你在单个节点函数中完成两件关键任务:

  1. 更新图状态 (update): 修改共享的 State 对象。

  2. 指定下一节点 (goto): 显式决定工作流下一步执行哪个节点。

def my_node(state: State) -> Command[Literal["my_other_node"]]:​return Command(​# 状态更新​update={"foo": "bar"},​# 控制流​goto="my_other_node"​)

        在这个示例中,my_node 函数返回一个 Command 对象,

  • update 参数用于指定状态的更新内容,将状态中的 "foo" 键值设为 "bar";

  • goto 参数则决定了下一个要前往的节点是 "my_other_node"。

关键约束

        在节点函数中返回 Command 时,必须添加返回类型注释,注明该节点可路由到的节点名称列表,如 Command[Literal["my_other_node"]]。这一点至关重要,它不仅是 graph 渲染所必需的,还能明确告知 LangGraph 该节点可以导航到 "my_other_node"。

Command 核心应用场景

动态控制流(替代条件边)

        直接在节点逻辑中根据状态判断跳转路径,代码更内聚:

def check_threshold(state: State) -> Command[Literal["proceed", "halt"]]:if state["value"] > state["threshold"]:return Command(goto="halt")  # 无需更新状态时update可省略return Command(goto="proceed")

Command 与条件边的决策

  • Command当您需要同时更新图形状态和路由到其他节点时使用。例如,在实现多代理切换时,需要路由到其他代理并向该代理传递一些信息。

  • 仅需根据当前状态选择分支且不涉及状态修改(如循环判断、简单路由),使用条件边更清晰。

跨层级节点跳转(子图 → 父图)

        实现跨子图的工作流跳转,需指定 graph=Command.PARENT,代码示例如下:

from typing import Literal, Optional, Annotated
from operator import addfrom langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command
from pydantic import BaseModel# 定义子图状态
class SubgraphState(BaseModel):"""子图状态定义"""issue: strescalated: bool = False  # 与父图共享的状态字段# 定义父图状态 reducer(处理子图与父图的状态冲突)
def escalated_reducer(left: bool, right: bool) -> bool:"""共享字段escalated的合并策略:右侧值(子图更新)优先"""return right# 定义父图状态
class ParentGraphState(BaseModel):"""父图状态定义"""issue: strescalated: Annotated[bool, escalated_reducer] = False  # 使用reducer注解resolution: Optional[str] = None# 子图节点
def subgraph_worker(state: SubgraphState) -> Command[Literal["manager_approval", END]]:"""子图中的工作节点,决定是否升级到父图"""if "critical" in state.issue.lower():return Command(update={"escalated": True},  # 更新共享状态goto="manager_approval",  # 父图中的目标节点graph=Command.PARENT  # 关键:指定跳转到父图)return Command(goto=END)  # 不升级则直接结束子图# 构建子图
subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node("worker", subgraph_worker)
subgraph_builder.add_edge(START, "worker")
subgraph = subgraph_builder.compile()# 父图节点
def manager_approval_node(state: ParentGraphState) -> ParentGraphState:"""父图中的经理审批节点"""return ParentGraphState(**state.model_dump(), resolution="经理已审批处理")# 构建父图(包含子图)
parent_builder = StateGraph(ParentGraphState)
parent_builder.add_node("subgraph", subgraph)  # 嵌入子图
parent_builder.add_node("manager_approval", manager_approval_node)# 定义父图边
parent_builder.add_edge(START, "subgraph")
parent_builder.add_edge("manager_approval", END)# 编译父图
parent_graph = parent_builder.compile(checkpointer=MemorySaver()
)# 运行示例
if __name__ == "__main__":print("=== 跨层级跳转测试 ===")# 测试会触发升级的情况result1 = parent_graph.invoke({"issue": "Critical error: system down"})print(f"测试1 - 触发升级: {result1}")# 应输出包含escalated=True和经理审批结果的状态# 测试不会触发升级的情况result2 = parent_graph.invoke({"issue": "Minor issue: slow response"})print(f"测试2 - 不触发升级: {result2}")# 应输出escalated=False且无经理审批结果的状态
  • 状态同步注意:若父子图共享状态字段,需在父图状态中为该字段定义 reducer 函数处理冲突。

工具调用与状态注入

        一个常见的工具调用场景是从工具内部更新图谱状态。例如,在客户支持应用中,您可能希望在对话开始时根据客户的账号或 ID 查找客户信息。要从工具中更新图谱状态,您可以从工具中返回 Command(update={"my_custom_key": "foo", "messages": [...]})。代码示例如下:

from typing import Annotated, Dict, Any, List
from pydantic import BaseModel
from operator import addfrom langgraph.graph import StateGraph, START, END, Command
from langgraph.prebuilt import ToolNode
from langgraph.checkpoint.memory import MemorySaver
from langchain_core.tools import tool
from langchain_core.messages import AIMessage, ToolMessage# 定义消息列表的reducer
def add_messages(left: List[Any], right: List[Any]) -> List[Any]:"""消息列表的合并策略"""return left + right# 1. 定义状态模型
class SupportState(BaseModel):user_id: str  # 客户IDuser_info: Annotated[Dict[str, Any], add] = {}  # 工具返回的用户信息messages: Annotated[List[Any], add_messages] = []  # 消息历史(必须包含工具调用记录)query_status: str = "pending"  # 流程状态标记# 2. 定义工具函数
@tool
def lookup_user_info(tool_call_id: Annotated[str, "tool_call_id"]) -> Command:"""根据用户ID查询客户信息(内部工具)"""# 假设从线程配置中获取user_id(实际中可从config获取)# 注意:此处简化处理,实际需注入configuser_id = "CUST-789"  # 模拟获取print(f"正在查询用户信息: {user_id}")# 模拟实际查询逻辑(可替换为数据库/API调用)user_data = {"user_id": user_id,"name": "陈明亮","membership": "白金会员","contact": "chen@example.com","recent_tickets": "订单延迟问题"}print(f"查询结果: {user_data}")# 返回Command更新状态return Command(update={"user_info": user_data,"messages": [ToolMessage(content=f"已获取用户 {user_id} 的详细信息",tool_call_id=tool_call_id)],"query_status": "user_info_fetched"})# 3. 定义触发工具调用的节点
def call_tool(state: SupportState) -> SupportState:"""创建工具调用消息的节点"""print(f"\n===== 触发工具调用 =====")tool_call_message = AIMessage(content="",tool_calls=[{"name": "lookup_user_info","args": {},  # 无额外参数"id": "call_123"}])return SupportState(messages=state.messages + [tool_call_message])# 4. 定义后续处理节点
def handle_support_request(state: SupportState) -> SupportState:"""处理客户请求的节点(依赖工具返回的用户信息)"""print(f"\n===== 开始处理客户请求 =====")print(f"客户ID: {state.user_id}")print(f"客户信息: {state.user_info}")print(f"当前状态: {state.query_status}")print(f"消息记录: {state.messages}")# 基于用户信息进行业务处理if state.user_info.get("membership") == "白金会员":priority_msg = "优先处理白金会员请求"else:priority_msg = "标准流程处理请求"# 更新最终状态return SupportState(user_id=state.user_id,user_info=state.user_info,messages=state.messages + [priority_msg, "客户请求处理完成"],query_status="completed")# 5. 构建图谱(使用ToolNode处理Command)
def build_support_graph():builder = StateGraph(SupportState)# 创建ToolNodetool_node = ToolNode([lookup_user_info])# 添加节点builder.add_node("call_tool", call_tool)builder.add_node("tools", tool_node)builder.add_node("process_request", handle_support_request)# 定义边builder.add_edge(START, "call_tool")builder.add_edge("call_tool", "tools")builder.add_edge("tools", "process_request")builder.add_edge("process_request", END)return builder.compile(checkpointer=MemorySaver())# 6. 运行示例
if __name__ == "__main__":support_graph = build_support_graph()print("=== 客户支持流程开始 ===")result = support_graph.invoke({"user_id": "CUST-789"})print("\n===== 流程结束 =====")print(f"最终用户信息: {result['user_info']['name']} ({result['user_info']['membership']})")print(f"流程完成状态: {result['query_status']}")print(f"完整消息历史: {result['messages']}")
  • @Tool 工具Command对象返回值:

    • update参数需包含业务数据(如用户信息、查询结果)和消息历史

    • messages字段必须包含ToolMessage,且需关联对应的tool_call_id
      (这是 LLM 提供商要求的格式,确保工具调用与结果在消息链中正确关联)

  • ToolNode 的核心作用:

    • 自动 Command 处理:解析工具返回的 Command 对象并更新状态

    • 消息历史维护:自动添加 ToolMessage 到消息序列

    • 错误处理:内置工具执行异常捕获机制

    • ID 管理:自动处理 tool_call_id 的生成和关联

人机协同工作流(Human-in-the-Loop)

        Command还可以与 interrupt() 结合实现人工审核中断与恢复:

from typing import TypedDict
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
from langgraph.types import Command, interrupt# 新增模型相关导入
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv()# 定义状态类型
class State(TypedDict):some_text: str# 初始化大模型(需要设置OPENAI_API_KEY环境变量)
model = ChatOpenAI(model="gpt-4o-mini")# 初始化检查点存储
checkpointer = MemorySaver()# 定义人类介入节点
def human_node(state: State):value = interrupt({"text_to_revise": state["some_text"],"instructions": "请修改以下文本:"})return {"some_text": value}# 修改后的自动处理节点(调用大模型)
def process_text(state: State):# 构造模型请求message = model.invoke([HumanMessage(content=f"请处理以下请求:{state['some_text']}。保持回答简洁。")])# 返回模型生成的文本return {"some_text": message.content}# 构建工作流
graph_builder = StateGraph(State)# 添加节点
graph_builder.add_node("human_review", human_node)
graph_builder.add_node("auto_process", process_text)# 设置流程
graph_builder.set_entry_point("auto_process")
graph_builder.add_edge("auto_process", "human_review")# 编译图表
graph = graph_builder.compile(checkpointer=checkpointer,interrupt_before=["human_review"]
)# 使用示例(保持不变)
if __name__ == "__main__":thread_id = "thread_123"thread_config = {"configurable": {"thread_id": thread_id}}initial_state = {"some_text": "输出一个五五乘法表"}result = graph.invoke(initial_state, config=thread_config)print("自动处理结果:", result["some_text"])human_input = input("请输入人类输入:")resume_result = graph.invoke(Command(resume=human_input),config=thread_config)print("最终结果:", resume_result["some_text"])

最佳实践与注意事项

  1. 类型标注不可少-> Command[Literal["node_a", "node_b"]] 是 LangGraph 静态检查和绘图的基础。

  2. 跨图状态设计:子图跳转父图更新共享状态时,父图需定义 reducer 处理冲突(如 lambda current, update: update)。

  3. 命名一致性goto 指定的节点名必须与图中注册的节点名完全一致。

  4. 工具安全调用:在工具中使用 Command 时,确保状态更新不会破坏图的一致性。

参考文献

Overview

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91687.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91687.shtml
英文地址,请注明出处:http://en.pswp.cn/web/91687.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【目标检测】小样本度量学习

小样本度量学习(Few-Shot Metric Learning)通常用于分类任务​(如图像分类),但它也可以与目标检测(Object Detection)结合,解决小样本目标检测(Few-Shot Object Detectio…

cmd怎么取消关机命令

在 Windows 的命令提示符(CMD)中取消已计划的关机操作,可以通过 shutdown 命令的 ​**-a**​ 参数实现。以下是具体步骤:​操作方法​​打开 CMD​按下 Win R 组合键,输入 cmd 并回车,打开命令提示符窗口。…

网易云音乐硬刚腾讯系!起诉SM娱乐滥用市场支配地位

企查查APP显示,近日,法院公开杭州乐读科技有限公司、杭州网易云音乐科技有限公司起诉SM ENTERTAINMENT CO. 、卡斯梦(上海)文化传播有限公司等开庭信息,案由涉及滥用市场支配地位纠纷。公告显示,该案件计划…

[css]切角

使用css实现一个切角的功能&#xff0c;有以下几种方案&#xff1a; <div class"box"></div>方案一&#xff1a;linear-gradient linear-gradient配合backgroud-image可以实现背景渐变的效果。linear-gradient的渐变过渡区的占比是总的空间&#xff08;高…

分享一个可以测试离线服务器性能的脚本

在日常运维工作中&#xff0c;经常会遇到系统性能莫名跟不上业务需求的情况&#xff1a;服务器响应变慢、应用加载卡顿、资源占用异常飙升等问题频繁出现&#xff0c;却难以快速问题根源究竟在CPU过载、内存泄漏、磁盘I/O阻塞还是网络带宽瓶颈。这种时候&#xff0c;特别需要一…

Python Pandas.unique函数解析与实战教程

Python Pandas.unique 函数解析与实战教程 摘要 本文章旨在全面地解析 pandas 库中的 unique 函数。pandas.unique 是一个用于从一维数组型(array-like)对象中提取唯一值的高效工具。我们将从其核心功能、函数签名、参数详解、返回值类型,到关键行为特性(如顺序保留、缺失…

排序算法入门:直接插入排序详解

这里写目录标题介绍原理代码实现分析介绍 直接插入排序是一种简单直观的排序算法&#xff0c;适用于小规模数据或基本有序的数据集。其核心思想是构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。 原理 我们…

ClickHouse MergeTree引擎:从核心架构到三级索引实战

摘要 MergeTree是ClickHouse最核心的存储引擎&#xff0c;采用列式存储LSM-Tree架构设计&#xff0c;支持高效的数据写入、合并和查询。本文将全面解析MergeTree引擎的基础概念、数据流、核心架构、索引系统以及常见问题。 基础篇&#xff1a; 一、MergeTree引擎基础概念 1. 定…

电脑手机热点方式通信(上)

电脑连接手机热点时的无线链路情况&#xff1a; 电脑上网时&#xff08;从服务器下载数据&#xff0c;或者上传指令、数据&#xff09;&#xff0c;首先电脑与手机之间基于WiFi协议在2.4G频段或者5G频段通信&#xff0c;然后手机与基站之间再基于4G LTE或者5G NR协议在2412MHz…

MySQL CPU占用过高排查指南

MySQL CPU 占用过高时&#xff0c;排查具体占用资源的表需结合系统监控、数据库分析工具和 SQL 诊断命令。&#x1f50d; ​一、快速定位问题根源​​确认 MySQL 进程占用 CPU​使用 top 或 htop 命令查看系统进程&#xff0c;确认是否为 mysqld 进程导致 CPU 飙升。若 MySQL 进…

软件交付终极闸口:验收测试全解析

验收测试&#xff1a;软件交付的关键环节 目录 验收测试&#xff1a;软件交付的关键环节 一、验收测试&#xff1a;软件交付的终极闸口 核心目标与作用 在 SDLC 中的位置 二、验收测试类型详解&#xff1a;精准匹配业务场景 三、验收测试全流程解析&#xff1a;从计划到…

深度学习核心:卷积神经网络 - 原理、实现及在医学影像领域的应用

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#,Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发…

多线程(二) ~ 线程核心属性与状态

文章目录一. 线程创建&#xff08;start&#xff09;&#xff08;一&#xff09;继承Thread类&#xff0c;重写run&#xff08;二&#xff09;继承Runnable类&#xff0c;重写run&#xff08;三&#xff09;Thread匿名内部类重写&#xff08;四&#xff09;Runnable匿名内部类重…

Linux---编辑器vim

一、vim的基本概念1.三种模式①命令模式控制屏幕光标的移动&#xff0c;字符、字或行的删除&#xff0c;移动复制某区段及进入插入模式或者进去底行模式②插入模式可进行文本输入&#xff0c;按Esc回到命令行模式③底行模式文件保存或退出&#xff0c;也可以进行文件替换&#…

如何在 Ubuntu 24.04 或 22.04 LTS Linux 上安装 Guake 终端应用程序

通过本教程的简单步骤,在 Ubuntu 24.04 或 22.04 LTS Jammy JellyFish 上安装 Guake 终端以运行命令。 Guake(基于 Quake)是一个基于 Python 的终端模拟器。Guake 的行为类似于 Quake 中的终端:通过某个按键(热键)按下时,窗口会从屏幕顶部滚下来,再次按下相同的按键时…

谷歌Gemini 2.5重磅应用:多模态研究助手Multi-Modal Researcher,实现全网自动研究与AI播客生成

在人工智能赋能科研与内容创作的浪潮中,谷歌基于其最新大模型 Gemini 2.5 推出了突破性工具 Multi-Modal Researcher。这一系统通过整合多模态数据(文本、视频、实时网络信息),实现了从自动研究到内容生成的全流程自动化。用户只需输入研究主题或YouTube视频链接,系统即可…

防御综合实验

一、实验拓补图二、实验需求及配置需求一设备接口VLAN接口类型SW2GE0/0/2VLAN 10AccessGE0/0/3VLAN 20AccessGE0/0/1VLAN List : 10 20Trunk[SW2]vlan 10 [SW2]vlan 20 [SW2]interface GigabitEthernet 0/0/2 [SW2-GigabitEthernet0/0/2]port link-type access [SW2-GigabitEt…

堆----2.前 K 个高频元素

347. 前 K 个高频元素 - 力扣&#xff08;LeetCode&#xff09; /** 桶排序: 首先遍历数组,使用HashMap统计每个元素出现的次数 创建一个大小为length 1的List数组,下标代表元素出现次数,出现次数一致的元素放在同一个数组中 倒数遍历List数组即可得得到前K个高频元素 细节注…

如何分析Linux内存性能问题

一、Linux中的buffer与cache的区别 Linux的内存管理与监控_linux服务器虚假内存和真实内存怎么区分-CSDN博客文章浏览阅读66次。本文主要是关于【Linux系统的物理内存与虚拟内存讲解】【重点对虚拟内存的作用与用法进行了讲解说明】【最后还对如何新增扩展、优化、删除内存交换…

二次型 线性代数

知识结构总览首先是我们的二次型的定义&#xff0c;就是说什么样的才算是一个二次型。然后就是如何把二次型化为标准型&#xff0c;最后就是正定二次型的定义和判断的一些条件。二次型的定义二次型其实是一种函数表达的方式&#xff0c;如上&#xff0c;含义其实就是每个项都是…