1. 从轮询到事件驱动
传统 Agent 采用轮询模式:定期检查是否有新任务或状态变化。这种模式简单但低效——要么延迟高(轮询间隔长),要么浪费资源(轮询间隔短)。
事件驱动架构(EDA)将 Agent 从"主动检查"转变为"被动响应":当有意义的事件发生时,系统主动通知 Agent 处理。核心优势:
- 低延迟:事件发生即触发,无需等待轮询周期
- 低资源消耗:无事件时 Agent 处于休眠状态
- 天然解耦:事件生产者和消费者完全分离
- 弹性扩展:事件积压时可动态增加消费者
2. 事件驱动 Agent 整体架构
┌──────────┐ ┌──────────┐ ┌──────────┐
│ GitHub │ │ Slack │ │ Sensor │ 事件源
│ Webhook │ │ Events │ │ Data │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────┐
│ Event Ingestion Layer │
│ (API Gateway / Message Queue) │
└───────────────────┬────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ Event Router / Filter │
└───────────────────┬────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Agent A │ │ Agent B │ │ Agent C │ 事件消费者
│(Coder) │ │(Reviewer)│ │(Notifier)│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────┐
│ Action Executor │
│ (API calls, DB writes, etc.) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Event Publisher (新事件) │
└─────────────────────────────────────┘
3. 事件模型设计
3.1 事件 Schema
from pydantic import BaseModel, Field
from datetime import datetime
from enum import Enum
from typing import Any, Optional
import uuid
class EventSeverity(str, Enum):
INFO = "info"
WARNING = "warning"
ERROR = "error"
CRITICAL = "critical"
class AgentEvent(BaseModel):
"""统一事件格式 - CloudEvents 兼容"""
event_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
event_type: str # "github.pr.opened", "agent.task.completed"
source: str # 事件来源标识
subject: str = "" # 事件主题(如 PR #123)
data: dict[str, Any] # 事件负载
time: datetime = Field(default_factory=datetime.now)
severity: EventSeverity = EventSeverity.INFO
trace_id: str = "" # 分布式追踪 ID
correlation_id: str = "" # 关联 ID(同一会话)
# 事件版本控制
spec_version: str = "1.0"
data_content_type: str = "application/json"
class TaskCompletedEvent(AgentEvent):
event_type: str = "agent.task.completed"
data: dict[str, Any] = Field(description="包含 task_id, result, duration 等")
class ErrorEvent(AgentEvent):
event_type: str = "agent.error"
severity: EventSeverity = EventSeverity.ERROR
3.2 事件注册表
class EventRegistry:
"""事件类型注册表 - 管理 Schema 和路由规则"""
def __init__(self):
self._schemas: dict[str, type[BaseModel]] = {}
self._handlers: dict[str, list[callable]] = {}
def register(self, event_type: str, schema: type[BaseModel]):
self._schemas[event_type] = schema
def subscribe(self, event_type: str, handler: callable):
self._handlers.setdefault(event_type, []).append(handler)
def get_handlers(self, event_type: str) -> list[callable]:
# 支持通配符匹配: "github.pr.*" 匹配 "github.pr.opened"
handlers = self._handlers.get(event_type, [])
for pattern, h_list in self._handlers.items():
if "*" in pattern:
prefix = pattern.replace("*", "")
if event_type.startswith(prefix):
handlers.extend(h_list)
return handlers
def validate(self, event: AgentEvent) -> bool:
schema = self._schemas.get(event.event_type)
if schema:
schema(**event.model_dump())
return True
4. 事件总线实现
4.1 内存事件总线(单机)
import asyncio
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor
class InMemoryEventBus:
def __init__(self, max_workers: int = 10):
self._subscribers: dict[str, list] = defaultdict(list)
self._executor = ThreadPoolExecutor(max_workers=max_workers)
self._dead_letter_queue: list[tuple[AgentEvent, Exception]] = []
self._middleware: list[callable] = []
def use(self, middleware: callable):
"""注册中间件:日志、认证、限流等"""
self._middleware.append(middleware)
async def subscribe(self, event_type: str, handler: callable):
self._subscribers[event_type].append(handler)
async def publish(self, event: AgentEvent):
# 执行中间件链
for mw in self._middleware:
event = await mw(event)
if event is None:
return # 被中间件拦截
handlers = self._subscribers.get(event.event_type, [])
# 匹配通配符订阅
for pattern, h_list in self._subscribers.items():
if "*" in pattern:
prefix = pattern.replace("*", "")
if event.event_type.startswith(prefix):
handlers.extend(h_list)
# 并行执行所有处理器
tasks = [self._safe_execute(h, event) for h in handlers]
await asyncio.gather(*tasks, return_exceptions=True)
async def _safe_execute(self, handler: callable, event: AgentEvent):
try:
if asyncio.iscoroutinefunction(handler):
await handler(event)
else:
await asyncio.get_event_loop().run_in_executor(
self._executor, handler, event
)
except Exception as e:
self._dead_letter_queue.append((event, e))
# 发布错误事件
error_event = ErrorEvent(
source="event_bus",
data={"original_event": event.event_id, "error": str(e)},
trace_id=event.trace_id,
)
await self.publish(error_event)
4.2 Kafka 事件总线(分布式)
from aiokafka import AIOKafkaProducer, AIOKafkaConsumer
import json
class KafkaEventBus:
def __init__(self, bootstrap_servers: str = "localhost:9092"):
self.bootstrap_servers = bootstrap_servers
self._producer: AIOKafkaProducer = None
self._consumers: list[AIOKafkaConsumer] = []
self._handlers: dict[str, callable] = {}
async def start(self):
self._producer = AIOKafkaProducer(
bootstrap_servers=self.bootstrap_servers,
value_serializer=lambda v: json.dumps(v.model_dump(mode="json")).encode(),
key_serializer=lambda k: k.encode() if k else None,
acks="all", # 等待所有副本确认
enable_idempotence=True, # 幂等生产者
compression_type="lz4", # 压缩
max_in_flight_requests_per_connection=5,
)
await self._producer.start()
async def publish(self, event: AgentEvent, topic: str = None):
topic = topic or event.event_type.split(".")[0] # 按事件类型分 Topic
partition_key = event.correlation_id or event.source
await self._producer.send_and_wait(
topic, event, key=partition_key
)
async def subscribe(self, event_type: str, handler: callable, group_id: str = "agent-group"):
topic = event_type.split(".")[0]
consumer = AIOKafkaConsumer(
topic,
bootstrap_servers=self.bootstrap_servers,
group_id=group_id,
value_deserializer=lambda v: AgentEvent(**json.loads(v.decode())),
auto_offset_reset="latest",
enable_auto_commit=False,
)
await consumer.start()
self._consumers.append(consumer)
self._handlers[event_type] = handler
async for msg in consumer:
event = msg.value
if event.event_type == event_type or event_type.endswith("*"):
try:
await handler(event)
await consumer.commit()
except Exception as e:
# 重试或进入死信队列
await self._handle_error(event, e)
async def stop(self):
await self._producer.stop()
for c in self._consumers:
await c.stop()
5. Agent 状态机
事件驱动 Agent 的核心是一个状态机:不同事件触发不同状态转换。
5.1 状态机实现
from enum import Enum, auto
from dataclasses import dataclass, field
from typing import Callable
class AgentState(str, Enum):
IDLE = "idle"
PLANNING = "planning"
EXECUTING = "executing"
WAITING_INPUT = "waiting_input"
REVIEWING = "reviewing"
ERROR = "error"
DONE = "done"
@dataclass
class StateTransition:
from_state: AgentState
to_state: AgentState
trigger_event: str # 触发事件类型
guard: Callable = None # 守卫条件
action: Callable = None # 转换时执行的动作
class AgentStateMachine:
def __init__(self, agent_id: str, initial_state: AgentState = AgentState.IDLE):
self.agent_id = agent_id
self.state = initial_state
self.transitions: list[StateTransition] = []
self.history: list[tuple[AgentState, AgentState, str, datetime]] = []
self._state_handlers: dict[AgentState, Callable] = {}
def add_transition(self, transition: StateTransition):
self.transitions.append(transition)
def on_state(self, state: AgentState, handler: Callable):
"""注册状态进入时的回调"""
self._state_handlers[state] = handler
async def handle_event(self, event: AgentEvent) -> bool:
"""处理事件,尝试状态转换"""
for t in self.transitions:
if t.from_state == self.state and t.trigger_event == event.event_type:
if t.guard and not t.guard(event):
continue
old_state = self.state
self.state = t.to_state
self.history.append((old_state, self.state, event.event_type, datetime.now()))
# 执行转换动作
if t.action:
await t.action(event)
# 执行新状态的处理函数
handler = self._state_handlers.get(self.state)
if handler:
await handler(event)
return True
return False
class CodeAgentStateMachine(AgentStateMachine):
def __init__(self, agent_id: str):
super().__init__(agent_id)
self.setup_transitions()
def setup_transitions(self):
self.add_transition(StateTransition(
AgentState.IDLE, AgentState.PLANNING, "task.assigned"
))
self.add_transition(StateTransition(
AgentState.PLANNING, AgentState.EXECUTING, "plan.ready",
action=self._start_execution
))
self.add_transition(StateTransition(
AgentState.EXECUTING, AgentState.REVIEWING, "code.committed",
action=self._trigger_review
))
self.add_transition(StateTransition(
AgentState.REVIEWING, AgentState.DONE, "review.passed"
))
self.add_transition(StateTransition(
AgentState.REVIEWING, AgentState.EXECUTING, "review.changes_requested",
action=self._apply_feedback
))
self.add_transition(StateTransition(
AgentState.EXECUTING, AgentState.ERROR, "execution.failed",
action=self._handle_error
))
self.add_transition(StateTransition(
AgentState.ERROR, AgentState.EXECUTING, "retry.triggered"
))
async def _start_execution(self, event: AgentEvent):
print(f"Agent {self.agent_id} 开始执行任务: {event.data.get('task_id')}")
async def _trigger_review(self, event: AgentEvent):
print(f"Agent {self.agent_id} 提交代码审查: {event.data.get('commit_sha')}")
async def _apply_feedback(self, event: AgentEvent):
print(f"Agent {self.agent_id} 根据反馈修改: {event.data.get('comments')}")
async def _handle_error(self, event: AgentEvent):
print(f"Agent {self.agent_id} 执行失败: {event.data.get('error')}")
6. Webhook 集成层
from fastapi import FastAPI, Request, HTTPException
import hmac
import hashlib
class WebhookIntegration:
def __init__(self, app: FastAPI, event_bus: InMemoryEventBus):
self.app = app
self.bus = event_bus
self._secrets: dict[str, str] = {} # source -> secret
def register_webhook(self, source: str, path: str, secret: str = None):
self._secrets[source] = secret
@self.app.post(path)
async def handle_webhook(request: Request):
body = await request.body()
# 验证签名
if secret:
signature = request.headers.get("X-Hub-Signature-256", "")
if not self._verify_signature(body, secret, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
payload = await request.json()
event_type = self._map_to_event_type(source, payload, request.headers)
event = AgentEvent(
event_type=event_type,
source=source,
data=payload,
trace_id=request.headers.get("X-Trace-Id", str(uuid.uuid4())),
)
await self.bus.publish(event)
return {"status": "accepted"}
def _verify_signature(self, body: bytes, secret: str, signature: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
def _map_to_event_type(self, source: str, payload: dict, headers) -> str:
if source == "github":
event_name = headers.get("X-GitHub-Event", "push")
action = payload.get("action", "")
return f"github.{event_name}.{action}" if action else f"github.{event_name}"
elif source == "slack":
return f"slack.{payload.get('event', {}).get('type', 'unknown')}"
elif source == "custom":
return payload.get("event_type", "custom.event")
return f"{source}.event"
7. 事件溯源与回放
class EventStore:
"""事件存储 - 支持回放和审计"""
def __init__(self, storage_backend="sqlite"):
self.backend = storage_backend
self._events: list[AgentEvent] = [] # 生产环境用数据库
async def append(self, event: AgentEvent):
self._events.append(event)
async def query(self, source: str = None, event_type: str = None,
since: datetime = None, limit: int = 100) -> list[AgentEvent]:
results = self._events
if source:
results = [e for e in results if e.source == source]
if event_type:
results = [e for e in results if e.event_type == event_type]
if since:
results = [e for e in results if e.time > since]
return results[-limit:]
async def replay(self, agent: AgentStateMachine, since: datetime = None):
"""从指定时间点重放事件,重建 Agent 状态"""
events = await self.query(since=since)
for event in events:
await agent.handle_event(event)
async def snapshot(self, agent: AgentStateMachine) -> dict:
"""定期快照,减少回放量"""
return {
"agent_id": agent.agent_id,
"state": agent.state,
"history_length": len(agent.history),
"timestamp": datetime.now().isoformat()
}
8. 背压与流控
class BackpressureHandler:
"""背压处理:当消费者跟不上生产速度时"""
def __init__(self, max_queue_size: int = 1000, strategy: str = "drop_oldest"):
self.max_queue_size = max_queue_size
self.strategy = strategy
self.queue: asyncio.Queue = asyncio.Queue(maxsize=max_queue_size)
self.dropped_count = 0
self.total_processed = 0
async def submit(self, event: AgentEvent):
if self.queue.full():
if self.strategy == "drop_oldest":
self.queue.get_nowait()
self.dropped_count += 1
elif self.strategy == "drop_newest":
self.dropped_count += 1
return False
elif self.strategy == "block":
await self.queue.put(event)
return True
await self.queue.put(event)
return True
async def consume(self, handler: callable):
while True:
event = await self.queue.get()
try:
await handler(event)
self.total_processed += 1
except Exception:
pass # 错误已在总线层处理
self.queue.task_done()
def health(self) -> dict:
return {
"queue_size": self.queue.qsize(),
"max_size": self.max_queue_size,
"utilization": self.queue.qsize() / self.max_queue_size,
"dropped": self.dropped_count,
"processed": self.total_processed,
}
9. 模式对比
| 维度 | 轮询模式 | 事件驱动 |
|---|---|---|
| 延迟 | 取决于轮询间隔 | 毫秒级 |
| 资源消耗 | 持续高 | 空闲时近零 |
| 扩展性 | 线性 | 弹性 |
| 复杂度 | 低 | 中高 |
| 调试难度 | 简单 | 需分布式追踪 |
| 可靠性 | 单点重试 | 事件持久化+回放 |
10. 总结
事件驱动 Agent 架构是构建实时响应系统的核心模式。设计要点:
- 统一事件模型:CloudEvents 兼容格式,便于跨系统互通
- 事件总线选型:单机用内存队列,分布式用 Kafka/NATS
- 状态机驱动:每个 Agent 是一个有限状态机,事件触发转换
- Webhook 集成:统一入口 + 签名验证 + 事件类型映射
- 背压控制:防止事件洪峰压垮系统
- 事件溯源:持久化所有事件,支持回放和审计
推荐技术栈:FastAPI(Webhook 接入)+ Kafka/NATS(事件总线)+ Redis(状态缓存)+ OpenTelemetry(分布式追踪)+ SQLite/PostgreSQL(事件存储)。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
