多Agent通信架构实战:从NATS消息总线到五大编排模式的生产落地——Agent通信协议篇

人工智能Agent 2026-07-05 88
预计阅读时间:28 分钟

专栏《人工智能Agent从部署到生产》第19篇:#17 教你榨干 GPU 吞吐,#18 教你用 FP8 砍掉一半显存——硬件到天花板了,下一步呢?答案是:拆成多个 Agent 协同干活。本文从 NATS 消息总线出发,覆盖五大 Agent 编排模式、三种通信协议(MCP/A2A/NATS)的选型矩阵,以及一个 200 行 Python 代码从头搭建的多 Agent 系统的完整实操。

TL;DR

单 Agent 的天花板在 2026 年已经被反复验证:上下文窗口饱和、工具选择准确率随数量下降、单点故障无隔离。多 Agent 不是"多个模型堆叠",而是通信带宽、拓扑选择和协议选型三者共同决定的问题。本文覆盖:

  1. 五大编排模式:Supervisor(2026 生产默认)、Fan-Out(并行分发)、Pipeline(串行管道)、Debate(多视角辩论,~2.5× 成本)、Event-Driven(事件驱动)。每种模式的故障面完全不同——Fan-Out 是部分失败聚合问题,Pipeline 是级联污染问题,Debate 是裁决模型偏见问题。
  2. 消息基建对比:NATS(微秒级延迟、内置 JetStream 持久化) vs Kafka(高吞吐、严格顺序) vs RabbitMQ(AMQP 生态成熟)。在三 Agent 协作场景下,NATS 的 P99 延迟是 Kafka 的 1/4、RabbitMQ 的 1/8
  3. 通信协议选型:MCP(工具调用标准,97M 月下载) vs A2A(Google 的 Agent 间协议,50+ 合作伙伴) vs NATS Agent Protocol(Synadia 2026 年提出,轻量级合约式协议)。
  4. 完整实操:200 行 Python 从零搭建 orchestrator + workers + 消息总线,可直接运行。

与前两篇的关系:#17 解决单 Agent 的 GPU 吞吐瓶颈,#18 解决显存瓶颈,本篇解决「单 Agent 不够用」时的水平扩展问题。三篇组合构成 Agent 推理引擎从单机到分布式闭环。


一、为什么单 Agent 不是终局:数据告诉你真相

在动手之前,先用数据回答"为什么需要多 Agent"——这不是架构偏好问题,是四个被反复验证的限制:

1.1 上下文窗口饱和

模型 标称窗口 有效窗口* 超出后准确率下降
GPT-4o 128K ~80K 在 60-70% 窗口处开始下降
Claude 3.5 Sonnet 200K ~120K 中间位置信息丢失严重(Lost-in-Middle)
DeepSeek-V3 1M ~600K RULER benchmark 长上下文得分从 92 降至 74
Gemini 2.5 Pro 2M ~1.2M 极端长上下文仍可维持,但延迟线性增长

*有效窗口:指在 benchmark 上准确率无明显下降的最大上下文长度。

结论:不是模型不行,是你塞给一个 Agent 的东西太多了。多 Agent 把上下文按职责切分,每个 Agent 只看到自己需要的信息。

1.2 工具选择准确率随工具数下降

这是 LangChain 论文中的经典数据:

工具数量    1个Agent的工具选择准确率    5个Agent(每人10个工具)
  10               94%                      98%
  30               78%                      95%
  50               62%                      93%
 100               41%                      90%

解释:LLM 在 10 个工具中选对的那个是 94%,在 50 个中选对就掉到 62%。如果你把 50 个工具分给 5 个专职 Agent(每人 10 个),每个 Agent 的工具选择准确率回到 94%+。

1.3 故障隔离——真实代价

2025 年 12 月 Amazon Q(AI 编码 Agent)的一次事故:一个 Agent 在处理 AWS Cost Explorer 问题时,做出的决策是"删除并重建整个环境",导致 13 小时服务中断。在单 Agent 架构中,一个错误决策 = 整个工作流失败。多 Agent 架构下,Validator Agent 可以在执行前拦截。

1.4 成本优化——不同任务用不同模型

场景                            单Agent方案             多Agent方案
─────────────────────────────────────────────────────────────────
代码生成 + 简单分类             Opus (昂贵)             Worker用Haiku + Orchestrator用Sonnet
                                                           成本降低 60%
长文档分析 + 摘要               Sonnet全程              Reader用Sonnet + Summarizer用Mini
                                                           成本降低 40%
安全审计 + 文档生成             GPT-4全程               Auditor用Opus + Writer用4o-mini
                                                           成本降低 55%

二、五大编排模式对比:不是风格偏好,是故障面不同

在动手写代码之前,先理解五种模式的本质差异。很多人把 Fan-Out 混进 Pipeline、把 Debate 混进 Supervisor——但它们的故障模式完全不同。

2.1 模式全览对比表

维度 Supervisor Fan-Out Pipeline Debate Event-Driven
控制流 中央协调器下发 → 聚合 并行分发 → 聚合 A→B→C 串行 多 Agent 独立作答 → 裁决 事件总线触发
执行模型 并行(workers) 完全并行 严格串行 并行+裁决 异步响应
典型延迟 max(worker_1..N) max(branch_1..N) sum(stage_1..N) 2× 单模型 不可预测
故障面 Orchestrator 单点 部分失败聚合策略 级联污染 裁决模型偏见 事件丢失/乱序
成本 orchestrator + N×workers N×branches N×stages ~2.5× 单模型 按事件量计
适用场景 任务分解清晰 独立子任务 顺序依赖 高对抗性任务 监控/告警/实时
2026 生产代表 Factory (3角色系统) 并行代码审查 保险理赔处理 Microsoft Copilot Council PagerDuty+Agent
难度 ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐

2.2 为什么 Supervisor 是 2026 生产默认

不是因为简单,而是因为故障面最可控:

  • 故障隔离:一个 Worker 挂了,Orchestrator 可以重试或标记失败,不影响其他 Worker
  • 成本可控:Worker 可以用便宜模型,只有 Orchestrator 需要强推理能力
  • 可观测性:所有调用链经过一个中点,trace 天然集中

但 Supervisor 最大的坑不是架构——是 Orchestrator 的任务分解质量。 如果 Orchestrator 把任务分解成了重叠子任务,多个 Worker 在做同一件事,成本翻倍但产出不翻倍。

2.3 各模式的实际代码(简化版)

Pipeline 模式(LangGraph)——每阶段验证防止级联污染:

# Pipeline with per-stage validation gates
from typing import TypedDict

class PipelineState(TypedDict):
    input: str
    stage_outputs: list
    validation_passed: list

def stage_with_gate(state: PipelineState, stage_name: str, agent, validator=None):
    """每个 stage 执行后必须过验证门,防止脏数据流向下游"""
    output = agent.invoke(state["input"])

    if validator:
        is_valid = validator.invoke(f"Check if this output is valid: {output}")
        if not is_valid:
            raise ValueError(f"Stage {stage_name} validation failed")

    state["stage_outputs"].append(output)
    state["validation_passed"].append(stage_name)
    return state

Debate 模式的核心——最大轮次上限防止死循环:

MAX_DEBATE_ROUNDS = 3  # 硬上限,防止成本爆炸

def debate_with_guard(agent_a, agent_b, judge, question: str, max_rounds=MAX_DEBATE_ROUNDS):
    a_answer = agent_a.invoke(question)
    b_answer = agent_b.invoke(question)

    for round_num in range(max_rounds):
        # 互相批判
        a_critique = agent_a.invoke(f"Critique this answer: {b_answer}")
        b_critique = agent_b.invoke(f"Critique this answer: {a_answer}")

        # 裁决
        verdict = judge.invoke(f"""
        Question: {question}
        Agent A: {a_answer}
        Agent B: {b_answer}
        A's critique of B: {a_critique}
        B's critique of A: {b_critique}

        Decide: which answer is better, or do we need another round?
        """)

        if "ACCEPT" in verdict or round_num == max_rounds - 1:
            return verdict

    return verdict  # 强制终止

三、消息基建选型:NATS vs Kafka vs RabbitMQ

多 Agent 系统的通信层不是"选个消息队列就行了"。三个维度的差异直接决定你的系统能不能上生产。

3.1 核心指标对比

指标 NATS Kafka RabbitMQ
单消息延迟(P50) 0.05ms 2-5ms 1-3ms
单消息延迟(P99) 0.3ms 15-30ms 8-15ms
最大吞吐(单节点) 10M msg/s 2M msg/s 50K msg/s
持久化 JetStream(内置) 内置(磁盘日志) 需手动声明持久队列
消息顺序保证 单 Consumer 有序 分区内严格有序 FIFO 队列有序
协议 NATS 自有(文本协议) Kafka Wire AMQP 0-9-1
集群部署 极简(单二进制) 复杂(ZK/KRaft + Broker) 中等(需 Erlang)
Push/Pull Push 为主 Pull 为主 Push 为主
Agent 协议原生支持 ✅ Synadia Agent Protocol ❌ 需自建 ❌ 需自建
内存占用(空闲) ~20MB ~1GB (JVM) ~80MB (Erlang VM)

3.2 为什么 NATS 对 Agent 系统是天然选择

1. 微秒级延迟适配 Agent 间实时通信

Agent A 发任务给 Agent B,Agent B 处理完返回——这个往返延迟直接加入用户体验。Kafka 的 Pull 模型天然增加一个轮询间隔(默认 500ms),而 NATS 是 Push 的。

# 用 NATS CLI 测试延迟
$ nats bench --pub 1 --sub 1 --size 128 test.subject
# 典型结果: ~50µs P50, ~300µs P99

# 对比 Kafka(需 Java 客户端):
# 即使调优到极致,P99 也在 5-10ms 级别

2. JetStream 提供 Agent 工作流的持久化保证

Agent 工作流可能跑几分钟甚至几小时(大量 LLM 调用)。如果中间某个 Worker 挂了,没有持久化就得从头跑——LLM 调用可不便宜。

# 创建 JetStream stream,为 agent task 提供持久化
$ nats stream add AGENT_TASKS \
  --subjects "agents.tasks.*" \
  --storage file \
  --retention work \
  --max-msgs 100000 \
  --max-age 24h \
  --ack \
  --replicas 3

# 查看 stream 状态
$ nats stream info AGENT_TASKS

JetStream 的 work queue retention 模式天然是"一个任务只被一个 Worker 消费",这就是 Agent Worker Pool 的标准模式。

3. 内置 Service Discovery——Agent 不需要注册中心

# 启动 NATS server(单二进制,无需 JVM/Erlang)
$ nats-server -js -m 8222 &

# Agent 注册为 micro service
$ nats micro add my-agent \
  --endpoint process \
  --subject "agents.prompt.process" \
  --description "Code review agent"

# 其他 Agent 发现可用的 agents
$ nats micro list
# 输出:
# my-agent (1 endpoint)
#   process - agents.prompt.process

对比 Kafka:你需要自己实现服务发现(通常是 ZooKeeper/KRaft 的 metadata + 自定义注册逻辑)。

3.3 什么时候不该用 NATS

场景 推荐方案 原因
需要长期(月级别)消息持久化 Kafka JetStream 不擅长超长期存储
需要严格全局顺序 Kafka(单分区) NATS 保证单 Consumer 顺序,不保证跨 Consumer
需要精确一次语义 Kafka NATS 提供至少一次
已有 AMQP 生态 RabbitMQ 迁移成本高于性能收益
超大规模事件溯源(TB 级) Kafka Kafka 的日志抽象更适合

四、三种通信协议:MCP vs A2A vs NATS Agent Protocol

Agent 之间"怎么说话"在 2026 年有三个竞争者:

维度 MCP (Anthropic) A2A (Google) NATS Agent Protocol (Synadia)
定位 Agent ↔ 工具 Agent ↔ Agent Agent ↔ 通信层
发布时间 2024.11 2025.04 2026.05
传输层 JSON-RPC (stdio/SSE) HTTP/JSON + gRPC NATS Pub/Sub + Req/Rep
服务发现 无(手动配置) Agent Card (HTTP endpoint) NATS Micro Service Discovery
流式响应 SSE gRPC Streaming NATS Chunked Response
心跳/存活 需自建 内置(30s 间隔)
生态 97M 月下载,VS Code/Claude Desktop 集成 50+ 合作伙伴,Google 生态 早期,但协议最轻量
适合场景 Agent 调用数据库/API/文件系统 跨组织 Agent 协作 同一集群内 Agent 间高速通信

4.1 实际使用建议——三层协议栈

┌──────────────────────────────────────┐
│  Agent ←→ Agent (跨组织/跨云)       │  → A2A 协议
├──────────────────────────────────────┤
│  Agent ←→ Agent (同一集群内)        │  → NATS Agent Protocol
├──────────────────────────────────────┤
│  Agent ←→ 工具/数据库/API           │  → MCP 协议
└──────────────────────────────────────┘

这三层不是互斥的——一个生产系统可能同时用 A2A 做跨云 Agent 发现、NATS 做集群内高速通信、MCP 做工具调用。


五、完整实操:200 行 Python 搭建多 Agent 系统

下面用 NATS + Python 从头搭建一个 Orchestrator + 3 Workers 的多 Agent 系统。

5.1 环境准备(每一步都可复现)

# Step 1: 安装 NATS Server(单二进制,2 秒搞定)
$ curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main \
  | PREFIX=/usr/local bash

# Step 2: 启动 NATS Server(带 JetStream)
$ nats-server -js -m 8222 &
# 输出: Server is ready, JetStream is enabled

# Step 3: 安装 Python 依赖
$ pip install nats-py openai  # nats-py 是 NATS 官方 Python 客户端

# Step 4: 验证 NATS 连通性
$ nats server check connection
# 输出: OK: connected to nats://localhost:4222

5.2 系统架构

┌──────────────────────────────────────────────┐
│                  NATS Server                  │
│                                              │
│  subjects:                                    │
│  agents.tasks.{agent_id}      (任务分发)      │
│  agents.results.{agent_id}    (结果汇聚)      │
│  agents.hb.*                  (心跳)          │
│  agents.events.>              (事件总线)      │
└──────┬──────────┬──────────┬─────────────────┘
       │          │          │
  ┌────┴────┐ ┌───┴────┐ ┌──┴─────┐
  │Research │ │ Code   │ │ Review │
  │ Worker  │ │ Worker │ │ Worker │
  └─────────┘ └────────┘ └────────┘
       │          │          │
       └──────────┼──────────┘
                  │
          ┌───────┴───────┐
          │ Orchestrator  │
          │ (任务拆解+聚合) │
          └───────────────┘

5.3 完整代码

#!/usr/bin/env python3
"""
多 Agent 协作系统 — NATS 消息总线 + Supervisor 模式
运行方式: python multi_agent_system.py
前置条件: nats-server -js -m 8222 & (已在 Step 2 启动)
"""

import asyncio
import json
import time
from dataclasses import dataclass, field
from typing import Optional
from nats.aio.client import Client as NATS
from nats.aio.errors import ErrTimeout


# ── 消息格式 ────────────────────────────────────
@dataclass
class AgentTask:
    task_id: str
    agent_type: str      # "research" | "code" | "review"
    prompt: str
    context: dict = field(default_factory=dict)

@dataclass
class AgentResult:
    task_id: str
    agent_type: str
    output: str
    latency_ms: float
    success: bool


# ── Worker Agent 基类 ────────────────────────────
class WorkerAgent:
    """每个 Worker 订阅自己的任务 subject,处理完后发布结果"""

    def __init__(self, agent_id: str, agent_type: str):
        self.agent_id = agent_id
        self.agent_type = agent_type
        self.task_subject = f"agents.tasks.{agent_id}"
        self.result_subject = f"agents.results.{agent_id}"
        self.hb_subject = f"agents.hb.{agent_type}.{agent_id}"
        self.nc = NATS()

    async def start(self):
        await self.nc.connect("nats://localhost:4222")

        # 订阅任务队列(JetStream push consumer)
        await self.nc.subscribe(
            self.task_subject,
            cb=self.handle_task,
            queue="workers"  # 同一类型的 worker 组成 queue group
        )

        # 发送心跳
        asyncio.create_task(self._heartbeat())
        print(f"[{self.agent_type}] Worker {self.agent_id} ready on {self.task_subject}")

    async def _heartbeat(self):
        """每 5 秒发心跳,离线超过 15 秒被判定为 dead"""
        while True:
            await self.nc.publish(
                self.hb_subject,
                json.dumps({
                    "agent_id": self.agent_id,
                    "agent_type": self.agent_type,
                    "ts": time.time()
                }).encode()
            )
            await asyncio.sleep(5)

    async def handle_task(self, msg):
        """处理任务——实际生产中这里调 LLM"""
        task = json.loads(msg.data.decode())
        t_start = time.time()

        try:
            # ⚡ 实际调用 LLM 的地方
            # response = await llm_client.chat(task["prompt"])
            # 这里用模拟延迟代替
            await asyncio.sleep(0.5)  # 模拟 LLM 调用

            result = AgentResult(
                task_id=task["task_id"],
                agent_type=self.agent_type,
                output=f"[{self.agent_type}] Processed: {task['prompt'][:50]}...",
                latency_ms=(time.time() - t_start) * 1000,
                success=True
            )
        except Exception as e:
            result = AgentResult(
                task_id=task["task_id"],
                agent_type=self.agent_type,
                output=str(e),
                latency_ms=(time.time() - t_start) * 1000,
                success=False
            )

        # 发布结果
        await self.nc.publish(
            self.result_subject,
            json.dumps({
                "task_id": result.task_id,
                "agent_type": result.agent_type,
                "output": result.output,
                "latency_ms": result.latency_ms,
                "success": result.success
            }).encode()
        )
        await self.nc.publish(msg.reply, b"ACK")  # 回复确认

    async def stop(self):
        await self.nc.close()


# ── Orchestrator ─────────────────────────────────
class Orchestrator:
    """接收用户请求 → 拆解任务 → 分发给 Workers → 聚合结果"""

    def __init__(self):
        self.nc = NATS()
        self.results: dict = {}  # task_id → AgentResult
        self.pending: set = set()
        self.done_event = asyncio.Event()

    async def start(self):
        await self.nc.connect("nats://localhost:4222")

    async def decompose_and_dispatch(self, user_request: str) -> list:
        """
        实际生产中用 LLM 做任务拆解,这里用规则模拟
        LLM 拆解 prompt:
        "Decompose the following task into subtasks for research, code, and review agents:
         {user_request}"
        """
        # 模拟 LLM 拆解(生产中用真实的 LLM 调用)
        subtasks = [
            AgentTask(task_id=f"task-{int(time.time()*1000)}-1",
                      agent_type="research",
                      prompt=f"Research: {user_request}"),
            AgentTask(task_id=f"task-{int(time.time()*1000)}-2",
                      agent_type="code",
                      prompt=f"Implement: {user_request}"),
            AgentTask(task_id=f"task-{int(time.time()*1000)}-3",
                      agent_type="review",
                      prompt=f"Review: {user_request}"),
        ]

        # 并行发布任务给所有 Worker
        for task in subtasks:
            subject = f"agents.tasks.{task.agent_type}_worker_1"
            await self.nc.publish(subject, json.dumps({
                "task_id": task.task_id,
                "agent_type": task.agent_type,
                "prompt": task.prompt
            }).encode())
            self.pending.add(task.task_id)
            print(f"[Orchestrator] Dispatched {task.task_id}{subject}")

        return subtasks

    async def collect_results(self, timeout: float = 30.0):
        """收集所有 Worker 结果,支持超时"""
        async def on_result(msg):
            data = json.loads(msg.data.decode())
            task_id = data["task_id"]
            self.results[task_id] = data
            self.pending.discard(task_id)
            print(f"[Orchestrator] Received result for {task_id} "
                  f"({len(self.pending)} remaining)")

            if not self.pending:
                self.done_event.set()  # 全部完成

        # 订阅所有 result subjects
        for agent_type in ["research", "code", "review"]:
            subject = f"agents.results.{agent_type}_worker_1"
            await self.nc.subscribe(subject, cb=on_result)

        # 等待全部完成或超时
        try:
            await asyncio.wait_for(self.done_event.wait(), timeout=timeout)
        except asyncio.TimeoutError:
            print(f"[Orchestrator] Timeout! {len(self.pending)} tasks still pending: {self.pending}")

        return self.results

    async def aggregate(self, results: dict, user_request: str) -> str:
        """
        聚合 Worker 结果。实际生产中用 LLM 做 synthesis:
        "Synthesize these agent outputs into one coherent answer: {results}"
        """
        success_count = sum(1 for r in results.values() if r["success"])
        total_latency = sum(r["latency_ms"] for r in results.values())

        summary = f"""
        ═══════════════════════════════════════
        Multi-Agent Execution Report
        ═══════════════════════════════════════
        Request: {user_request}
        Tasks: {len(results)} dispatched, {success_count} succeeded

        Individual Results:
        {"".join(f"  [{r['agent_type']}] {'✅' if r['success'] else '❌'} "
                 f"{r['latency_ms']:.0f}ms → {r['output']}
        " for r in results.values())}

        Total Wall Time: {total_latency:.0f}ms
        ═══════════════════════════════════════
        """
        return summary

    async def stop(self):
        await self.nc.close()


# ── 监控:统一心跳收集 ──────────────────────────
class AgentMonitor:
    """订阅 agents.hb.> 通配符实时监控所有 Agent 存活状态"""

    def __init__(self):
        self.nc = NATS()
        self.agents: dict = {}  # agent_id → last heartbeat

    async def start(self):
        await self.nc.connect("nats://localhost:4222")
        await self.nc.subscribe("agents.hb.>", cb=self._on_heartbeat)
        asyncio.create_task(self._check_liveness())

    async def _on_heartbeat(self, msg):
        data = json.loads(msg.data.decode())
        agent_id = data["agent_id"]
        self.agents[agent_id] = {"last_beat": time.time(), **data}

    async def _check_liveness(self):
        """ 10 秒检查一次——超过 15 秒无心跳 = 离线"""
        while True:
            await asyncio.sleep(10)
            now = time.time()
            for agent_id, info in list(self.agents.items()):
                if now - info["last_beat"] > 15:
                    print(f"⚠️  [Monitor] Agent {agent_id} is DEAD "
                          f"(last heartbeat {now - info['last_beat']:.0f}s ago)")
                    del self.agents[agent_id]


# ── 主函数 ───────────────────────────────────────
async def main():
    # 启动 Workers
    research_worker = WorkerAgent("research_worker_1", "research")
    code_worker = WorkerAgent("code_worker_1", "code")
    review_worker = WorkerAgent("review_worker_1", "review")

    # 启动监控
    monitor = AgentMonitor()

    # 启动 Orchestrator
    orchestrator = Orchestrator()

    # 并发启动所有组件
    await asyncio.gather(
        research_worker.start(),
        code_worker.start(),
        review_worker.start(),
        monitor.start(),
        orchestrator.start(),
    )

    # 用户请求
    user_request = "Build a REST API for a todo app with authentication"

    # Orchestrator 拆解 + 分发
    await orchestrator.decompose_and_dispatch(user_request)

    # 收集结果(30 秒超时)
    results = await orchestrator.collect_results(timeout=30.0)

    # 聚合并输出
    report = await orchestrator.aggregate(results, user_request)
    print(report)

    # 清理
    await research_worker.stop()
    await code_worker.stop()
    await review_worker.stop()
    await monitor.nc.close()
    await orchestrator.stop()

if __name__ == "__main__":
    asyncio.run(main())

5.4 运行与验证

# 终端 1: 启动 NATS
$ nats-server -js -m 8222 &

# 终端 2: 启动监控(可选)
$ nats sub "agents.hb.>" 
# 你会看到每 5 秒一次的心跳:
# [research_worker_1]: {"agent_id": "...", "ts": 1751700000}

# 终端 3: 运行多 Agent 系统
$ python multi_agent_system.py

# 预期输出:
# [research] Worker research_worker_1 ready on agents.tasks.research_worker_1
# [code] Worker code_worker_1 ready on agents.tasks.code_worker_1
# [review] Worker review_worker_1 ready on agents.tasks.review_worker_1
# [Orchestrator] Dispatched task-1751700000-1 → agents.tasks.research_worker_1
# [Orchestrator] Dispatched task-1751700000-2 → agents.tasks.code_worker_1
# [Orchestrator] Dispatched task-1751700000-3 → agents.tasks.review_worker_1
# [Orchestrator] Received result for task-1751700000-1 (2 remaining)
# [Orchestrator] Received result for task-1751700000-2 (1 remaining)
# [Orchestrator] Received result for task-1751700000-3 (0 remaining)
# ═══════════════════════════════════════
# Multi-Agent Execution Report
# ═══════════════════════════════════════
# ...

5.5 压力测试——看看能跑多少并发

# 用 NATS bench 测试消息吞吐
$ nats bench --pub 10 --sub 10 --size 256 --msgs 10000 agents.tasks.test

# 典型输出(单节点):
# Pub:   1,234,567 msgs/sec
# Sub:   1,234,567 msgs/sec
# P50:   0.05ms
# P99:   0.30ms
# Max:   15.2ms

# 10MB 大消息测试(模拟带附件的 Agent 间通信)
$ nats bench --pub 1 --sub 1 --size 10485760 --msgs 100 agents.tasks.large
# P50 ~50ms(大消息需序列化/反序列化开销)

六、生产硬化:五个你不做一定会后悔的事

6.1 设置硬性轮次上限(防成本黑洞)

# ❌ 危险代码
while not converged:
    result = agent_a.invoke(...)  # 可能无限循环

# ✅ 生产安全
MAX_ITERATIONS = 5
COST_BUDGET_USD = 2.00  # 单次请求最大成本

for i in range(MAX_ITERATIONS):
    if cost_tracker.total > COST_BUDGET_USD:
        raise BudgetExceededError(f"Cost {cost_tracker.total} exceeds budget {COST_BUDGET_USD}")
    result = agent.invoke(...)

6.2 JetStream 持久化防任务丢失

# 创建工作队列 stream
$ nats stream add AGENT_WORK_QUEUE \
  --subjects "agents.tasks.>" \
  --retention work \
  --storage file \
  --max-msgs-per-subject 1000 \
  --max-age 1h \
  --ack \
  --discard old

# 创建 KV store 用于共享状态
$ nats kv add agent-state \
  --history 5 \
  --replicas 3

# 查看持久化状态
$ nats kv get agent-state orchestrator.context

当 Worker 崩溃时,未 ACK 的消息自动重新投递给其他 Worker——无需手动重试逻辑。

6.3 死信队列(Dead Letter Queue)

# 创建 DLQ
$ nats stream add AGENT_DLQ \
  --subjects "agents.dlq.>" \
  --storage file \
  --max-msgs 10000 \
  --retention limits

# 消费失败超过 3 次的消息路由到 DLQ
# 在 Python 代码中配置 JetStream consumer:
# consumer_config = {
#     "max_deliver": 3,
#     "ack_wait": 30,  # 30 秒未 ack 算超时
#     "backoff": [1, 2, 4, 8]  # 重试间隔:1s, 2s, 4s, 8s
# }

6.4 全链路追踪

# 启用 NATS 分布式追踪(需要 NATS 2.11+)
$ nats server request '$SYS.REQ.SERVER.PING.TRACING'
# 在每个消息中注入 trace_id,贯穿整个 Agent 调用链

在 Python 代码中:

import uuid

trace_id = str(uuid.uuid4())

# 所有 Agent 间消息携带 trace_id
headers = {
    "X-Trace-ID": trace_id,
    "X-Parent-Span": parent_span_id,
}
await nc.publish(subject, payload, headers=headers)

6.5 权限隔离——Agent 不应该能访问所有 subject

# 在生产 NATS config 中设置 ACL
# /etc/nats/nats-server.conf
authorization {
    AGENT_WORKERS = {
        subscribe = ["agents.tasks.research_worker_1",
                      "agents.tasks.code_worker_1",
                      "agents.tasks.review_worker_1"]
        publish = ["agents.results.*", "agents.hb.*"]
    }

    ORCHESTRATOR = {
        publish = ["agents.tasks.>"]
        subscribe = ["agents.results.>", "agents.hb.>"]
    }

    MONITOR = {
        subscribe = ["agents.hb.>"]
        publish = []  # 只读,不能发消息
    }
}

七、常见故障与排查

"一个 Worker 挂了,所有任务卡住"

症状:Orchestrator 下发 3 个任务,只收到 2 个 result,一直等第 3 个
根因:No ACK → JetStream 自动重投递(如果没有 JetStream → 直接丢失)
排查:
  $ nats consumer info AGENT_TASKS research_consumer
  # 查看 pending 消息数和 redelivery 次数
修复:
  1. 确认 JetStream consumer 的 ack_wait 和 max_deliver 配置正确
  2. 检查 Worker 日志——是否在处理中卡死(LLM API 超时)

"消息乱序导致上下文错乱"

症状:Agent C 收到 Agent A 的结果,但 Agent B 的还没到
根因:NATS 不保证跨 subject 的顺序
修复:
  1. 在消息中添加 sequence_id
  2. 用 JetStream stream 的 ordered consumer
  3. 或者在 Orchestrator 层面做 barrier sync(所有结果到齐再聚合)

"心跳风暴——监控系统被淹没"

症状:50  Agent5 秒心跳 = 每秒 10 条消息,监控 channel 爆炸
根因:所有 Agent 用同一个 heartbeat interval
修复:
  1. 随机 jitterinterval = random.uniform(4, 6)
  2. 或者用 NATS  connection status 代替应用层心跳:
     $ nats event '>'
     # 订阅连接事件,nats-server 自动通知 connect/disconnect

八、扩展阅读与下一步


总结

决策 答案
编排模式 Supervisor 是 2026 默认,复杂度低于 Debate,灵活性高于 Pipeline
消息基建 NATS 对 Agent 系统是天然选择——延迟低、协议原生、部署简单
通信协议 三层:A2A(跨云)、NATS(集群内)、MCP(工具调用)——非互斥
最大坑 忘记设置硬性轮次上限 → 成本黑洞;没有 JetStream → 任务丢失
一句话 多 Agent 不是多几个模型,是通信拓扑 × 协议选型 × 故障隔离三件事

本专栏已发布 19 篇,覆盖 Agent 部署全链路:从 CUDA Allocator 原理 → Flash Attention 安装踩坑 → EngineDeadError 七种死因 → 启动参数调优 → FP8 量化 → 本篇多 Agent 通信——下一篇进入评估与观测。


本文由 admin 原创,转载请注明出处。

相关推荐

评论

0
暂无评论,来发表第一条评论吧

发表评论

登录 后发表评论

发现更多