LangGraph学习笔记五(Command+Send+Runtime)

一.Command使用

python 复制代码
"""
CommandDemo.py | LangGraph 1.0.6 正式版
Command 基础演示:状态更新+流程控制+动态路由
"""
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command

# 全局常量:统一递归限制,便于维护
RECURSION_LIMIT = 50

# 定义状态
class AgentState(TypedDict):
    messages: Annotated[list, lambda x, y: x + y]  # 自动合并消息
    current_agent: str
    task_completed: bool

#{'messages': [('user', '我需要计算数学题')], 'current_agent': 'user', 'task_completed': False}

# 决策代理(核心路由节点)
def decision_agent(state: AgentState) -> Command[AgentState]:
    """根据消息内容路由代理,任务完成则直接终止"""
    print("执行节点: decision_agent")
    # 优先终止流程(核心防循环逻辑)
    if state["task_completed"]:
        print("✅ 检测到任务已完成,直接终止流程")
        return Command(
            update={"messages": [("system", "所有任务处理完成,流程正常结束")]},
            goto=END
        )
    # 提取消息文本(兼容空消息)
    last_message = state["messages"][-1] if state["messages"] else ("", "")
    last_msg_content = last_message[1]   #last_message = ("user", "我需要计算数学题")
    print(f"最新消息文本: {last_msg_content}")

    # 动态路由
    if "数学" in last_msg_content:
        print("✅ 检测到数学任务,路由到数学代理")
        return Command(
            update={"messages": [("system", "路由到数学代理")], "current_agent": "math_agent"},
            goto="math_agent"
        )
    elif "翻译" in last_msg_content:
        print("✅ 检测到翻译任务,路由到翻译代理")
        return Command(
            update={"messages": [("system", "路由到翻译代理")], "current_agent": "translation_agent"},
            goto="translation_agent"
        )
    else:
        print("❌ 未识别任务类型,标记任务完成并终止")
        return Command(
            update={"messages": [("system", "任务完成")], "task_completed": True},
            goto=END
        )


# 数学代理(业务节点)
def math_agent(state: AgentState) -> Command[AgentState]:
    """处理数学计算任务,完成后返回决策代理"""
    print("执行节点: math_agent")
    result = "2 + 2 = 4"
    print(f"计算结果: {result}")
    return Command(
        update={
            "messages": [("assistant", f"数学计算结果: {result}")],
            "current_agent": "decision_agent",
            "task_completed": True
        },
        goto="decision_agent"
    )


# 翻译代理(业务节点)
def translation_agent(state: AgentState) -> Command[AgentState]:
    """处理中英翻译任务,完成后返回决策代理"""
    print("执行节点: translation_agent")
    translation = "Hello -> 你好"
    print(f"翻译结果: {translation}")
    return Command(
        update={
            "messages": [("assistant", f"翻译结果: {translation}")],
            "current_agent": "decision_agent",
            "task_completed": True
        },
        goto="decision_agent"
    )


def main():
    """演示Command基础用法:状态更新+动态路由+流程终止"""
    print("=== Command 基础演示(LangGraph 1.0.6)===\n")

    # 1. 构建状态图
    builder = StateGraph(AgentState)
    builder.add_node("decision_agent", decision_agent)
    builder.add_node("math_agent", math_agent)
    builder.add_node("translation_agent", translation_agent)

    # 2. 定义边(完整节点关系)
    builder.add_edge(START, "decision_agent")
    builder.add_edge("math_agent", "decision_agent")
    builder.add_edge("translation_agent", "decision_agent")
    builder.add_edge("decision_agent", END)

    # 3. 编译图
    graph = builder.compile()

    # 测试1:数学任务
    print("【测试1: 数学任务】")
    initial_state = {"messages": [("user", "我需要计算数学题")], "current_agent": "user", "task_completed": False}
    print("初始状态:", initial_state)
    result = graph.invoke(initial_state, recursion_limit=RECURSION_LIMIT)
    print("最终状态(简化):", {k: v for k, v in result.items() if k != "messages"})  # 简化输出
    print("\n" + "-" * 50 + "\n")

    # 测试2:翻译任务
    print("【测试2: 翻译任务】")
    initial_state = {"messages": [("user", "我需要翻译文本")], "current_agent": "user", "task_completed": False}
    print("初始状态:", initial_state)
    result = graph.invoke(initial_state, recursion_limit=RECURSION_LIMIT)
    print("最终状态(简化):", {k: v for k, v in result.items() if k != "messages"})
    print("\n" + "-" * 50 + "\n")

    # 测试3:未识别任务
    print("【测试3: 未识别任务类型】")
    initial_state = {"messages": [("user", "你好")], "current_agent": "user", "task_completed": False}
    print("初始状态:", initial_state)
    result = graph.invoke(initial_state, recursion_limit=RECURSION_LIMIT)
    print("最终状态(简化):", {k: v for k, v in result.items() if k != "messages"})

    # 新增:可视化图结构(教学演示必备)
    print("\n=== 图结构可视化 ===")
    print(graph.get_graph().draw_mermaid())


if __name__ == "__main__":
    main()

执行结果:

二.context_schema参数和Runtime用法

python 复制代码
"""
RuntimeContextDemo.py

LangGraph Context Schema 演示

演示如何在 LangGraph 1.0 中使用 context_schema 向节点传递不属于图表状态的信息。
这在传递模型名称、数据库连接等依赖项时非常有用。
"""

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.runtime import Runtime
from langchain_core.messages import AIMessage, HumanMessage
from dataclasses import dataclass


# 定义状态结构
class AgentState(TypedDict):
    messages: Annotated[list, lambda x, y: x + y]
    response: str


# 定义上下文结构
@dataclass
class ContextSchema:
    model_name: str
    db_connection: str
    api_key: str


# 节点函数:处理用户消息
def process_message(state: AgentState, runtime: Runtime[ContextSchema]) -> dict:
    """处理用户消息的节点,使用context中的信息"""
    print("执行节点: process_message")

    # 获取最新的用户消息
    last_message = state["messages"][-1].content if state["messages"] else ""
    print(f"用户消息: {last_message}")
    print("=========以下是从RuntimeContext中获得信息=========")
    # 使用runtime.context中的信息
    model_name = runtime.context.model_name
    db_connection = runtime.context.db_connection
    api_key = runtime.context.api_key

    print(f"使用的模型: {model_name}")
    print(f"数据库连接: {db_connection}")
    print(f"API密钥前缀: {api_key[:5]}***")  # 只显示前5位,隐藏其余部分

    # 模拟使用这些信息处理请求
    response = f"使用 {model_name} 处理了您的请求,已连接到 {db_connection}"

    return {
        "messages": [AIMessage(content=response)],
        "response": response
    }


# 节点函数:生成最终响应
def generate_response(state: AgentState, runtime: Runtime[ContextSchema]) -> dict:
    """生成最终响应的节点"""
    print("执行节点: generate_response")

    # 使用runtime.context中的信息
    model_name = runtime.context.model_name
    print(f"使用模型 {model_name} 生成最终响应")

    # 获取之前的结果
    previous_response = state["response"]

    # 生成更详细的响应
    final_response = f"{previous_response}\n\n这是使用 {model_name} 生成的完整响应。"

    return {
        "messages": [AIMessage(content=final_response)],
        "response": final_response
    }


def main():
    """演示 context_schema 的使用"""
    print("=== Context Schema 演示 ===\n")

    # 定义上下文
    context = ContextSchema(
        model_name="gpt-4-turbo",
        db_connection="postgresql://user:pass@localhost:5432/orders_db",
        api_key="sk-abcdefghijklmnopqrstuvwxyz123456"
    )

    # 创建图,指定state_schema和context_schema
    builder = StateGraph(AgentState, context_schema=ContextSchema)

    # 添加节点
    builder.add_node(node="process_message", action=process_message)
    builder.add_node("generate_response", generate_response)

    # 添加边
    builder.add_edge(START, "process_message")
    builder.add_edge("process_message", "generate_response")
    builder.add_edge("generate_response", END)

    # 编译图
    graph = builder.compile()

    # 定义初始状态
    initial_state = {
        "messages": [HumanMessage(content="请帮我查询最新的订单信息")],
        "response": ""
    }

    print("初始状态:", initial_state)
    print()
    print("上下文信息:\n", {
        "model_name": context.model_name,
        "db_connection": context.db_connection,
        "api_key": f"{context.api_key[:5]}***"
    })
    print("\n" + "-" * 50 + "\n")

    # 执行图,通过context参数传递上下文
    result = graph.invoke(initial_state, context=context)

    print("\n" + "=" * 50)
    print("最终状态:", result)
    print("\n最终响应:")
    print(result["response"])


if __name__ == "__main__":
    main()

执行结果:

三.Send用法

python 复制代码
"""
SendDemo.py
LangGraph Map-Reduce 模式演示
通过使用 Send 对象,LangGraph 提供了一种优雅的方式来实现这种动态图结构,
使得我们可以根据运行时状态来决定执行路径。

解释:
(1)首先执行 generate_subjects主题列表节点,生成主题列表:['猫', '狗', '程序员']
(2)然后通过条件边函数 map_subjects_to_jokes 为每个主题创建一个 Send 对象
(3)make_joke 节点被并行执行3次,每次处理一个主题
(4)最终将所有生成的笑话合并到一个列表中
这种模式非常适合处理动态数量的任务
"""

from typing import Annotated, List, Sequence
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.types import Send


# 定义状态
class AtguiguState(TypedDict):
    subjects: List[str]
    jokes: Annotated[List[str], lambda x, y: x + y]  # 使用列表合并的方式


# 第一个节点:生成需要处理的主题列表
def generate_subjects(state: AtguiguState) -> dict:
    """生成需要处理的主题列表"""
    print("执行节点(第一个节点:生成需要处理的主题列表): generate_subjects")
    subjects = ["猫", "狗", "程序员"]
    print(f"生成主题列表: {subjects}")
    return {"subjects": subjects}


# Map节点:为每个主题生成笑话
def make_joke(state: AtguiguState) -> dict:
    """为单个主题生成笑话"""
    subject = state.get("subject", "未知")
    print(f"执行节点: make_joke,处理主题: {subject}")

    # 根据主题生成相应笑话
    jokes_map = {
        "猫": "为什么猫不喜欢在线购物?因为它们更喜欢实体店!",
        "狗": "为什么狗不喜欢计算机?因为它们害怕被鼠标咬!",
        "程序员": "为什么程序员喜欢洗衣服?因为他们在寻找bugs!",
        "未知": "这是一个关于未知主题的神秘笑话。"
    }

    joke = jokes_map.get(subject, f"这是一个关于{subject}的即兴笑话。")
    print(f"生成笑话: {joke}")
    return {"jokes": [joke]}


# 条件边函数:根据主题列表生成Send对象列表
def map_subjects_to_jokes(state: AtguiguState) -> List[Send]:
    """将主题列表映射到joke生成任务"""
    print("执行条件边函数: map_subjects_to_jokes")
    subjects = state["subjects"]
    print(f"映射主题到joke任务: {subjects}")

    # 为每个主题创建一个Send对象,指向make_joke节点
    # 每个Send对象包含节点名称和传递给该节点的状态
    send_list = [Send("make_joke", {"subject": subject}) for subject in subjects]
    print(f"生成Send对象列表: {send_list}")
    return send_list


def main():
    """演示Map-Reduce模式"""
    print("=== Map-Reduce 模式演示 ===\n")

    # 创建图
    builder = StateGraph(AtguiguState)

    # 添加节点
    builder.add_node("generate_subjects", generate_subjects)
    builder.add_node("make_joke", make_joke)

    # 添加边
    builder.add_edge(START, "generate_subjects")

    # 添加条件边,使用Send对象实现map-reduce
    builder.add_conditional_edges(
        source="generate_subjects",  # 源节点
        path=map_subjects_to_jokes  # 路由函数,返回Send对象列表
    )

    # 从make_joke到结束
    builder.add_edge("make_joke", END)

    # 编译图
    graph = builder.compile()
    print(graph.get_graph().print_ascii())

    # 执行图
    initial_state = {"subjects": [], "jokes": []}
    print("初始状态:", initial_state)
    print("\n开始执行图...")

    result = graph.invoke(initial_state)
    print(f"\n最终结果: {result}")

    print("\n=== 演示完成 ===")


if __name__ == "__main__":
    main()

执行结果:

相关推荐
代码搬运媛11 小时前
【前端必知】浏览器原生 API 底层机制详解
前端
咔咔库奇11 小时前
js-执行上下文
开发语言·前端·javascript
咪饭只吃一小碗11 小时前
JS 打工记:同步搬砖、异步摸鱼,Promise 来救场
前端·javascript·面试
用户7138742290011 小时前
彻底搞懂浏览器客户端存储:从 localStorage 到完整存储体系
前端
掘金安东尼12 小时前
前端周刊第 467 期 🗂 本期精选目录
前端
qcx2312 小时前
【系统学AI】07 ReAct范式:从奠基之作到Reflexion/RAF的演进
前端·人工智能·react.js
无糖可可果12 小时前
从 Python List 到 LLM:一个开发者的 AI 学习之路
前端
一点一木12 小时前
Claude Opus 4.8 实测:AI 终于学会「承认自己不知道」了?
前端·人工智能·claude
克里斯前端12 小时前
SSE实践(1)
前端