Agent几种不同的执行方式

🧩 Agent 执行的核心方式

以下是Agent的主要执行方式对比:

执行方式 方法名 类型 核心特点与适用场景
同步调用 invoke() 同步 最基础的调用方式。传入单个输入,程序会阻塞直到Agent完成所有步骤并返回最终结果。适用于对实时性要求不高的后台任务或简单脚本。
异步调用 ainvoke() 异步 invoke的异步版本。不会阻塞主线程,适合在 async 函数中调用,或在Web应用等并发环境下使用。
同步流式 stream() 同步 逐步返回结果。可以实时获取Agent执行过程中产生的数据块(如LLM的token)。适合构建命令行工具,让用户即时看到反馈。
异步流式 astream() 异步 stream的异步版本。在异步环境中逐步获取输出,是构建高性能流式应用的基础。
同步批处理 batch() 同步 高效处理多个输入。默认使用线程池并行执行 invoke(),显著提升吞吐量。适合离线批量数据处理。
异步批处理 abatch() 异步 batch的异步版本。在异步环境中并发处理多个输入,是构建高并发异步批处理服务的理想选择。
事件流式 astream_events() 异步 最强大的流式接口。不仅能流式输出最终结果,还能流式输出Agent内部执行过程的所有事件。是实现复杂UI反馈、日志记录、调试等高级功能的首选。

接下来我们主要针对astream和astream_events举例: 代码如下:

js 复制代码
async def generate_label_analysis_stream_response_v2(tips: str):
    system_prompt = """
            你现在是一个标签分析师,请根据以下信息输出标签的分析报告:
            1、标签元数据信息{label_detail_info_dict}
            2、标签的维度值分布{label_dimension_distribute}
            3、标签关联的人群包信息{crowd_label_relation}
            
            要求:
                1、标签的维度值分布结果需要附加饼状图(饼状图已html的格式输出,并能支持在浏览器打开),并显示各维值的占比
                2、输出的分析结果以markdown的格式输出
    """
    tools = [query_label_info, query_label_distribution, get_crowd_by_label]
    agent = create_agent(model=llm, tools=tools, system_prompt=system_prompt)
    current_node = None
    """
        方案一:agent.astream({"messages": [{"role": "user", "content": tips}]}, stream_mode="messages")
               astream 搭配 stream_mode="messages" 只能获得最终回复的文本流,无法看到完整的推理链条。
    """
    # async for chunk in agent.astream({"messages": [{"role": "user", "content": tips}]}, stream_mode="messages"):
    #     print(chunk)  # messages模式返回是的元组
    #     if chunk:
    #         msg, metadata = chunk
    #         node_name = metadata.get("langgraph_node", "")
    #         model = metadata.get("ls_model_name", "")
    #         if hasattr(msg, 'content') and msg.content:
    #             if current_node and current_node != node_name:
    #                 yield f"data: {json.dumps({'content': '', 'node': current_node, 'status': 'node_completed', 'finished': True, 'model': model, 'timestamp': '2026-07-27 13:48:00'}, ensure_ascii=False)}\n\n"
    #             current_node = node_name
    #             data = {
    #                 "content": msg.content,
    #                 "node": node_name,
    #                 "status": "streaming",
    #                 "finished": False,
    #                 "model": model,
    #                 "timestamp": "2026-07-27 13:48:00",
    #             }
    #             yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
    #             # await asyncio.sleep(0.05)
    # if current_node:
    #     yield f"data: {json.dumps({'content': '', 'node': current_node, 'status': 'node_completed', 'finished': False}, ensure_ascii=False)}\n\n"
    # yield f"data: {json.dumps({'content': '', 'node': '', 'status': 'completed', 'finished': True}, ensure_ascii=False)}\n\n"

    """
        方案二、使用 stream_events 获取推理过程。
        stream_events(..., version="v2") 是来获取结构化的流式事件, 输出Agent内部执行过程的所有事件执行顺序和工具调用(tool calls) 细节, 获取Agent推理的全过程。
    """
    # 4. 使用stream_events迭代流式事件
    inputs = {"messages": [("user", tips)]}
    async for event in agent.astream_events(inputs, version="v2"):
        kind = event["event"]

        # 处理不同类型的事件
        if kind == "on_chain_start":
            # 当开始执行一个链/节点时触发 此处可能是model,tool,langgraph的开始执行
            print(f"🔄 开始执行节点: {event.get('name')}")
            data = {
                "content": f"<br>🔄 开始执行节点: {event.get('name')}",
                "finished": False,
                "model": "llm",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            }
            yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
        elif kind == "on_chat_model_stream":
            # **最常用**: 当LLM生成新token时触发,用于实现打字机效果
            chunk = event["data"]["chunk"]
            if chunk.content:
                print(chunk.content, end="", flush=True)
                data = {
                    "content": chunk.content,
                    "finished": False,
                    "model": "llm",
                    "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
                }
                yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
        elif kind == "on_tool_start":
            # 当开始调用一个工具时触发
            print(f"\n🔧 开始调用工具: {event['name']}, 参数: {event['data'].get('input')}")
            data = {
                "content": f"<br>🔧 开始调用工具: {event['name']}, 参数: {event['data'].get('input')}",
                "finished": False,
                "model": "llm",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            }
            yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
        elif kind == "on_tool_end":
            # 当工具执行完毕时触发
            print(f"\n✅ 工具执行完成: {event['name']}, 结果: {event['data'].get('output')}")
            data = {
                "content": f"<br>✅ 工具执行完成: {event['name']}, 结果: {event['data'].get('output')}",
                "finished": True,
                "model": "llm",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            }
            yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
        elif kind == "on_chain_end":
            # 当整个链/节点执行完毕时触发
            print(f"\n🏁 节点执行完成: {event.get('name')}")
            data = {
                "content": f"<br>🏁 节点执行完成: {event.get('name')}",
                "finished": True,
                "model": "llm",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            }
            yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
        # 推送数据时暂停0.05秒
        # await asyncio.sleep(0.05)



@router.post("/analysis")
async def label_analysis(user_message: str):
    """
        标签分析智能体(Agent)
        :param user_message: 用户输入的意图
        :return:
    """
    return StreamingResponse(
        generate_label_analysis_stream_response_v2(user_message), 
        media_type="text/plain",
        headers={
            "Cache-Control": "no-cache",  # 禁用缓存
            "Connection": "keep-alive",  # 保持连接
            "Content-Type": "text/event-stream",  # SSE内容类型
        }
    )

注意:要使用此功能,Agent实例必须是一个 Runnable 对象。通过 create_agent 创建的Agent满足此要求。

💎 astream_events不同版本的差异

astream_events() 目前主要有三个版本:v1v2v3。简单来说,v2v1 的升级替代品,而 v3 则引入了一种全新的调用模式

📜 v1 vs v2:迭代模式的演进

v1 是早期版本,目前已被弃用 ,并计划在 LangChain 0.4.0 版本中移除。v2 是对 v1 的完全重写,旨在解决其存在的问题。

主要区别体现在以下几个方面:

  • 性能与效率v2 版本更高效,内部实现减少了不必要的抽象和开销。

  • 事件结构一致性v1 中,同一事件(如 on_chat_model_end)在不同上下文(根级或链内)的输出结构不一致v2 对此进行了统一,使事件结构更简洁、一致

  • 事件内容丰富度

    • 层级追踪v2 提供 parent_ids 字段,用于追踪事件的父子关系,便于理解调用链
    • 自定义事件v2 支持开发者派发自定义事件
  • 移除冗余事件v2 移除了 v1 中遗留的 on_retriever_streamon_tool_stream 事件,相关信息已整合到对应的 _end 事件中

  • 名称传播v2 中可运行对象的名称传播更一致,如果你的代码依赖特定名称过滤事件,需要注意更新

  • 修复错误事件v2 修复了 v1RunnableRetry 在重试时可能产生错误 on_chain_end 事件的问题

🆕 v3:全新的 Awaitable 模式

v3 是比 v2 更新的版本,它不是 v2 的简单升级,而是提供了一种完全不同的调用方式

v1/v2可迭代(Iterable) 模式不同,v3 是一个可等待(Awaitable) 对象。

  • v1 / v2 (迭代模式) :返回一个异步迭代器,使用 async for 来逐个处理 StreamEvent 字典。

    python

    csharp 复制代码
    async for event in agent.astream_events(inputs, version="v2"):
        # 处理 event 字典
  • v3 (等待模式) :返回一个可等待对象,需要用 await 来获取一个更高级的流对象,然后可以从中提取不同部分的数据。

    python

    csharp 复制代码
    # 先 await 获取流对象
    stream = await agent.astream_events(inputs, version="v3")
    # 然后可以分别迭代不同部分,例如文本和工具调用
    async for chunk in stream.text:
        # 处理文本块
    async for tool_call in stream.tool_calls():
        # 处理工具调用

    这种设计让开发者能更有结构地处理复杂的输出流。

💎 总结与建议

特性 v1 (已弃用) v2 (推荐) v3 (最新)
状态 已弃用,将在 0.4.0 移除 当前默认,稳定推荐 最新实验性功能
调用方式 异步迭代 (async for) 异步迭代 (async for) 异步等待 (await)
返回类型 AsyncIterator[StreamEvent] AsyncIterator[StreamEvent] Awaitable[T] (一个可等待对象)
性能 基准 更高 (重写优化) 信息待补充
事件结构 不一致 一致且简洁 信息待补充
parent_ids 不支持 (返回空列表) 支持 信息待补充
自定义事件 不支持 支持 信息待补充

对于绝大多数场景,最稳妥、最推荐的做法是坚持使用默认的 version="v2" 。它成熟稳定,功能强大,能满足绝大部分流式处理需求。

v3 则代表了未来的方向,提供了更结构化的数据消费模式。如果你需要更精细地控制复杂的输出流,并且愿意尝试最新特性,可以关注 v3 的后续发展。

至于选择哪种执行方式,取决于你的具体需求:

  • 追求简单:用 invoke()ainvoke()
  • 需要实时输出:用 stream()astream()
  • 需要批量处理:用 batch()abatch()
  • 需要细粒度监控和复杂交互:用 astream_events()

astream_events 提供了最强大的控制能力,是构建生产级、高交互性AI应用的关键工具。

相关推荐
一点一木2 小时前
豆包工作发布:飞书,才是它真正的底牌
人工智能·ai编程·产品
京东云开发者2 小时前
SKill测评:谁才是真正的Skill之王?
ai编程
烬羽2 小时前
AI Coding 全流程实战:从需求到上线,我用 AI 开发了一个 NPM 包
前端·react.js·ai编程
Lambert2812 小时前
MCP 史上最大升级:握手没了、会话没了,Java 生态的六个坑
ai编程·mcp
不一样的少年_3 小时前
图解 AI Agent ②:模型到底是怎么读取文件的?
人工智能·agent·ai编程
threerocks3 小时前
《The AI-Native SDLC Playbook》万字拆解
算法·aigc·ai编程
Lambert2813 小时前
一个 @McpTool 注解,让 Claude 和 Cursor 直接调用你的 Spring 服务
ai编程
全栈弄潮儿4 小时前
用 AI 做代码优化:哪些建议值得采纳
aigc·openai·ai编程
薛定猫AI5 小时前
【技术干货】Claude Code多模型代理与反馈闭环:Python实现可验证的AI编程工作流
开发语言·python·ai编程