引言:为什么Agent系统需要事件驱动?

传统的同步HTTP调用在Agent系统中面临严峻挑战:当一个复杂任务涉及数十次工具调用、记忆检索和多轮对话时,同步调用会导致漫长的等待时间和服务间的强耦合。更关键的是,Agent系统天然需要处理异步、长时运行的任务——一次网页搜索可能耗时数秒,一次代码执行可能需要更长时间。

事件驱动架构通过解耦服务间的时间依赖,让Agent系统能够优雅地处理异步性和并发性。

事件驱动架构的核心模式

发布-订阅模式

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ Tool        │───▶│ Message     │───▶│ Memory      │
│ Service     │    │ Bus         │    │ Service     │
└─────────────┘    │             │    └─────────────┘
                   │ ┌─────────┐ │    ┌─────────────┐
┌─────────────┐    │ │ Topic   │ │───▶│ LLM         │
│ Router      │───▶│ │ Router  │ │    │ Inference   │
│ Service     │    │ └─────────┘ │    └─────────────┘
└─────────────┘    └─────────────┘
                   ┌────┴────┐
                   │Event    │
                   │Producer │
                   └─────────┘

事件溯源模式

在Agent系统中,事件溯源(Event Sourcing)能够完整记录Agent的思考和决策过程:

from dataclasses import dataclass, asdict
from datetime import datetime
import json

@dataclass
class AgentEvent:
    """Agent事件基类"""
    event_id: str
    session_id: str
    timestamp: datetime
    event_type: str
    
@dataclass
class ToolExecutionEvent(AgentEvent):
    """工具执行事件"""
    tool_name: str
    input_params: dict
    output_result: str
    execution_time_ms: float
    
@dataclass
class LLMEvaluationEvent(AgentEvent):
    """LLM评估事件"""
    prompt_tokens: int
    completion_tokens: int
    model: str
    latency_ms: float
    quality_score: float
    
class AgentEventStore:
    """事件存储,支持回放和问题诊断"""
    
    def __init__(self, message_bus):
        self.bus = message_bus
        self.event_log = []
    
    async def emit(self, event: AgentEvent):
        """发布事件"""
        await self.bus.publish(
            topic=f"agent.events.{event.session_id}",
            message=json.dumps(asdict(event)),
            headers={
                "event_type": event.event_type,
                "timestamp": event.timestamp.isoformat()
            }
        )
        self.event_log.append(event)
    
    async def replay(self, session_id: str) -> list:
        """回放指定会话的所有事件"""
        events = await self.bus.subscribe(
            topic=f"agent.events.{session_id}",
            offset="earliest"
        )
        return [AgentEvent(**json.loads(e.message)) for e in events]

消息总线技术选型

Kafka vs NATS vs RabbitMQ

特性Apache KafkaNATS JetStreamRabbitMQ
吞吐量百万级/秒千万级/秒十万级/秒
延迟2-5ms<1ms1-3ms
消息持久化需配置
消息回溯支持支持不支持
流处理原生支持有限支持需插件
运维复杂度

对于Agent系统,建议按场景选择:

class MessageBusStrategy:
    """根据场景选择合适的消息总线"""
    
    SCENARIOS = {
        "high_throughput_streaming": {
            "backend": "nats",
            "reason": "超低延迟,支持千万级吞吐"
        },
        "event_sourcing_audit": {
            "backend": "kafka", 
            "reason": "消息持久化+回溯能力强"
        },
        "task_queue_workflow": {
            "backend": "kafka + temporal",
            "reason": "支持工作流状态管理和重试"
        },
        "real_time_notification": {
            "backend": "nats",
            "reason": "低延迟,完美支持WebSocket推送"
        }
    }
    
    @classmethod
    def get_bus(cls, scenario: str):
        backend = cls.SCENARIOS.get(scenario, {}).get("backend", "nats")
        if backend == "nats":
            return NATSMessageBus()
        elif backend == "kafka":
            return KafkaMessageBus()

Agent事件流设计

标准事件定义

// agent_events.proto
syntax = "proto3";

message AgentEvent {
  string event_id = 1;
  string session_id = 2;
  int64 timestamp_ns = 3;
  EventType type = 4;
  google.protobuf.Any payload = 5;
}

enum EventType {
  SESSION_STARTED = 0;
  USER_MESSAGE_RECEIVED = 1;
  INTENT_DETECTED = 2;
  TOOL_CALL_INITIATED = 3;
  TOOL_CALL_COMPLETED = 4;
  TOOL_CALL_FAILED = 5;
  LLM_GENERATION_STARTED = 6;
  LLM_GENERATION_COMPLETED = 7;
  MEMORY_RETRIEVED = 8;
  MEMORY_STORED = 9;
  RESPONSE_DELIVERED = 10;
  SESSION_ENDED = 11;
}

message ToolCallEvent {
  string tool_name = 1;
  map<string, string> parameters = 2;
  int32 execution_time_ms = 3;
  bool success = 4;
  string error_message = 5;
}

事件流拓扑

用户输入 ──▶ Router ──┬──▶ IntentDetection ──▶ ToolExecution
                      │                            │
                      │                     ┌──────▼──────┐
                      │                     │ ToolResult  │
                      │                     │ Aggregation │
                      │                     └──────┬──────┘
                      │                            │
                      ├──▶ MemoryUpdate            │
                      │                            │
                      └──▶ MetricsCollect ────▶ LLMSynthesis
                                              ┌─────▼──────┐
                                              │ Response   │
                                              │ Delivery   │
                                              └────────────┘

死信队列与故障处理

异步系统中最棘手的问题是消息处理失败后的处理:

import asyncio
from typing import Callable, Any
import structlog

logger = structlog.get_logger()

class DeadLetterHandler:
    """死信队列处理器"""
    
    def __init__(self, dlq_topic: str, retry_config: dict):
        self.dlq_topic = dlq_topic
        self.retry_config = retry_config
        self.processing = {}
    
    async def handle_failed_message(
        self,
        message: Any,
        error: Exception,
        context: dict
    ) -> bool:
        """处理失败消息"""
        retry_count = context.get("retry_count", 0)
        max_retries = self.retry_config["max_retries"]
        backoff = self.retry_config["backoff_base"]
        
        if retry_count < max_retries:
            # 计算退避延迟
            delay = backoff * (2 ** retry_count)
            
            # 重新入队(带延迟)
            await self.schedule_retry(
                message=message,
                delay_seconds=delay,
                retry_count=retry_count + 1
            )
            logger.info(
                "message_scheduled_for_retry",
                message_id=context["message_id"],
                retry_count=retry_count + 1,
                delay_seconds=delay
            )
            return True
        
        # 超过最大重试次数,发送到DLQ
        await self.send_to_dlq(message, error, context)
        logger.error(
            "message_sent_to_dlq",
            message_id=context["message_id"],
            error=str(error),
            total_retries=retry_count
        )
        return False
    
    async def send_to_dlq(
        self,
        message: Any,
        error: Exception,
        context: dict
    ):
        """发送到死信队列"""
        dlq_message = {
            "original_message": message,
            "error": str(error),
            "error_type": type(error).__name__,
            "context": context,
            "failed_at": datetime.now().isoformat(),
            "stack_trace": traceback.format_exc()
        }
        await self.message_bus.publish(
            topic=self.dlq_topic,
            message=json.dumps(dlq_message)
        )

顺序保证与幂等性

分区策略

class ConsistentHashingPartitioner:
    """一致性哈希分区,确保同一会话的消息有序"""
    
    def __init__(self, num_partitions: int):
        self.num_partitions = num_partitions
        self.ring = {}
    
    def partition(self, message: dict) -> int:
        """基于session_id进行哈希分区"""
        session_id = message.get("session_id", "")
        # 使用MurmurHash3确保均匀分布
        hash_value = self._murmurhash3(session_id)
        return hash_value % self.num_partitions
    
    @staticmethod
    def _murmurhash3(key: str) -> int:
        """MurmurHash3 实现"""
        data = key.encode('utf-8')
        # 简化实现
        h1 = 0
        for i, byte in enumerate(data):
            k1 = byte
            k1 ^= k1 >> 16
            k1 = (k1 * 0x85ebca6b) & 0xFFFFFFFF
            k1 ^= k1 >> 13
            k1 = (k1 * 0xc2b2ae35) & 0xFFFFFFFF
            h1 ^= k1
            h1 ^= h1 >> 16
        return h1

幂等处理器

class IdempotentHandler:
    """幂等处理器,防止重复处理"""
    
    def __init__(self, redis_client):
        self.redis = redis_client
        self.idempotency_window = 3600  # 1小时窗口
    
    async def is_duplicate(self, event_id: str) -> bool:
        """检查是否已处理"""
        key = f"idempotent:{event_id}"
        return await self.redis.exists(key)
    
    async def mark_processed(self, event_id: str):
        """标记为已处理"""
        key = f"idempotent:{event_id}"
        await self.redis.setex(key, self.idempotency_window, "1")
    
    async def process_idempotently(
        self,
        event: AgentEvent,
        handler: Callable
    ):
        """幂等处理装饰器"""
        if await self.is_duplicate(event.event_id):
            logger.warning("duplicate_event_skipped", event_id=event.event_id)
            return
        
        result = await handler(event)
        await self.mark_processed(event.event_id)
        return result

生产监控指标

事件驱动架构需要关注的核心指标:

指标说明告警阈值
consumer_lag消费延迟>1000条
dlq_depth死信队列深度>100条
processing_time_p99处理耗时P99>500ms
retry_rate重试率>5%
event_loss_rate消息丢失率>0.01%

总结

事件驱动架构是构建高并发、高可用Agent系统的基石。选择合适的消息总线、设计清晰的事件流拓扑、建立完善的死信处理机制和幂等保证,是实现生产级事件驱动系统的关键要素。在实际落地时,建议从小规模试点开始,逐步扩展到完整的事件驱动架构。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。