跳转到内容

事件驱动的 Agent 运行时

事件驱动的 Agent 运行时

学习目标

读完本章后,你将能够:

  • 理解事件驱动架构与函数调用式 Agent 的根本区别
  • 设计基于发布/订阅的 Agent 间通信机制
  • 实现灵活的订阅系统实现 Agent 到 Topic 的路由映射
  • 区分 RPC(请求-响应)和 pub/sub(广播)两种消息模式
  • 评估单线程与分布式运行时的适用场景

核心概念

运行时(Runtime)的职责

事件驱动的 Agent 运行时是整个系统的中枢,承担以下职责:

  1. 消息路由:将消息从发送者传递到接收者
  2. 生命周期管理:Agent 注册、启动、关闭
  3. 消息序列化:不同 Agent 间的消息格式转换
  4. 订阅管理:维护 Topic 到 Agent 的映射关系

两种消息模式

模式方法语义返回值适用场景
RPCsend_message点对点请求-响应有(Response)需要获取确定结果
Pub/Subpublish_message一对多广播无(Fire-and-forget)事件通知、状态广播

RPC 模式适合需要明确返回值的场景,例如询问某个 Agent 获取处理结果。调用方会等待响应或异常。

Pub/Sub 模式适合一对多通知场景,例如一个 Agent 完成工作后通知所有订阅者。发送方不等待响应。

订阅系统

订阅系统是事件驱动架构的核心创新点。它定义了 Agent 对哪些 Topic 感兴趣,以及消息如何路由到正确的 Agent。

三种订阅策略

订阅类型匹配规则路由策略使用场景
TypeSubscription精确匹配消息类型单 Agent专用处理器
DefaultSubscription匹配默认 Topic单 Agent简单一对一通信
TypePrefixSubscription匹配类型前缀多 Agent一组相关消息的路由
TypePrefixSubscription(带 topic)匹配 Topic 前缀多 Agent命名空间分组

消息处理上下文

每个消息处理调用都会附带一个 MessageContext,提供:

  • sender:消息发送者的 Agent ID
  • topic_id:消息发布的 Topic
  • is_rpc:是否为 RPC 调用
  • cancellation_token:取消令牌,支持异步取消

Agent 类型

类型特点使用场景
RoutedAgent基于装饰器的消息路由通用 Agent,需灵活处理多种消息
ClosureAgent轻量级闭包 Agent简单的一次性处理逻辑
BaseAgent最底层抽象自定义 Agent 基类

RoutedAgent 使用装饰器标记消息处理方法:

  • @message_handler:通用处理器,处理事件和 RPC
  • @event:仅处理事件(pub/sub),无返回值
  • @rpc:仅处理 RPC,必须返回值

问题与规避

单线程运行时的并发限制

单线程运行时(SingleThreadedAgentRuntime)使用 asyncio 事件循环,所有 Agent 处理在同一条线程中执行。

陷阱:如果某个 Agent 的处理方法中包含阻塞操作(如同步 I/O),会阻塞整个运行时。

规避策略

  • 所有耗时操作必须使用 async 方法
  • 使用 asyncio.to_thread() 包装同步阻塞调用
  • 对于高吞吐场景,考虑切换到分布式运行时

消息队列背压

当消息生产速率超过消费速率时,队列会持续增长。

规避策略

  • 监控队列深度,设置告警阈值
  • 使用有界队列(maxsize 参数)配合背压策略
  • 对低优先级消息使用 publish_message(无响应等待),减少队列压力

订阅冲突与消息重复消费

多个订阅可能匹配同一 Topic,导致消息被多次处理。

规避策略

  • 使用 TypeSubscription 而非 TypePrefixSubscription 当需要精确路由时
  • 在消息中添加幂等 ID,防止重复处理的副作用

设计取舍

为什么选择事件驱动而非直接调用?

优势

  • 解耦:Agent 间不需要互相引用,降低耦合度
  • 可扩展:新增 Agent 只需注册订阅,无需修改现有代码
  • 灵活性:同一消息可被多个 Agent 处理,支持多播

代价

  • 调试复杂度:消息流转路径不直观,需要专门的追踪工具
  • 性能开销:消息序列化、路由匹配引入额外延迟

替代方案对比

方案解耦度调试难度多播支持性能
直接函数调用不支持最高
事件驱动 pub/sub支持中等
gRPC 同步调用不支持
消息队列(RabbitMQ)支持中等(网络开销)

参考来源


补充:LlamaIndex 的 Workflow 事件驱动状态机

来源:LlamaIndex llama-index-core v0.14.22 + llama-index-workflows PyPI 包

LlamaIndex 的 Agent 循环基于一个事件驱动状态机实现,与传统的 while True 循环有本质区别。

@step 装饰器构建事件流图

每个 Agent 的方法用 @step 装饰器标记,引擎通过静态分析函数签名自动推断该步骤接受什么事件、产生什么事件:

@step
async def run_agent_step(self, ctx: Context, ev: AgentSetup) -> AgentOutput:
return await self.take_step(ctx, ev)

@step 的本质是定义事件流图的边:输入事件类型 → 处理 → 输出事件类型。引擎将所有 @step 方法组装成一个有向图,事件沿着图的边流动。

并行工具调用的等待-聚合模式

LlamaIndex 通过 ctx.collect_events() 实现并行工具调用的结果聚合:

当 Agent 需要调用多个工具时,所有工具调用并行发出collect_events 等待全部结果到达后才进入聚合步骤。这避免了传统循环中工具串行调用的延迟累积。

Context.store 的状态管理

Context 对象提供一个 KV 存储 ctx.store,用于管理 Agent 的运行时状态:

  • memory:对话历史(列表)
  • state:自定义状态字典
  • max_iterations:最大迭代次数
  • iteration_count:当前迭代计数器

设计优势:与简单 while 循环相比,事件驱动架构天然支持并发工具调用、状态持久化(Context 可序列化)、和可观测性(每步状态推送给订阅者)。