LangGraph Send 函数演示 - 动态并发路由

python 复制代码
"""
LangGraph Send 函数演示 - 动态并发路由
=========================================

本示例展示如何使用 Send 实现:
1. 动态决定要执行哪些节点
2. 并发执行多个节点(不是顺序执行)
3. 等待所有并发节点完成后进行汇总

场景:用户查询包含多个关键词,根据关键词动态路由到不同的处理器
"""

import asyncio
import time
from typing import List, TypedDict, Annotated, Literal
from datetime import datetime
from langgraph.graph import StateGraph, START, END
from langgraph.graph.state import Send
import operator


# ===================== 1. 定义状态结构 =====================
class ProcessState(TypedDict):
    """工作流的共享状态"""
    query: str                                          # 用户查询
    keywords: List[str]                                # 检测到的关键词
    results: Annotated[List[dict], operator.add]       # 所有处理器的结果(支持并发追加)
    final_answer: str                                  # 最终答案


# ===================== 2. 节点函数定义 =====================
def analyze_query(state: ProcessState) -> dict:
    """
    第1步:分析查询并提取关键词
    根据关键词决定调用哪些处理器
    """
    print("\n📊 [分析节点] 开始分析查询...")
    query = state["query"]
    
    # 模拟关键词检测
    keywords = []
    if any(word in query.lower() for word in ["weather", "温度", "天气", "下雨"]):
        keywords.append("weather")
    if any(word in query.lower() for word in ["news", "新闻", "头条"]):
        keywords.append("news")
    if any(word in query.lower() for word in ["stock", "股票", "行情"]):
        keywords.append("stock")
    if any(word in query.lower() for word in ["covid", "疫情", "感染"]):
        keywords.append("covid")
    
    # 至少有一个关键词
    if not keywords:
        keywords = ["weather"]
    
    print(f"  ✓ 分析完成,检测到关键词: {keywords}")
    return {"keywords": keywords}


def route_to_processors(state: ProcessState) -> List:
    """
    第2步:根据关键词动态路由到不同的处理器
    返回要执行的 Send 对象列表,LangGraph 会并发执行它们
    
    这是 Send 的关键用法:
    - 每个 Send(node_name, state) 创建一个要执行的节点
    - 返回列表中的所有节点会并发执行
    """
    print("\n⚡ [路由节点] 动态分发处理任务...")
    
    sends = []
    keywords = set(state.get("keywords", []))
    
    # 根据关键词动态创建 Send 对象
    # 这些节点会并发执行(不是顺序执行)
    if "weather" in keywords:
        print("  → 分派给天气处理器")
        sends.append(Send("weather_processor", state))
    
    if "news" in keywords:
        print("  → 分派给新闻处理器")
        sends.append(Send("news_processor", state))
    
    if "stock" in keywords:
        print("  → 分派给股票处理器")
        sends.append(Send("stock_processor", state))
    
    if "covid" in keywords:
        print("  → 分派给疫情处理器")
        sends.append(Send("covid_processor", state))
    
    return sends


# ===================== 3. 各个处理器节点(并发执行)=====================
def weather_processor(state: ProcessState) -> dict:
    """天气数据处理器"""
    print("    🌤️  [天气处理器] 开始处理... (延迟2秒)")
    time.sleep(2)  # 模拟处理延迟
    
    result = {
        "processor": "Weather",
        "data": "北京今日温度 15-22°C,有小雨,请携带雨具",
        "processing_time": 2.0
    }
    print(f"    ✓ [天气处理器] 完成!用时 {result['processing_time']}秒")
    
    return {"results": [result]}


def news_processor(state: ProcessState) -> dict:
    """新闻数据处理器"""
    print("    📰 [新闻处理器] 开始处理... (延迟1.5秒)")
    time.sleep(1.5)  # 模拟处理延迟
    
    result = {
        "processor": "News",
        "data": "头条新闻:AI 技术突破,OpenAI 发布新模型",
        "processing_time": 1.5
    }
    print(f"    ✓ [新闻处理器] 完成!用时 {result['processing_time']}秒")
    
    return {"results": [result]}


def stock_processor(state: ProcessState) -> dict:
    """股票数据处理器"""
    print("    📈 [股票处理器] 开始处理... (延迟3秒)")
    time.sleep(3)  # 模拟处理延迟
    
    result = {
        "processor": "Stock",
        "data": "沪深300指数上涨 2.5%,科技板块领涨",
        "processing_time": 3.0
    }
    print(f"    ✓ [股票处理器] 完成!用时 {result['processing_time']}秒")
    
    return {"results": [result]}


def covid_processor(state: ProcessState) -> dict:
    """疫情数据处理器"""
    print("    🏥 [疫情处理器] 开始处理... (延迟2.5秒)")
    time.sleep(2.5)  # 模拟处理延迟
    
    result = {
        "processor": "COVID",
        "data": "全球新增感染数下降 15%,疫苗接种率达 70%",
        "processing_time": 2.5
    }
    print(f"    ✓ [疫情处理器] 完成!用时 {result['processing_time']}秒")
    
    return {"results": [result]}


# ===================== 4. 结果汇总节点 =====================
def synthesize_results(state: ProcessState) -> dict:
    """汇总所有处理器的结果"""
    print("\n🔗 [汇总节点] 整合处理结果...")
    
    if not state["results"]:
        final_answer = "未找到任何处理结果"
    else:
        answer_parts = [f"📌 针对查询「{state['query']}」的处理结果:\n"]
        
        for res in state["results"]:
            answer_parts.append(f"  【{res['processor']}】")
            answer_parts.append(f"    {res['data']}\n")
        
        final_answer = "".join(answer_parts)
    
    print(f"  ✓ 汇总完成,包含 {len(state['results'])} 个结果")
    return {"final_answer": final_answer}


# ===================== 5. 构建 LangGraph 工作流 =====================
def build_concurrent_workflow():
    """
    构建包含动态并发路由的工作流
    
    工作流结构:
    START
      ↓
    analyze_query (分析关键词)
      ↓
    route_to_processors (动态路由,返回多个Send)
      ↓
    ┌─────────────┬──────────────┬──────────────┬──────────────┐
    ↓             ↓              ↓              ↓              ↓
    weather   news_proc       stock_proc     covid_proc     (并发🚀)
    
    └─────────────┴──────────────┴──────────────┴──────────────┘
                               ↓
                        synthesize_results (等待全部完成)
                               ↓
                              END
    """
    
    # 创建图
    graph = StateGraph(ProcessState)
    
    # 添加节点
    graph.add_node("analyze", analyze_query)
    graph.add_node("weather_processor", weather_processor)
    graph.add_node("news_processor", news_processor)
    graph.add_node("stock_processor", stock_processor)
    graph.add_node("covid_processor", covid_processor)
    graph.add_node("synthesize", synthesize_results)
    
    # 添加边:从 START 开始
    graph.add_edge(START, "analyze")
    
    # 关键:条件路由 - 根据分析结果动态决定调用哪些处理器
    # route_to_processors 返回要执行的 Send 对象列表
    graph.add_conditional_edges("analyze", route_to_processors)
    
    # 所有处理器完成后进入汇总
    graph.add_edge("weather_processor", "synthesize")
    graph.add_edge("news_processor", "synthesize")
    graph.add_edge("stock_processor", "synthesize")
    graph.add_edge("covid_processor", "synthesize")
    
    # 汇总后结束
    graph.add_edge("synthesize", END)
    
    return graph.compile()


# ===================== 6. 执行示例 =====================
async def run_example():
    """运行并发路由示例"""
    
    # 创建工作流
    workflow = build_concurrent_workflow()
    
    # 测试用例
    test_queries = [
        "北京今天天气怎么样?",
        "最新的科技新闻和股票行情是什么?",
        "疫情的最新进展和天气预报",
        "请告诉我所有信息:天气、新闻、股票、疫情"
    ]
    
    for i, query in enumerate(test_queries, 1):
        print("\n" + "=" * 80)
        print(f"📋 测试查询 {i}/{len(test_queries)}")
        print("=" * 80)
        
        # 记录开始时间
        start_time = time.time()
        
        # 执行工作流
        initial_state = {
            "query": query,
            "keywords": [],
            "results": [],
            "final_answer": ""
        }
        
        final_state = await workflow.ainvoke(initial_state)
        
        # 计算耗时
        elapsed_time = time.time() - start_time
        
        # 输出结果
        print(final_state["final_answer"])
        print(f"\n⏱️  总耗时:{elapsed_time:.2f}秒")
        print(f"📊 处理器数量:{len(final_state['results'])}")
        
        # 性能分析
        if final_state["results"]:
            max_time = max(r["processing_time"] for r in final_state["results"])
            print(f"🚀 性能提升:顺序执行需要 {sum(r['processing_time'] for r in final_state['results']):.1f}秒,"
                  f"并发执行只需 {elapsed_time:.2f}秒")


if __name__ == "__main__":
    print("""
╔════════════════════════════════════════════════════════════════════════════╗
║           LangGraph Send 函数演示 - 动态并发路由                          ║
╚════════════════════════════════════════════════════════════════════════════╝

本示例展示:
✅ 如何使用 Send 实现动态路由
✅ 多个处理器的并发执行(不是顺序执行)
✅ 等待所有处理完成后的结果汇总

关键点:
• route_to_processors 返回 Send 对象列表
• 返回列表中的所有节点会 并发运行
• LangGraph 自动等待所有节点完成
    """)
    
    asyncio.run(run_example())

📊 执行结果分析

核心演示 - Send 的并发优势

查询 处理器 顺序耗时 并发耗时 性能提升
1 天气(1个) 2.0s 2.01s -
2 新闻+股票(2个) 4.5s 3.01s ⚡ 33% 更快
3 天气+疫情(2个) 4.5s 2.51s ⚡ 44% 更快
4 全部(4个) 9.0s 3.01s ⚡ 66% 更快

🔑 关键代码亮点

1. 定义动态路由函数

python 复制代码
def route_to_processors(state: ProcessState) -> List:
    """返回要执行的 Send 对象列表"""
    sends = []
    if "weather" in keywords:
        sends.append(Send("weather_processor", state))
    if "news" in keywords:
        sends.append(Send("news_processor", state))
    # ... 其他处理器
    return sends  # 列表中的所有节点会并发执行

2. LangGraph 配置

python 复制代码
graph.add_conditional_edges("analyze", route_to_processors)
# 这一行使得 route_to_processors 返回的所有 Send 对象并发执行

3. 结果追加操作(支持并发)

python 复制代码
results: Annotated[List[AgentOutput], operator.add]
# operator.add 允许多个并发节点同时追加结果

📌 工作流结构

复制代码
分析节点 → 检测关键词 → 动态路由
                         ↓
        ┌────────────────┼────────────────┬─────────────┐
        ↓                ↓                ↓             ↓
     天气处理        新闻处理         股票处理       疫情处理
     (2秒)          (1.5秒)          (3秒)         (2.5秒)
        ↓                ↓                ↓             ↓
        └────────────────┼────────────────┴─────────────┘
                         ↓
                    汇总结果(只需3秒!)

🎯 使用场景

这种模式适用于:

  • ✅ 多源数据聚合(GitHub + Notion + Slack)
  • ✅ 并行 API 调用
  • ✅ 分布式任务处理
  • ✅ 动态工作流编排
相关推荐
码农小韩3 小时前
AIAgent应用开发——DeepSeek分析(一)
人工智能·python·深度学习·agent·强化学习
ZaneAI4 小时前
🚀 Vercel AI SDK 使用指南:图像生成 (Image Generation)
llm·agent
ZaneAI6 小时前
🚀 Vercel AI SDK 使用指南:模型设置 (Settings)
react.js·agent
ZaneAI6 小时前
🚀 Vercel AI SDK 使用指南: 提示词工程 (Prompt Engineering)
react.js·agent
沛沛老爹9 小时前
AI助手专业能力评估体系构建:四大维度+工具链实战
人工智能·自动化·prompt·agent·评估模板
weixin_4404016910 小时前
Coze-智能体Agent(工作流:批处理+图像生成+视频生成+)未完待续
python·ai·agent·coze
会算数的⑨10 小时前
Spring AI Alibaba 学习(三):Graph Workflow 深度解析(上篇)
java·人工智能·后端·学习·阿里云·agent·saa
laplace01231 天前
temperature定义与使用
agent·claude·rag·mcp·skills