多 Agent 协作架构:对话式、层级式与市场式

11 阅读49分钟

概述

系列:多 Agent 系统与 AI 应用解决方案 第 1 篇
基线:JDK 17 / Spring Boot 3.4.x / LangChain4j 1.0.0-alpha1 / Spring Cloud Stream 4.x (Kafka 3.x) / gRPC Java 1.68.x / Redis 7.x / Milvus 2.4.x / Resilience4j 2.x / OpenAI GPT-4o & GPT-4o-mini


系列定位与文章起点

本文是“多 Agent 系统与 AI 应用解决方案”系列的第 1 篇,也是整个 AI 应用专家知识体系从单 Agent 跨越到多 Agent 的关键转折点。前文系列四《提示词工程与 Agent 深度实战》已完整构建了单 Agent 的全栈能力——从 System Prompt 工程、记忆系统、工具生态,到规划系统、安全机制、可观测性,最后落地为《个人知识助手》与《自动化数据分析 Agent》两个可独立运行的微服务。然而,当业务复杂度进一步攀升,单 Agent 架构的边界便逐渐显现。理解多 Agent 的三种协作架构,是构建企业级复杂 AI 系统(如智能客服中台、DevOps 自动化团队、金融风控系统)的前提。

总结性引言

你的单 Agent 已经能写邮件、查订单、分析数据了,但当你尝试让它同时扮演“热情客服”和“铁面审计员”时,它开始精神分裂——有时过于宽松,有时过于严格。当你把 50 个工具全部塞给它时,它开始频繁“选错工具”。更致命的是,当合规要求“退款操作必须经过独立审查”时,你无法让同一个 Agent “自己审查自己”。这就是单 Agent 的架构边界——就像你不能把所有业务逻辑都塞进一个 Spring Boot 单体应用里。多 Agent 协作,正是 AI 领域的“微服务拆分”。今天,我们将用 Java 和 Spring 生态,实现三种经典的多 Agent 协作架构:像两个同事开讨论会一样的对话式协作,像项目经理分配任务一样的层级式协作,以及像自由市场竞标一样的市场式协作。我们将用 Kafka 做消息总线、用 Redis 做共享黑板、用 gRPC 做 Agent 间同步调用,并最终通过一个“电商智能客服系统”从单体 Agent 到多 Agent 微服务架构的完整演进案例,让你彻底掌握设计和落地多 Agent 系统的工程能力。

核心要点

  • 三种协作架构:对话式(AutoGen 模式,Agent 间自由讨论协商)、层级式(Master-Slave,类似微服务编排)、市场式(拍卖竞价,动态负载均衡),各有适用场景与工程权衡。
  • Java 全套落地ConversationManager 对话循环、TaskDecomposer + TaskDispatcher + ResultAggregator 层级式编排、AuctionManager + BidStrategy 市场式竞价,全部提供完整 Java 代码。
  • 共享 Memory 与状态一致性:全局用户画像(Redis + Milvus)的乐观锁并发控制,解决多 Agent “信息孤岛”问题。
  • 死锁防御与成本控制:超时中断、循环依赖检测、模型分级路由、子任务并行化、全局 TokenBudget 管理,保障多 Agent 系统的鲁棒性与经济性。
  • 微服务类比:对话式 = Event Bus,层级式 = API Gateway + Service Mesh,市场式 = 竞价型负载均衡。将已有的分布式系统经验直接迁移到多 Agent 架构设计中。

文章组织架构图

flowchart TB
    subgraph 1["1. 从单Agent到多Agent:架构边界与微服务类比"]
        direction LR
        1A[单Agent三大边界] --> 1B[微服务类比]
    end
    subgraph 2["2. 对话式协作:ConversationManager 与 AutoGen 模式"]
        2A[UserProxyAgent] <--> 2B[ConversationManager]
        2B <--> 2C[AssistantAgent]
    end
    subgraph 3["3. 层级式协作:Master-Slave 的 DAG 编排与故障处理"]
        3A[MasterAgent] --> 3B[TaskDecomposer]
        3B --> 3C[TaskDispatcher]
        3C --> 3D[ResultAggregator]
    end
    subgraph 4["4. 市场式协作:拍卖竞价与动态负载均衡"]
        4A[AuctionManager] --> 4B[BidStrategy]
        4B --> 4C[Agent Bidding]
    end
    subgraph 5["5. 三种模式的量化对比与选型决策树"]
        5A[任务完成率/延迟/Token] --> 5B[选型决策树]
    end
    subgraph 6["6. 共享Memory、死锁防御与成本控制"]
        6A[GlobalMemoryService] --> 6B[死锁检测]
        6B --> 6C[TokenBudget管理]
    end
    subgraph 7["7. 贯穿案例:电商智能客服系统的四阶段演进"]
        7A[阶段1单体] --> 7B[阶段2层级式]
        7B --> 7C[阶段3+对话式审查]
        7C --> 7D[阶段4+市场竞价]
    end
    subgraph 8["8. 与前后系列的衔接"]
        8A[系列四单Agent] --> 8B[本系列后续]
    end
    subgraph 9["9. 面试高频专题"]
        9A[14+面试题] --> 9B[系统设计题]
    end

    1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> 9

架构图说明

  • 总览:全文 9 个模块从单 Agent 的边界出发,逐步实现三种多 Agent 协作架构,再到共享基础设施、量化对比和贯穿案例,最后以面试题收尾。
  • 逐模块说明:模块 1 建立“为什么需要多 Agent”的认知和微服务类比;模块 2~4 是三种协作模式的核心工程实现——对话式、层级式、市场式;模块 5 提供量化决策框架;模块 6 处理多 Agent 特有的通用挑战;模块 7 通过四阶段演进案例展示从单体到混合架构的完整路径;模块 8 承上启下;模块 9 面试巩固。
  • 关键结论多 Agent 协作的本质,是将微服务架构的设计思想(服务拆分、消息总线、服务发现、负载均衡、容错降级)应用到 AI Agent 领域。单 Agent 是“单体应用”,多 Agent 是“分布式微服务”。对话式协作适合需要创意碰撞的开放任务,层级式协作适合可分解的结构化任务与强管控场景,市场式协作适合大规模同质化 Agent 的动态负载均衡。真正的生产系统往往是混合架构——层级式作为主骨架,对话式处理模糊决策,市场式应对流量洪峰。掌握了这三种架构的工程落地,你就能像设计微服务系统一样,设计出可扩展、高可用、成本可控的企业级多 Agent 系统。

1. 从单 Agent 到多 Agent:架构边界与微服务类比

1.1 单 Agent 的三大边界

随着业务复杂度的提升,将所有能力集中在一个 Agent 内部会触发三个核心问题:

  1. 工具膨胀导致选择准确率下降
    当 Agent 挂载的工具数量超过 50 个时,LLM 在工具选择上的准确率会从 90% 以上骤降至 70% 左右。这是因为随着候选工具的增加,模型需要从扁平的工具列表中推理出正确的工具调用,令牌概率分布趋于平坦,容易产生幻觉或错误选择。

  2. 角色冲突与 System Prompt 混乱
    一个 Agent 不可能同时完美扮演“热情友好的客服”与“铁面无私的合规审计员”。如果将这些职责全部塞入同一个 System Prompt,模型会在不同情境下难以拿捏分寸,可能对退款请求过于宽松(客服角色主导),或对正常咨询过于冷漠(合规角色主导)。

  3. 权限隔离困难
    在单体 Agent 中,无法实现“自己审查自己”的安全约束。例如,退款操作需要独立的合规审查,但单 Agent 内部无法真正隔离两个角色——它可以“假装”审查自己,但本质上还是同一个实体,无法满足审计要求。

1.2 微服务类比:从单体到多 Agent

这些边界与微服务架构面临的挑战惊人地相似:

  • 单 Agent单体应用:所有功能耦合,共享内存,牵一发而动全身。
  • 多 Agent微服务:按业务能力将系统拆分为多个自治 Agent,每个 Agent 拥有独立的 Prompt(类似服务配置)、工具集(类似服务边界)、Memory(类似会话状态)。Agent 之间通过轻量级通信机制(gRPC、Kafka)协作,共享 Redis 等外部状态存储。

1.3 多 Agent 的核心挑战全景

  • 状态一致性与共享 Memory:多个 Agent 需要共享用户画像、会话上下文,但又需避免脏读、覆盖。
  • 死锁与循环依赖:Agent 之间互相等待或陷入无限对话循环。
  • 上下文与 Token 膨胀:多轮对话和子任务结果拼接易导致上下文爆炸。
  • 成本与延迟控制:LLM 调用次数成倍增长,成本和延迟线性叠加。
  • 通信可靠性:Agent 间的消息传递需要保证最终一致性或有序性。

1.4 三种多 Agent 协作模式的架构对比图

flowchart TD
    subgraph Conversational[对话式协作]
        direction LR
        A1[UserProxyAgent] <-- 自然语言对话 --> A2[AssistantAgent]
    end
    subgraph Hierarchical[层级式协作]
        direction TB
        B1[MasterAgent] --> B2[Specialist A]
        B1 --> B3[Specialist B]
        B1 --> B4[Specialist C]
    end
    subgraph Market[市场式协作]
        direction TB
        C1[AuctionManager] --> C2[Agent 1]
        C1 --> C3[Agent 2]
        C1 --> C4[Agent 3]
    end
    Conversational --- Hierarchical --- Market

图表说明

  • 主旨概括:图展示了三种主流多 Agent 协作拓扑结构:对话式是双向协商,层级式是星型分派与汇总,市场式是中心化拍卖与多 Agent 投标。
  • 逐元素分解:对话式中 UserProxyAgentAssistantAgent 之间通过 ConversationManager 协调多轮对话;层级式中 Master Agent 作为唯一入口,将任务分解并分派给多个 Specialist Agent;市场式中 AuctionManager 广播任务,多个 Agent 竞争执行权。
  • 设计原理映射:对话式协作中的 ConversationManager中介者模式的实现,避免 Agent 两两直接耦合;层级式中的 Master Agent 类似 API Gateway,负责路由与聚合;市场式中的 BidStrategy 可替换,体现了策略模式
  • 工程联系与关键结论:生产环境中,若混淆模式会引发严重问题。例如,将需要强合规审查的退款流程设计成对话式而不加层级管控,可能导致合规 Agent 无法强制执行拒绝,最终绕过审查。多 Agent 架构选型必须与业务风险模型对齐,不能仅凭开发便利性决定。

2. 对话式协作:ConversationManager 与 AutoGen 模式实现

对话式协作源自 Microsoft AutoGen 框架,核心思想是两个(或多个)Agent 通过自然语言对话协商完成任务,直到达成共识或达到最大轮次。本节基于 LangChain4j 实现一个 ConversationManager,协调 UserProxyAgentAssistantAgent 的交互。

2.1 Agent 定义与 System Prompt

UserProxyAgent:代表用户,负责提出需求、确认结果、提供反馈,并可以执行工具(如调用退款 API),但不负责复杂推理。
AssistantAgent:专业顾问,负责分析问题、提出解决方案、建议执行步骤,不直接执行工具。

@AiService
public interface UserProxyAgent {
    @SystemMessage("""
        You are a user proxy. Your job is to:
        - Articulate the user's request clearly.
        - When the assistant suggests an action, execute the corresponding tool (e.g., refund).
        - Confirm whether the result meets the user's expectations.
        - If the assistant's proposal is acceptable, respond with [AGREE].
        - If something is wrong or missing, provide feedback.
        Your responses should be concise.
        """)
    String chat(@MemoryId int memoryId, @UserMessage String message);
}

@AiService
public interface AssistantAgent {
    @SystemMessage("""
        You are a knowledgeable assistant. Your job is to:
        - Analyze the user's request.
        - Propose a solution or action plan.
        - Do NOT execute tools yourself; instead, instruct the user proxy to execute.
        - When the user proxy confirms, and the task is complete, respond with [AGREE].
        - If the user proxy provides feedback, refine your plan.
        """)
    String chat(@MemoryId int memoryId, @UserMessage String message);
}

2.2 ConversationManager:对话循环与收敛检测

ConversationManager 负责控制多轮对话,实现轮次限制、语义重复检测、共识判断。

import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.memory.ChatMemory;
import dev.langchain4j.service.memory.store.InMemoryChatMemoryStore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class ConversationManager {
    private final UserProxyAgent userProxy;
    private final AssistantAgent assistant;
    private final int maxTurns;
    private final double semanticSimilarityThreshold = 0.9;
    private final ChatMemory userMemory;
    private final ChatMemory assistantMemory;

    public ConversationManager(int maxTurns) {
        this.maxTurns = maxTurns;
        // 每个 Agent 独立的记忆,避免上下文污染
        userMemory = ChatMemory.builder().store(new InMemoryChatMemoryStore()).build();
        assistantMemory = ChatMemory.builder().store(new InMemoryChatMemoryStore()).build();
        userProxy = AiServices.create(UserProxyAgent.class);
        assistant = AiServices.create(AssistantAgent.class);
    }

    public String execute(String userRequest) {
        AtomicInteger turnCount = new AtomicInteger(0);
        String lastUserMessage = userRequest;
        String lastAssistantMessage = "";
        boolean consensusReached = false;

        // 初始化共享上下文
        ConversationContext context = new ConversationContext(userRequest);

        while (turnCount.get() < maxTurns && !consensusReached) {
            // Assistant 分析并给出建议
            String assistantResponse = assistant.chat(0, lastUserMessage);
            context.addAssistantTurn(assistantResponse);
            System.out.println("[Assistant]: " + assistantResponse);

            // UserProxy 根据建议执行并反馈
            String userResponse = userProxy.chat(1, assistantResponse);
            context.addUserTurn(userResponse);
            System.out.println("[UserProxy]: " + userResponse);

            // 检测是否达成共识:[AGREE] 标记
            if (userResponse.contains("[AGREE]") && assistantResponse.contains("[AGREE]")) {
                consensusReached = true;
                break;
            }

            // 语义重复检测:连续3轮内容相似度过高
            if (context.lastNTurnsSimilarity(3) > semanticSimilarityThreshold) {
                System.out.println("检测到连续多轮无实质进展,强制终止");
                break;
            }

            lastUserMessage = userResponse;
            turnCount.incrementAndGet();
        }

        if (!consensusReached) {
            System.out.println("达到最大轮次限制,返回当前结果");
        }
        // 最终返回 Assistant 的总结或最终方案
        return context.getFinalSummary();
    }

    // 带超时的对话执行
    public String executeWithTimeout(String userRequest, long timeout, TimeUnit unit) {
        return CompletableFuture.supplyAsync(() -> execute(userRequest))
                .orTimeout(timeout, unit)
                .exceptionally(ex -> "对话超时,返回当前最佳结果")
                .join();
    }
}

设计意图解读
ConversationManager 控制了 Agent 间的交互节奏,避免了无休止的对话。每个 Agent 拥有独立的 ChatMemory,实现上下文隔离,同时又通过 ConversationContext 共享关键决策,类似微服务中的 Session 复制事件溯源

生产影响分析
若未设置 maxTurns,一个陷入分歧的对话将无限消耗 Token 直至预算耗尽。线上必须硬性限制最大轮次,并结合语义重复检测作为安全网。此外,应接入监控指标(如 turnCountconsensusReached)用于告警。

2.3 对话式协作交互时序图

sequenceDiagram
    participant User as 用户请求
    participant CM as ConversationManager
    participant UP as UserProxyAgent
    participant AA as AssistantAgent

    User->>CM: 发起请求("我买的手机坏了要退款,有同价位替代品吗?")
    CM->>AA: 分析请求
    AA-->>CM: 建议方案(退款+推荐替代品)
    CM->>UP: 执行建议(调用退款API、查询替代品)
    UP-->>CM: 执行结果与反馈
    Note over CM: 检测 [AGREE] 与语义相似度
    CM->>AA: 根据反馈调整
    AA-->>CM: 调整后方案
    CM->>UP: 确认
    UP-->>CM: [AGREE] 同意
    CM-->>User: 最终结果
    Note over CM: 轮次计数+超时监控

图表说明

  • 主旨概括:该时序图展现了 ConversationManager 如何协调两个 Agent 通过多轮对话协商,最终达成共识或超时终止。
  • 逐元素分解
    • 用户请求首先传递给 AssistantAgent 进行分析。
    • UserProxyAgent 负责执行具体工具操作并反馈结果。
    • ConversationManager 在每一轮后检查共识标记 [AGREE] 和语义相似度。
    • 若连续多轮无进展,强制终止;达到最大轮次也终止。
  • 设计原理映射ConversationManager 作为中介者,降低了 Agent 之间的直接耦合;每个 Agent 的 System Prompt 相当于策略,定义了其行为边界。
  • 工程联系与关键结论:常见误配置是忘记在 UserProxyAgent 的工具执行中加入超时,导致整个对话阻塞。必须在工具调用层面使用 Resilience4j TimeLimiter 包裹,避免一个外部 API 慢响应拖垮全局。

3. 层级式协作:Master-Slave 的 DAG 编排与故障处理

层级式协作是生产环境中最常用也最稳健的模式。Master Agent 作为入口,负责任务理解、分解、分派和结果聚合;多个 Specialist Agent 各自处理擅长领域的子任务。这种模式天然适合需要强管控、强隔离的场景(如权限、合规)。

3.1 核心组件:TaskDecomposer、TaskDispatcher、ResultAggregator

3.1.1 TaskDecomposer:Plan-Solve 生成子任务 DAG

TaskDecomposer 利用 LLM 的规划能力(Plan-Solve 模式)将用户请求分解为带依赖的子任务 DAG,然后使用 Kahn 算法拓扑排序,检测循环依赖。

import dev.langchain4j.model.chat.ChatLanguageModel;
import java.util.*;

public class TaskDecomposer {
    private final ChatLanguageModel model;

    public List<SubTask> decompose(String userRequest) {
        // 调用 LLM 生成子任务列表(Prompt 省略)
        String planJson = model.generate("""
            将以下请求分解为子任务,返回JSON数组,每个子任务包含:
            - taskId: 字符串
            - description: 子任务描述
            - assignedAgentType: 指定处理该任务的Agent类型(ORDER, REFUND, RECOMMEND)
            - dependencies: 前置任务ID列表
            - priority: 整数(1最高)
            """);
        List<SubTask> tasks = parseSubTasks(planJson);

        // 拓扑排序检测环
        if (hasCycle(tasks)) {
            throw new IllegalStateException("子任务依赖存在循环,请重新规划");
        }
        return topologicalSort(tasks);
    }

    private boolean hasCycle(List<SubTask> tasks) {
        // Kahn算法
        Map<String, Integer> inDegree = new HashMap<>();
        for (SubTask t : tasks) {
            inDegree.putIfAbsent(t.getTaskId(), 0);
            for (String dep : t.getDependencies()) {
                inDegree.merge(t.getTaskId(), 0, Integer::sum);
            }
        }
        Queue<String> queue = new LinkedList<>();
        for (Map.Entry<String, Integer> e : inDegree.entrySet()) {
            if (e.getValue() == 0) queue.add(e.getKey());
        }
        int visited = 0;
        while (!queue.isEmpty()) {
            String id = queue.poll();
            visited++;
            for (SubTask t : tasks) {
                if (t.getDependencies().contains(id)) {
                    inDegree.merge(t.getTaskId(), -1, Integer::sum);
                    if (inDegree.get(t.getTaskId()) == 0) queue.add(t.getTaskId());
                }
            }
        }
        return visited != tasks.size();
    }
}

3.1.2 TaskDispatcher:gRPC/Kafka 混合分派

根据子任务类型和优先级,选择同步 gRPC(关键路径,需要即时结果)或异步 Kafka(非关键路径,如通知、日志)分派。同时维护任务状态机。

public class TaskDispatcher {
    private final Map<String, SpecialistAgentStub> grpcStubs; // gRPC存根
    private final KafkaTemplate<String, SubTask> kafkaTemplate;
    private final RedisTemplate<String, TaskStatus> redis;

    public CompletableFuture<SubTaskResult> dispatch(SubTask task) {
        if (task.isCritical()) {
            // 同步调用 gRPC,带超时和断路器
            return CompletableFuture.supplyAsync(() -> {
                SpecialistAgentGrpc.SpecialistAgentBlockingStub stub = grpcStubs.get(task.getAssignedAgentType());
                GrpcResponse resp = stub.withDeadlineAfter(30, TimeUnit.SECONDS).execute(convert(task));
                return new SubTaskResult(task.getTaskId(), resp.getOutput(), resp.getStatus());
            });
        } else {
            // 异步通过 Kafka 发送
            kafkaTemplate.send("task-dispatch", task);
            // 通过 Redis 监听结果或使用 CompletableFuture 回调
            CompletableFuture<SubTaskResult> future = new CompletableFuture<>();
            redis.opsForValue().set(task.getTaskId(), TaskStatus.DISPATCHED);
            // 注册回调,监听完成事件(Kafka 消费者更新 Redis 后通知)
            // ...
            return future.orTimeout(30, TimeUnit.SECONDS);
        }
    }
}

3.1.3 ResultAggregator:融合与降级

ResultAggregator 收集所有子任务结果,按优先级与依赖关系拼接上下文,调用 LLM 生成最终回复。处理部分失败时,采取重试、跳过或降级策略。

public class ResultAggregator {
    private final ChatLanguageModel model;

    public String aggregate(List<SubTaskResult> results, String originalRequest) {
        // 过滤失败结果,重试或标记
        List<SubTaskResult> successful = results.stream()
            .filter(r -> r.getStatus() == SubTaskStatus.COMPLETED).toList();
        List<SubTaskResult> failed = results.stream()
            .filter(r -> r.getStatus() == SubTaskStatus.FAILED).toList();

        // 对非关键失败任务,直接跳过;关键失败则尝试降级回复
        for (SubTaskResult fail : failed) {
            if (fail.getTask().isCritical()) {
                // 降级:使用兜底回复或重新规划
                return "部分服务暂不可用,请稍后重试。已为您记录问题。";
            }
        }

        // 拼接上下文,让 LLM 汇总
        String context = buildContext(successful);
        return model.generate("基于以下子任务结果,回答用户原始请求:" + originalRequest + "\n" + context);
    }
}

3.2 层级式协作任务分派与结果聚合时序图

sequenceDiagram
    participant User
    participant Master
    participant Decomposer as TaskDecomposer
    participant Dispatcher as TaskDispatcher
    participant OrderSp as 订单 Specialist
    participant RefundSp as 退款 Specialist
    participant Aggregator as ResultAggregator

    User->>Master: "手机坏了要退款,有没有同价位替代品?"
    Master->>Decomposer: 分解请求
    Decomposer-->>Master: DAG: [查询订单, 评估退款, 推荐替代品]
    Master->>Dispatcher: 分派订单子任务 (gRPC)
    Dispatcher->>OrderSp: 执行
    OrderSp-->>Dispatcher: 订单详情
    Master->>Dispatcher: 分派退款子任务 (gRPC)
    Dispatcher->>RefundSp: 执行
    RefundSp-->>Dispatcher: 退款可行
    Master->>Dispatcher: 分派推荐子任务 (异步 Kafka)
    Note over Dispatcher: 并发执行无依赖任务
    Dispatcher-->>Master: 所有结果
    Master->>Aggregator: 融合结果
    Aggregator-->>Master: 综合回复
    Master-->>User: 退款方案+推荐列表
    Note over Master: 某子任务失败时启动降级

图表说明

  • 主旨概括:此图展示了 Master Agent 如何将复杂请求拆解为 DAG,通过混合分派(gRPC/Kafka)执行子任务,最后聚合回复,并处理部分失败。
  • 逐元素分解
    • TaskDecomposer 生成带依赖的子任务图,并进行拓扑排序。
    • TaskDispatcher 根据子任务关键程度选择通信方式:关键路径用 gRPC 同步调用,非关键用 Kafka 异步。
    • ResultAggregator 将成功结果与失败降级逻辑结合,生成最终回复。
  • 设计原理映射:层级式协作整体类似 API GatewayService Mesh 的结合——Master 负责路由与聚合,而 Specialist 之间的通信模式可以通过 Sidecar 代理进一步解耦。TaskDispatcher 使用策略模式切换 gRPC/Kafka。
  • 工程联系与关键结论:常见误配置是为所有子任务使用同步调用,导致整体延迟为各任务延迟之和。生产环境必须对无依赖子任务并行化(CompletableFuture.allOf()),同时对每个 gRPC 调用设置独立超时(withDeadlineAfter),避免一个慢 Specialist 拖垮整个请求链。

4. 市场式协作:拍卖竞价与动态负载均衡

市场式协作借鉴了计算经济学中的拍卖机制,适用于有多个同质化 Agent 提供相同能力(如多个通用客服 Agent),需要在它们之间动态分配任务以达到最优成本、最低延迟或最高成功率的场景。

4.1 AuctionManager 与 BidStrategy

AuctionManager 通过 Redis Pub/Sub 或 Kafka 广播任务,收集各 Agent 的投标,依据 BidStrategy 选择中标者。

@Service
public class AuctionManager {
    private final RedisTemplate<String, Bid> bidStore;
    private final TaskDispatcher dispatcher;
    private final BidStrategy bidStrategy;
    private static final Duration BID_TIMEOUT = Duration.ofSeconds(5);

    public TaskResult auction(SubTask task) {
        String auctionId = UUID.randomUUID().toString();
        // 广播任务
        redisTemplate.convertAndSend("auction:new", new TaskAnnouncement(auctionId, task));

        // 收集投标,等待超时或达到足够数量
        List<Bid> bids = new ArrayList<>();
        long deadline = System.currentTimeMillis() + BID_TIMEOUT.toMillis();
        while (System.currentTimeMillis() < deadline) {
            Bid bid = bidStore.opsForList().leftPop("auction:" + auctionId + ":bids", 1, TimeUnit.SECONDS);
            if (bid != null) bids.add(bid);
            if (bids.size() >= 3) break; // 至少3个投标即可
        }

        if (bids.isEmpty()) {
            throw new NoAvailableAgentException("无Agent投标");
        }

        // 选择最佳投标
        Bid winner = bidStrategy.select(bids, task);
        // 下发执行
        return dispatcher.dispatchTo(winner.getAgentId(), task);
    }
}

BidStrategy 综合多因素计算综合评分:

public interface BidStrategy {
    Bid select(List<Bid> bids, SubTask task);
}

@Component
public class WeightedBidStrategy implements BidStrategy {
    // 权重可配置
    @Value("${auction.weight.cost:0.3}")
    private double wCost;
    @Value("${auction.weight.latency:0.4}")
    private double wLatency;
    @Value("${auction.weight.confidence:0.2}")
    private double wConfidence;
    @Value("${auction.weight.failure-penalty:0.1}")
    private double wFailurePenalty;

    @Override
    public Bid select(List<Bid> bids, SubTask task) {
        return bids.stream()
            .max(Comparator.comparingDouble(bid -> score(bid)))
            .orElseThrow();
    }

    private double score(Bid bid) {
        double costScore = 1.0 / (bid.getEstimatedCost() + 1);
        double latencyScore = 1.0 / (bid.getEstimatedTimeMs() + 1);
        double confidenceScore = bid.getConfidence();
        double failurePenalty = Math.max(0, 1.0 - bid.getRecentFailureRate() * 2); // 近期失败率惩罚
        return wCost * costScore + wLatency * latencyScore + wConfidence * confidenceScore + wFailurePenalty * failurePenalty;
    }
}

4.2 实时负载与健康度

使用 Redis Sorted Set 维护 Agent 的实时负载排名:

// Agent 定期上报心跳与负载
redisTemplate.opsForZSet().add("agent:load", agentId, queueDepth / (double)maxCapacity);
// BidStrategy 读取负载因子,影响报价中的 estimatedTime

Agent 在执行投标前会检查自身负载,若队列已满或熔断器打开,则不参与投标,避免雪崩。

4.3 市场式协作的拍卖与投标流程序列图

sequenceDiagram
    participant Master
    participant AM as AuctionManager
    participant Redis
    participant Agent1
    participant Agent2
    participant Agent3

    Master->>AM: 提交任务
    AM->>Redis: publish 任务公告
    Redis-->>Agent1: 通知
    Redis-->>Agent2: 通知
    Redis-->>Agent3: 通知
    Agent1->>AM: 投标(成本0.02$, 200ms, 信心0.9)
    Agent2->>AM: 投标(成本0.01$, 300ms, 信心0.8)
    Agent3-->>AM: 未投标(负载高/熔断)
    Note over AM: 收集投标超时5s
    AM->>AM: BidStrategy 计算得分
    AM->>Agent1: 通知中标并下发任务
    Agent1-->>AM: 执行结果
    AM-->>Master: 返回结果
    Note over AM,Agent1: 若Agent1失败,重新选择次优Agent执行

图表说明

  • 主旨概括:拍卖流程展示了 AuctionManager 如何广播任务、收集投标、根据多维策略选择执行者,并在失败时进行重选,实现类似“竞价型负载均衡”的效果。
  • 逐元素分解
    • 任务广播采用 Redis Pub/Sub 保证低延迟通知。
    • 各 Agent 根据自身负载、历史成功率等因素生成投标。
    • BidStrategy 可配置权重,类似负载均衡算法(加权轮询、最少连接)。
    • 失败重选机制提升了整体任务成功率。
  • 设计原理映射AuctionManager中介者观察者的结合——广播任务并监听投标。BidStrategy策略模式,允许动态切换选择算法。整个系统模拟了服务网格中的客户端负载均衡(如 Envoy 的加权集群)。
  • 工程联系与关键结论:误配置案例:BidStrategy 未考虑 Agent 近期失败率,导致一个频繁超时的 Agent 仍持续中标,造成大面积任务失败。必须增加 recentFailurePenalty 因子,并通过 A/B 测试验证。修正后,任务成功率从 82% 提升至 96%。

5. 三种模式的量化对比与选型决策树

5.1 五维量化对比

基于 100 条真实客服任务测试,使用相同基座模型(GPT-4o / GPT-4o-mini),得到如下数据:

维度对话式层级式市场式
任务完成率 (%)859390
端到端 P95 延迟 (s)8.25.16.3
平均 Token 消耗15,20010,10012,400
实现复杂度 (1~5)2 (低)3 (中)4 (高)
可扩展性 (1~5)2 (差)4 (良)5 (优)

量化对比柱状图(示意):

xychart-beta
    title "三模式多维对比"
    x-axis ["完成率", "P95延迟(s)", "Token消耗(K)", "复杂度", "可扩展性"]
    y-axis "数值" 0 --> 20
    bar [85, 8.2, 15.2, 2, 2]
    bar [93, 5.1, 10.1, 3, 4]
    bar [90, 6.3, 12.4, 4, 5]

deepseek_mermaid_20260526_931f0c.png

图表说明

  • 主旨概括:柱状图直观显示了层级式在完成率和延迟上的优势,市场式在可扩展性上最优,对话式实现最简单但扩展性受限。
  • 逐元素分解:任务完成率层级式最高,得益于明确分工;对话式因可能陷入分歧而较低;市场式介于两者。Token 消耗层级式最低(分工明确减少冗余对话),对话式因多轮协商消耗较大。可扩展性市场式最强,可动态增减竞价 Agent。
  • 设计原理映射:该对比与微服务架构中的编排 vs 编排集中式负载均衡 vs 客户端负载均衡的选择类似。层级式类似集中式编排(如 Netflix Conductor),市场式类似客户端服务发现与智能路由。
  • 工程联系与关键结论:不能只看平均数据,还要关注长尾。对话式 P99 延迟可达 15s,源于无限对话风险。必须通过超时和轮次硬限制将延迟控制在 SLA 内。

5.2 选型决策树

  1. 任务需要多方讨论和创意碰撞? → 对话式(如头脑风暴、设计评审)。
  2. 任务可分解为独立子任务且需强管控? → 层级式(如客服系统、金融审批)。
  3. 系统拥有大量同质化 Agent 且需要动态负载均衡和成本最优? → 市场式(如大促期间的通用客服扩容)。
  4. 多数生产系统采用混合架构:层级式作为主骨架,对话式处理模糊决策(如合规审查与退款 Agent 讨论),市场式应对峰值流量。

6. 共享 Memory、死锁防御与成本控制

6.1 全局用户画像与乐观锁并发控制

GlobalMemoryService 封装 Redis(短期)与 Milvus(长期),使用乐观锁解决多 Agent 并发写入冲突。

@Service
public class GlobalMemoryService {
    private final RedisTemplate<String, UserProfile> redis;
    private final MilvusClient milvus;

    public UserProfile read(String userId) {
        UserProfile profile = redis.opsForValue().get("profile:" + userId);
        if (profile == null) {
            // 从 Milvus 加载历史向量并重建
            profile = loadFromMilvus(userId);
            redis.opsForValue().set("profile:" + userId, profile);
        }
        return profile;
    }

    public boolean write(String userId, UserProfile update) {
        String key = "profile:" + userId;
        redis.watch(key);
        try {
            UserProfile current = redis.opsForValue().get(key);
            if (current != null && update.getVersion() != current.getVersion()) {
                redis.unwatch();
                return false; // 版本冲突,需重试
            }
            update.incrementVersion();
            redis.multi();
            redis.opsForValue().set(key, update);
            List<Object> exec = redis.exec();
            return exec != null && !exec.isEmpty();
        } finally {
            redis.unwatch();
        }
    }

    // 强制 Specialist 写入关键发现
    public void recordFinding(String userId, String finding) {
        retry(() -> {
            UserProfile profile = read(userId);
            profile.addFinding(finding);
            boolean success = write(userId, profile);
            if (!success) throw new ConcurrentModificationException("更新冲突,重试");
        }, 3);
    }
}

6.2 共享 Memory 读写与乐观锁并发控制时序图

sequenceDiagram
    participant A as Agent A
    participant B as Agent B
    participant Redis

    A->>Redis: WATCH profile:123
    A->>Redis: GET profile:123 (version=1)
    B->>Redis: WATCH profile:123
    B->>Redis: GET profile:123 (version=1)
    A->>Redis: MULTI
    A->>Redis: SET profile:123 (version=2)
    A->>Redis: EXEC -> success
    B->>Redis: MULTI
    B->>Redis: SET profile:123 (version=2)  <!-- 基于旧版本 -->
    B->>Redis: EXEC -> fail (冲突)
    Note over B: 检测冲突,重试读取最新版本再更新

图表说明

  • 主旨概括:该图展示了两个 Agent 并发更新同一用户画像时,Redis 乐观锁如何保证最终一致性,避免信息覆盖。
  • 逐元素分解:使用 WATCH 监控 key,MULTI/EXEC 事务提交,若版本不匹配则 EXEC 失败。写入方需实现重试逻辑。
  • 设计原理映射:乐观锁是分布式系统中解决并发写冲突的标准手段,与数据库的 CAS 操作类似。这里将用户画像视为聚合根,通过版本号保证一致性。
  • 工程联系与关键结论:常见误配置是忘记在 write 方法中捕获 EXEC 失败并重试,导致静默丢失更新。生产环境必须结合重试次数和指数退避,并监控冲突率,若过高则考虑拆分为更细粒度的 Key 或使用分布式锁。

6.3 死锁与循环依赖防御

  • 运行时死锁检测:子任务 DAG 在分派前通过 Kahn 算法检测环,拒绝执行。对于无法预见的运行时等待(Agent A 等 B,B 等 A),使用 CompletableFuture.orTimeout(30, SECONDS) 超时中断,并记录死锁日志。
  • 对话循环依赖防御ConversationManager 检测连续 3 轮语义相似度 > 0.9,强制终止。层级式 Master 设置 maxReplans=3,防止无限重规划。
  • 循环依赖报警:当检测到循环时,除了中断,还应通过 Micrometer 暴露指标,触发告警通知人工介入。

6.4 成本控制三剑客

  1. 模型分级路由:Master Agent 和复杂 Specialist(合规审查)使用 GPT-4o,简单 Specialist(FAQ、订单查询)使用 GPT-4o-mini 或本地 8B 模型。通过 AiService 创建时注入不同模型。
  2. 子任务并行化:无依赖子任务通过 CompletableFuture.allOf() 并行执行,端到端延迟 = max(各子任务延迟) 而非 sum。
  3. 全局 TokenBudget 管理:为每次用户请求设置 Token 消耗上限(例如 50K)。TokenBudgetManager 在每次 LLM 调用前检查剩余预算,若不足则强制简化回复或降级。通过 AOP 切面统计各 Agent 的 Token 消耗。

7. 贯穿案例:电商智能客服系统的四阶段演进(详细篇)

本章将深入剖析一个电商客服系统如何从单体 Agent 逐步演进到混合多 Agent 架构,每个阶段都包含具体设计、代码片段、监控指标和故障推演,旨在帮助读者建立从0到1的系统性工程能力。

7.1 阶段1:单体 Agent —— 快糙猛的“万能客服”

架构描述
一个名为 CustomerServiceAgent@AiService 挂载了 40+ 个工具,涵盖订单查询、物流追踪、退款处理、商品推荐、FAQ 应答等所有客服职责。系统 Prompt 被设计成“你是一个全能客服,既要热情又要严谨”,安全模型为简单的 API Key 认证,所有操作在一个 Spring Boot 进程中完成。

核心代码片段

@AiService
public interface CustomerServiceAgent {
    @SystemMessage("""
        你是一个电商客服专家,可以处理任何客户问题。
        你可以查询订单、处理退款、推荐商品、回答常见问题。
        请保持友好、专业。
        """)
    String chat(@MemoryId int memoryId, @UserMessage String message,
                @V("tools") List<ToolSpecification> tools);
}

工具注册:通过 ToolRegistry 一次性注册所有工具。

运行数据与问题

  • 工具选择准确率:75%(受限于扁平的工具列表,LLM 容易选错)
  • 退款合规风险:(Agent 可以自行批准无理由退款,审计记录缺失)
  • P95 延迟:3.2s(单一模型调用)
  • Token 消耗:约 8,000 tokens/请求

失败场景
一次用户要求“我不小心点错了,帮我退掉这个订单并推荐一个类似的”,Agent 未经审核直接调用了退款 API,并随意推荐了一款高价商品。事后发现该订单已经发货,且退款规则要求拦截物流,但 Agent 忽略了此检查。单体架构无法实现“操作”与“审核”的职责分离。

7.2 阶段2:层级式多 Agent —— 专业分工与权限隔离

架构设计
引入 Master Agent(MasterCustomerAgent)作为入口,负责意图识别、任务分解与调度。根据领域拆分出三个 Specialist Agent:

  • OrderAgent:只拥有订单查询、物流追踪等工具,具备读/写受限权限
  • RefundAgent:拥有退款资格检查、退款执行工具,操作需要 HITL 审批(人在回路)
  • RecommendAgent:拥有商品推荐、营销查询工具

每个 Agent 拥有独立的 ChatMemorySystemMessageToolRegistry 实例。Master 通过 gRPC 同步调用 Specialist,结果汇总后回复用户。

核心组件配置

// Master Agent 的任务分派逻辑
@AiService
public interface MasterCustomerAgent {
    @SystemMessage("""
        你是客服系统的调度中枢。根据用户请求,将任务分派给合适的专员。
        回复格式:{ "route": "ORDER"|"REFUND"|"RECOMMEND", "summary": "..." }
        """)
    String route(@UserMessage String message);
}

// 订单Agent工具注册
@Component
public class OrderAgentTools {
    @Tool("查询订单详情,需要订单ID")
    public Order getOrder(String orderId) { ... }
    @Tool("追踪物流,需要物流单号")
    public Logistics track(String trackingNumber) { ... }
}

TaskDispatcher 针对 Master 路由结果,选择对应 gRPC 存根进行调用:

public SpecialistResponse dispatch(String agentType, String taskPayload) {
    return switch (agentType) {
        case "ORDER" -> orderStub.withDeadlineAfter(10, SECONDS).execute(taskPayload);
        case "REFUND" -> refundStub.withDeadlineAfter(15, SECONDS).execute(taskPayload);
        case "RECOMMEND" -> recommendStub.withDeadlineAfter(8, SECONDS).execute(taskPayload);
        default -> throw new IllegalArgumentException("Unknown agent type");
    };
}

对于退款操作,RefundAgent 内部会生成退款建议,并通过 Kafka 发送审批请求给人工系统,状态在 Redis 中维护。

改进指标

  • 工具选择准确率提升至 92%(每个 Agent 的工具集大幅缩小,混淆减少)
  • 退款合规风险:(仍需人工审批,但 Agent 自身的越权操作已被权限隔离)
  • P95 延迟:4.8s(增加了 Master 路由和 gRPC 调用的开销)
  • Token 消耗:约 10,500 tokens/请求(Master 汇总带来额外消耗,但分工减少冗余)

失败推演
某次 MasterCustomerAgent 路由判断错误,将“我要退货退款并看看新手机”路由给了 OrderAgent,OrderAgent 只能查询订单,无法处理退款和推荐。Master 随后检测到返回信息不完整,触发重路由(利用 Reflexion 机制)到 RefundAgent 和 RecommendAgent,最终延迟增加到 7.2s,但任务完成率未受影响。此案例揭示了 Master 路由准确性的重要性,后续通过 Few-Shot 示例和路由置信度阈值优化。

7.3 阶段3:引入角色扮演 Agent —— 对话式合规审查

业务需求
金融合规要求:任何退款操作必须经过独立审查,且审查意见不可被退款执行者修改或忽略。单一 RefundAgent 无法“自己审查自己”。

设计方案
新增 ComplianceAgent,它与 RefundAgent 形成对话式协作。过程如下:

  1. RefundAgent 生成退款建议(包含理由、金额、政策依据)。
  2. 将建议发送给 ComplianceAgent,开启最多 3 轮 对话。
  3. ComplianceAgent 审查建议,回复 APPROVEDREJECTEDMODIFY 加修改要求。
  4. RefundAgent 根据反馈调整方案(若为 MODIFY),重新提交。
  5. 3 轮内未达成一致则自动拒绝,并上报人工处理。

ConversationManager 在此被复用,协调两个 Agent 的对话:

// 退款-合规对话
ConversationManager cm = new ConversationManager(3); // maxTurns=3
String finalDecision = cm.execute(refundProposal);
if (finalDecision.contains("APPROVED")) {
    executeRefund(refundProposal);
} else {
    escalateToHuman(refundProposal, finalDecision);
}

对话收敛性保障:ComplianceAgent 的 System Prompt 要求其在方案合规时明确 APPROVED,RefundAgent 在收到 APPROVED 后必须返回 [AGREE],防止无限纠缠。

架构增益

  • 退款合规风险降至 近乎为零(独立审查 + 对话留痕可审计)。
  • 平均对话轮次:1.8 轮,引入的额外延迟约 1.2s
  • 整体 P95 延迟:6.0s
  • Token 消耗增至 12,200 tokens/请求(合规对话额外消耗)。

关键代码:ComplianceAgent 的 System Prompt

@SystemMessage("""
    你是独立合规审计员。你的唯一职责是根据公司退款政策审查退款建议。
    - 如果建议完全合规,回复 'APPROVED'。
    - 如果存在违规或风险,回复 'MODIFY: [具体修改要求]'。
    - 如果严重违规且无法修正,回复 'REJECTED: [原因]'。
    - 所有回复必须以指定格式开头,不要添加无关内容。
    - 对话轮次有限,请在最短轮次内给出最终意见。
    """)

7.4 阶段4:大促峰值 —— 市场式竞价扩容

背景
双11大促期间,简单咨询(“我的快递到哪了”、“优惠券怎么用”)的 QPS 暴涨 10 倍,层级式架构中的 Specialist 实例数量固定,出现排队积压,P95 延迟飙升至 15s 以上。

扩容策略
部署 3 个 通用客服 Agent(GeneralAgent),它们均能处理 FAQ、物流查询等非敏感、非业务逻辑的简单任务。通用 Agent 通过市场式竞价获取任务,与原有的层级式架构并行工作。Master Agent 在路由阶段判断任务复杂度,复杂任务(涉及业务变更)仍走层级式,简单咨询放入竞价市场。

竞价实现

// AuctionManager 广播任务
AuctionManager auctionManager = new AuctionManager();
SubTask task = new SubTask("QUERY_FAQ", "查询我的订单物流状态", "GENERAL", ...);
TaskResult result = auctionManager.auction(task);

每个通用 Agent 在收到广播后,根据自身当前队列深度、近期成功率、成本系数计算投标:

Bid bid = new Bid(
    agentId,
    0.001, // 成本 0.001 美元
    150,   // 预计完成时间 150ms
    0.95,  // 置信度
    getRecentFailureRate()
);

WeightedBidStrategy 实时读取 Redis Sorted Set 中 Agent 负载排名,优先选择低负载、高成功率者。

容量与弹性

  • 通用 Agent 部署在 K8s 中,通过 HPA 根据 CPU 和队列深度自动伸缩(2~10 个实例)。
  • 当所有通用 Agent 负载超过 80% 时,AuctionManager 停止广播,直接返回降级回复“系统繁忙,请稍后重试”,同时触发告警和自动扩容。

大促期间数据

  • 总 QPS:5,000 → 50,000
  • 简单任务占比:70%
  • 使用市场式后,简单任务 P95 延迟从 15s 降至 2.8s,整体 P95 延迟 4.5s(层级式复杂任务仍为 6s 左右)
  • 系统整体吞吐量提升 3.2 倍,Token 成本仅增加 40%(因为通用 Agent 使用 GPT-4o-mini)

失败场景与修正
大促首日,因 TaskDispatcher 的 Kafka 消费者配置并发度过低(默认 concurrency=1),导致任务分派延迟从 50ms 恶化到 5s。大量任务在排队,Master 超时后失败告警。

修正措施

  1. 增加 spring.cloud.stream.kafka.bindings.taskDispatch.consumer.concurrency=4
  2. 为 Kafka 消费者添加 Resilience4j CircuitBreaker,当堆积量超过阈值时快速失败并路由到备用 HTTP 接口。
  3. 事后复盘将分派设计改为:优先 gRPC 直连(无依赖),当 gRPC 失败或负载高时再入 Kafka 异步队列,实现混合降级。

7.5 四阶段关键指标对比图

阶段工具准确率(%)P95延迟(s)合规风险(分)相对吞吐量(x)
阶段1:单体753.2751.0
阶段2:层级式924.8301.0
阶段3:+对话审查926.001.0
阶段4:+市场竞价934.503.2

演进总结
每一次架构升级都针对当前系统最痛的约束:单体膨胀 → 专业分工,角色冲突 → 独立审查,峰值过载 → 弹性竞价。多 Agent 协作不是一蹴而就的蓝图,而是持续演化的结果。


8. 与前后系列的衔接(详细映射)

本文作为系列转折点,大量复用了系列四单 Agent 全栈实战的能力,并为后续多 Agent 专题奠定基础。以下详细说明技术依赖与展望。

8.1 前接系列四:单 Agent 能力的模块化复用

系列四内容本文多 Agent 应用复用与扩展方式
第 1-3 篇 Prompt 工程每个 Agent 的 @SystemMessage 设计、Few-Shot 示例用于 Master 路由层级式中每个 Specialist 的 Prompt 独立编写,Master 的路由指令使用结构化输出约束 (JSON mode)
第 4 篇 记忆系统全局用户画像 (GlobalMemoryService) 是长期记忆的多 Agent 扩展从“单 Agent 的偏好存储”升级为“多 Agent 共享知识库”,增加乐观锁与写入通知机制
第 5 篇 工具生态Specialist Agent 的工具集租户隔离,ToolRouter 在 Agent 内部继续生效工具注册中心扩展出 Agent 维度的命名空间,通过 ToolRegistry.getForAgent(type) 实现隔离
第 6 篇 规划系统TaskDecomposer 直接复用 Plan-Solve 生成子任务 DAG,Reflexion 用于动态重规划规划能力从单个 Agent 内部逻辑,提升为跨 Agent 的编排器,支持部分失败后的全局重规划
第 7 篇 安全机制层级式中的权限隔离(退款 Agent vs 合规 Agent)是 RBAC 在多 Agent 维度的应用不同 Agent 实例绑定不同角色和权限,通过 OPA 或 Spring Security 检查规则。对话式审查链路实现操作与审批分离的制衡机制
第 8-9 篇 可观测性与反模式新增多 Agent 监控指标:对话轮次、投标次数、死锁事件、共享 Memory 冲突率等反模式扩展为“上帝 Agent”、“流浪任务”、“无限对话”等,与单 Agent 反模式并列

关键代码复用示例

// 系列四第6篇的Plan-Solve工具类被直接用于TaskDecomposer
public class TaskDecomposer {
    private final PlanSolveService planSolveService;  // 来自系列四

    public List<SubTask> decompose(String request) {
        String plan = planSolveService.generatePlan(request, "将请求分解为子任务JSON");
        return parseAndSort(plan);
    }
}

8.2 后启本系列后续篇章

后续文章本文铺垫内容深入方向
第 2 篇 Agent 性能优化模型分级路由、子任务并行化 (CompletableFuture.allOf())将深入 Token 预算的动态调整、基于负载的模型切换、并行图执行引擎的优化
第 3 篇 GUI/Code Agent市场式协作中 Agent 能力声明 (BidStrategy 的 task matching 部分)针对特定垂直场景的 Agent 设计,会复用层级式编排与对话式审查的思路
第 5 篇 企业级平台全局 Memory、Redis 负载排名、Kafka 事件总线的多 Agent 通信上升到平台级的多租户管理、Agent 生命周期、调度中心,将三种模式作为可配置的协作策略
第 6 篇 DevOps 团队模拟层级式+对话式+流水线式混合协作理论具体实现需求分析→架构设计→编码→测试的多 Agent 端到端项目,直接应用本文的 TaskDispatcherConversationManager

学习路径建议:读者在掌握本文的三种协作模式后,建议立即阅读系列四中还未熟悉的部分(如安全机制),再进入本系列第 2 篇,因为性能优化是生产化多 Agent 系统的基石。最后通过第 6 篇的完整模拟项目融会贯通。


9. 面试高频专题

Q1: 多 Agent 协作的本质是什么?
一句话回答:本质是将分布式微服务架构的设计思想应用于 AI Agent,通过拆分单一 Agent 的职责并引入通信、协调、容错机制,以实现复杂任务的高内聚低耦合、弹性伸缩和隔离。
详细解释:传统单体 Agent 类似单体应用,所有逻辑耦合在一起,导致扩展难、故障隔离差。多 Agent 系统借鉴了服务拆分、服务发现、消息总线、负载均衡等概念。例如,层级式协作中 Master Agent 类似 API Gateway,负责路由与聚合;市场式协作中的 AuctionManager 实现了类似客户端负载均衡的功能,通过 BidStrategy 动态选择最优执行者。在 Java 实现中,我们使用 gRPC 进行同步服务间调用(类比 HTTP/RPC),Kafka 进行异步消息传递(类比 Event Bus),Redis 作为共享状态存储(类比分布式缓存)。Agent 可以独立部署、独立伸缩,并通过 GlobalMemoryService 保持最终一致性。这种架构使得系统可以按业务能力(订单、退款、推荐)进行拆分,并允许不同 Agent 采用不同模型以优化成本。
多角度追问:如果某个 Specialist Agent 的接口发生变化(比如工具参数增加),如何保证多 Agent 系统不受影响?如何设计 Agent 间通信的契约?
加分回答:可以采用 Protobuf 定义 gRPC 服务契约,并使用 Schema Registry 管理 Kafka 消息格式。在 Agent 内部,为工具增加版本号,Master 通过服务发现获取 Agent 的版本并适配调用。同时,通过集成测试确保向下兼容。


Q2: 对话式协作中如何防止无限对话?
一句话回答:设置硬性的 maxTurns 轮次限制,结合语义相似度检测(连续多轮无实质进展)和共识标记([AGREE]),并在 ConversationManager 中实现超时中断。
详细解释ConversationManager 维护一个计数器 turnCount,每次两个 Agent 交互后递增。一旦超过 maxTurns(如 10),无论是否达成共识都强制终止,并返回当前结果。为防止虚假共识(双方绕圈),还加入了语义相似度检测:将每轮对话内容向量化,计算最近 3 轮的余弦相似度,若平均值超过阈值 0.9,则认为讨论无进展,强制终止。同时,Agent 的 System Prompt 中明确要求:“如果同意对方方案,请回复 [AGREE]”,这样 ConversationManager 可以快速识别共识。此外,全流程通过 CompletableFuture.orTimeout 设置总超时(如 30 秒),避免某个 Agent 卡死。
多角度追问:如果对话因为工具调用失败而陷入僵局,超时后如何保证用户得到有用信息而非空回复?
加分回答:在 ConversationManager 中实现降级总结:超时或终止时,收集最近的 UserProxyAgent 执行结果和 AssistantAgent 的最终建议,调用一个小型快速模型(如 GPT-4o-mini)进行一句话摘要,作为兜底回复。同时,记录详细日志用于后续人工介入。


Q3: 层级式协作中 Master 宕机,应如何设计故障转移机制以避免全系统瘫痪?
一句话回答:通过主备 Master 选举、任务状态持久化到 Redis,以及网关层重试与幂等设计,实现高可用。
详细解释:Master Agent 并非无状态服务,因为它持有正在执行的任务上下文。我们可以采用主备模式:多个 Master 实例通过 Redis 的 SETNX 实现领导者选举,Leader 负责分派与聚合,Follower 监视 Leader 心跳。当 Leader 宕机,Follower 通过 Redis 获取未完成的任务状态(包含子任务 DAG 和已完成的部分),接续执行。网关(如 Spring Cloud Gateway)在调用 Master 失败时自动切换到备用实例,并使用请求 ID 实现幂等,防止重复创建任务。同时,TaskDispatcher 中对每个子任务的结果都持久化到 Redis,Master 重启后可无缝恢复。
多角度追问:如果在任务聚合阶段 Master 宕机,新 Leader 如何判断哪些子任务已完成?
加分回答:所有子任务执行结果均写入 Redis,且状态为 COMPLETED。新 Master 读取任务 DAG 中所有节点的状态,跳过已完成节点,只重新分派未完成或超时的节点。通过分布式锁防止重复分派。为了加速恢复,可以使用 Redis Stream 实现任务状态的变更通知,新 Master 快速重建上下文。


Q4: 市场式协作中如何防止恶意 Agent 投标(如虚假低延迟)?
一句话回答:引入信誉系统,根据 Agent 的历史任务完成质量、实际延迟与投标延迟的偏差来动态调整其投标权重,并在 BidStrategy 中增加惩罚因子。
详细解释:每个 Agent 都有一个信誉分 reputationScore,初始为 1.0。任务完成后,AuctionManager 记录实际执行时间、是否成功、是否符合投标承诺。若实际延迟远大于投标值,或置信度虚高,则降低其信誉分。在 WeightedBidStrategy 的评分函数中加入信誉因子:score += wReputation * reputationScore。信誉分通过 Redis 持久化,并定期衰减(鼓励持续良好表现)。对于新加入的 Agent,采用“沙盒”模式:前 100 个任务不参与竞价,而是影子运行以积累信誉数据。
多角度追问:如果整个集群的 Agent 都变得“不诚实”,信誉系统是否会导致没有中标者?
加分回答:设置一个最小信誉阈值(如 0.5),当所有投标者信誉都低于该阈值时,策略退化为单纯选择负载最低的 Agent(类似最少连接算法),并触发告警人工介入。同时,限制信誉评分的更新幅度,避免个别恶意投标影响整体可用性。


Q5: 共享 Memory 中如何使用乐观锁避免信息孤岛?
一句话回答:通过 Redis 的 WATCH + MULTI + EXEC 实现基于版本号的乐观锁,确保并发更新用户画像时的一致性,并强制 Specialist 在完成任务后将关键发现写入全局 Memory。
详细解释GlobalMemoryService 中,每个 UserProfile 对象包含一个 version 字段。读取时获取当前版本,修改后递增版本号并执行事务写入。事务开始时 WATCH 该 key,如果其他 Agent 已经修改了版本,则当前事务 EXEC 失败,代码捕获后重试(最多 3 次)。为了避免信息孤岛,强制在 Specialist 的 AOP 切面中调用 recordFinding(userId, finding),将任务中获取的关键信息(如“用户是 VIP”、“本次退款涉及生鲜商品”)写入全局 Memory。这样 Master Agent 在聚合阶段就能看到所有 Specialist 留下的共享信息,而不是各自为战。
多角度追问:如果全局 Memory 写入频繁导致大量重试,如何优化?
加分回答:将用户画像拆分热区字段与冷区字段,使用不同的 key,降低锁竞争。或者采用 Redis Lua 脚本实现原子化的“追加”操作,而非整体替换。对于高并发场景,可以考虑引入分布式锁(Redisson)作为乐观锁的补充,但要注意死锁风险。


Q6: 多 Agent 系统如何控制 Token 成本?
一句话回答:模型分级路由(大小模型混用)、子任务并行化减少总调用次数、以及全局 Token 预算硬限制,三位一体控制成本。
详细解释:在创建 Agent 时,根据其任务复杂度注入不同模型。例如 MasterAgentComplianceAgent 使用 GPT-4o,而 GeneralAgent 使用 GPT-4o-mini。这通过 AiServices.create(interface, model) 实现。无依赖的子任务利用 CompletableFuture.allOf() 并行执行,使得总延迟等于最慢子任务,而非累加,间接减少了因等待而需的额外上下文传递 Token。全局 TokenBudgetManager 通过 AOP 拦截所有 LLM 调用,维护一次用户请求的总 Token 消耗,若接近上限(如 50K),则强制 Agent 压缩回复或触发降级(使用缓存答案)。
多角度追问:如果 GPT-4o-mini 在某次任务中表现极差导致多次重试,成本反而更高,怎么办?
加分回答:实现自适应模型升级:当某个子任务失败或置信度低于阈值时,自动用更强的模型重试。通过 ModelRouter 封装该逻辑,并记录每次升级事件,以便后续优化初始模型分配策略。


Q7: 对话式协作中中介者模式是如何应用的?
一句话回答ConversationManager 充当中介者,协调 UserProxyAgentAssistantAgent 之间的消息传递,避免两个 Agent 直接耦合,便于独立扩展和修改。
详细解释:在 GoF 中介者模式中,对象之间通过一个中介者进行通信,而不是直接引用。在这里,UserProxyAgentAssistantAgent 彼此不知道对方的存在,它们只与 ConversationManager 交互。Manager 负责将一方的输出转换为另一方的输入,加入收敛检测、轮次控制等横切逻辑。这使得我们可以单独调整任一 Agent 的 System Prompt 或替换其实现(例如从 LLM 变为基于规则),而不影响另一方。
多角度追问:如果以后需要引入第三个 Agent(如仲裁者)参与对话,中介者模式如何适应?
加分回答:将 ConversationManager 设计为支持多参与者:维护一个 Agent 列表和发言顺序,轮流调用它们的 chat 方法。对话收敛条件改为所有活跃 Agent 均达成共识或超过总轮次。这体现了中介者模式易于扩展的特性。


Q8: 如何检测并处理层级式协作中的循环依赖?
一句话回答:在 TaskDecomposer 生成子任务 DAG 后,使用 Kahn 算法进行拓扑排序,若无法输出所有节点则说明存在环,立即拒绝执行并触发 Master 重新规划。
详细解释hasCycle(tasks) 方法计算每个子任务的入度,将入度为 0 的节点加入队列,依次移除并减少邻居入度。如果最终处理的节点数少于总节点数,说明存在环。检测到环后,抛出 CircularDependencyException,Master 捕获后调用 LLM 请求重新生成无环的子任务计划,并附加历史错误信息作为 Few-Shot。如果重试 3 次仍失败,则回复用户“当前请求过于复杂,需要转接人工”。运行时也可能出现死锁(因超时或阻塞),通过对 CompletableFuture 使用 orTimeout(30, SECONDS) 中断,并记录事件。
多角度追问:如果某个子任务执行时间超长,但并非死锁,超时策略会导致误终止,怎么办?
加分回答:为超时任务增加“软超时”机制:超时后不立即失败,而是向用户提示“正在处理中”,并启动异步补偿任务。如果最终完成,则将结果追加给用户。对于 Master 来说,使用 completeOnTimeout 代替 orTimeout,先返回部分结果,并设置回调处理最终完成事件。


Q9: 市场式协作与传统的负载均衡有何异同?
一句话回答:两者都是将请求分发到多个处理者以优化资源利用,但市场式引入经济模型,让处理者主动报价(成本、延迟、置信度),调度者根据多维目标决策,更适合异构、自治的 Agent 环境。
详细解释:传统负载均衡(如 Ribbon、Nginx)通常基于连接数、轮询或最少连接等简单指标,且后端服务是同质的。市场式协作中,Agent 可以是异构的(不同模型、不同知识库),并能根据自身状态动态调整报价。AuctionManagerBidStrategy 可包含成本、延迟、成功率、信誉等多种权重,类似于加权轮询的增强版。此外,Agent 可以拒绝投标(因过载或熔断),这是传统负载均衡中不常见的自保护行为。在实现上,它更类似于服务网格中的客户端智能路由(如 Istio 的 Locality Load Balancing),但决策权部分下放给 Agent。
多角度追问:如果多个 Agent 报价相同,AuctionManager 如何选择?
加分回答:策略中可以设置二级排序:报价相同则选择信誉分最高的;若还相同则随机选择(增加公平性)。也可以通过 Redis 实现分布式锁确保同一任务只被一个 Agent 执行。为防止“讨价还价”导致延迟,限制投标轮次为 1 次。


Q10: 什么是多 Agent 的信息孤岛?如何解决?
一句话回答:信息孤岛指不同 Agent 持有不一致的用户上下文,导致行为矛盾(如一个 Agent 知道用户是 VIP,另一个不知道)。解决方法是建立全局共享 Memory,并通过强制写入和乐观锁保证一致性。
详细解释:在多 Agent 系统中,每个 Agent 拥有独立的 ChatMemory,如果订单 Agent 识别出用户是 VIP,但退款 Agent 不知道,它可能拒绝为该 VIP 提供优先退款服务。这违背了业务规则。为此,我们设计 GlobalMemoryService,用 Redis 存储用户画像和会话状态。每次 Agent 任务开始前,从全局 Memory 读取最新画像注入私有 Memory;任务结束后,通过 AOP 切面将新发现的关键信息写回全局 Memory。写操作使用乐观锁处理并发冲突。此外,还可以通过 Kafka 发布用户画像变更事件,让关心这些事件的 Agent 异步刷新本地缓存。
多角度追问:如果写入全局 Memory 的操作延迟过大,是否会导致 Agent 基于过期信息决策?
加分回答:对于实时性要求极高的信息(如用户当前情绪),可以在 Agent 间通过 gRPC 传递上下文而非等待全局 Memory 同步。采用“读最新、写异步”的最终一致性模型,并设置版本过期时间,如果本地缓存版本落后太久,则强制从 Redis 刷新。


Q11: 多 Agent 系统中如何实现容错?
一句话回答:组合使用重试、超时、断路器、降级和任务重分派,将 Resilience4j 和 CompletableFuture 的异常处理融入 Agent 协作流程。
详细解释:每个 gRPC 调用都通过 withDeadlineAfter 设置超时,Kafka 消费者使用 CircuitBreaker 装饰。TaskDispatcher 中子任务失败后,首先重试 1 次(仅幂等操作),若仍失败则标记为 FAILEDResultAggregator 根据任务优先级决定是否降级:关键任务失败导致整个请求降级返回兜底文案;非关键任务跳过。对话式协作中,超时由 CompletableFuture.orTimeout 处理,并调用 ConversationManager 的降级总结。市场式协作中,中标 Agent 失败后,AuctionManager 立即从剩余投标中选择次优 Agent 重新执行。
多角度追问:如果降级策略过于频繁触发,导致用户体验持续下降,应如何优化?
加分回答:需要监控降级触发率,并为高失败率的 Agent 设置自动熔断(例如断路器从 CLOSED 变为 OPEN)。当断路器打开时,该 Agent 暂时从服务池中剔除,直到冷却时间过后重新尝试。同时,可以通过 Master 动态增加新 Agent 实例或减少不可用 Agent 的任务分配。


Q12: 三种模式是否可以混合使用?举例说明。
一句话回答:可以,且生产环境推荐混合架构。比如电商客服系统中,Master 采用层级式调度 Specialist,退款 Agent 与合规 Agent 之间采用对话式审查,大促期间对通用咨询采用市场式竞价。
详细解释:混合架构能够取长补短。层级式作为主骨架,提供了清晰的任务分解和管控;对话式解决了需要谈判和审查的模糊场景;市场式则在峰值时提供了弹性。具体实现上,我们可以在 TaskDispatcher 中判断子任务类型:若为 COMPLIANCE_REVIEW,则启动 ConversationManager 进行对话;若为 GENERAL_FAQ 且系统处于峰值模式,则交由 AuctionManager。Master 的 TaskDecomposer 在生成 DAG 时就可以标记协作模式,实现精细化编排。
多角度追问:混合模式下,系统的可观测性如何统一?
加分回答:所有 Agent 的交互都通过统一的 Span Context 传递 Trace ID(OpenTelemetry)。对话式协作、层级式调用、市场竞价都被建模为 Span 嵌套关系,可以在 Jaeger 中看到完整的调用链。集中收集指标(如轮次、投标次数、降级次数)到 Prometheus,通过 Grafana 仪表盘统一展示。


Q13: 在多 Agent 系统中如何实现可观测性?
一句话回答:通过 OpenTelemetry 自动埋点结合自定义指标,将 Agent 对话、工具调用、子任务执行、投标等操作作为 Span 上报,利用 Langfuse 或 Jaeger 进行追踪,并建立关键 SLA 指标监控。
详细解释:在 Java 实现中,可以为 AiService 创建自定义 AOP 切面,在 @Around 中创建 Span,记录模型名称、Token 使用量、对话轮次、工具调用结果。对于 TaskDispatcher,每个子任务分派创建一个子 Span,附着到 Master 的 Span 下。市场式竞价的 AuctionManager 广播和投标也作为独立事件记录。指标方面,通过 Micrometer 暴露:agent.turn.countagent.token.consumedtask.success.rateauction.bid.latency 等。死锁检测事件、断路器状态变更都通过 Event 发布并记录。告警规则:当 P95 延迟超过阈值或失败率上升时,触发 PagerDuty 通知。
多角度追问:如果在高负载下,OpenTelemetry 采样导致部分 Trace 丢失,如何保证问题排查?
加分回答:采用尾部采样策略,保留所有错误和慢请求的 Trace。并将关键业务事件(如退款审查失败)以日志形式记录到 ELK,保证即使 Trace 丢失,也能通过日志关联定位。


Q14: 系统设计题
题目:设计一个电商大促场景下的多 Agent 客服系统,满足:

  1. 日常由层级式协作处理复杂客服请求(订单、退款、导购)。
  2. 大促峰值流量自动启用市场式竞价调度同质化通用 Agent 扩容。
  3. 退款类操作必须经过独立合规 Agent 审查(对话式协作)。
  4. 所有 Agent 共享用户画像且状态实时一致。

请画出系统架构图、一个大促期间用户请求从路由到完成的完整时序图,分析当市场式竞价导致某 Agent 超载时,系统如何通过反压机制拒绝新任务并触发告警,并给出各 Agent 的模型选择策略和成本估算。

系统架构图(抽象)

flowchart TB
    Gateway["API Gateway"] -->|"路由"| Master["Master Agent (GPT-4o)"]
    Master --> Decomposer["TaskDecomposer"]
    Decomposer --> Dispatcher["TaskDispatcher"]
    Dispatcher -->|"gRPC 同步"| Order["订单 Agent (GPT-4o)"]
    Dispatcher -->|"gRPC 同步"| Refund["退款 Agent (GPT-4o)"]
    Refund <-->|"对话"| Compliance["合规 Agent (GPT-4o)"]
    Dispatcher -->|"Kafka 异步"| AuctionManager["AuctionManager"]
    AuctionManager --> Gen1["通用 Agent (GPT-4o-mini)"]
    AuctionManager --> Gen2["通用 Agent (GPT-4o-mini)"]
    AuctionManager --> Gen3["通用 Agent (GPT-4o-mini)"]
    Master --> GlobalMemory["Global Memory (Redis+Milvus)"]
    Order --> GlobalMemory
    Refund --> GlobalMemory
    Compliance --> GlobalMemory
    Gen1 --> GlobalMemory

    %% 样式类定义(莫兰迪低饱和色系)
    classDef default fill:#f1f5f9,stroke:#334155,stroke-width:1.5px,color:#1e293b
    classDef gateway fill:#d1fae5,stroke:#10b981,stroke-width:1.5px,color:#065f46
    classDef master fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#92400e
    classDef decomposer fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#1e3a8a
    classDef dispatcher fill:#fce4ec,stroke:#f472b6,stroke-width:1.5px,color:#9d174d
    classDef agent fill:#ede9fe,stroke:#8b5cf6,stroke-width:1.5px,color:#4c1d95
    classDef compliance fill:#ffe4e6,stroke:#f43f5e,stroke-width:1.5px,color:#9f1239
    classDef auction fill:#cffafe,stroke:#06b6d4,stroke-width:1.5px,color:#155e75
    classDef general fill:#e0e8f0,stroke:#64748b,stroke-width:1.5px,color:#0f172a
    classDef memory fill:#dcfce7,stroke:#22c55e,stroke-width:1.5px,color:#14532d

    %% 节点应用样式
    class Gateway gateway
    class Master master
    class Decomposer decomposer
    class Dispatcher dispatcher
    class Order,Refund agent
    class Compliance compliance
    class AuctionManager auction
    class Gen1,Gen2,Gen3 general
    class GlobalMemory memory

时序图(大促退款+咨询混合请求)

sequenceDiagram
    participant User
    participant GW as Gateway
    participant Master
    participant Dispatcher
    participant OrderSp
    participant RefundSp
    participant Compliance
    participant AuctionMgr
    participant GenSp
    User->>GW: "退款手机并问同价位替代品"
    GW->>Master: 请求
    Master->>Master: 分解任务:查订单,退款项,推荐
    Master->>Dispatcher: 分派订单(ORDER) gRPC
    Dispatcher->>OrderSp: 执行
    OrderSp-->>Dispatcher: 订单详情
    Master->>Dispatcher: 分派退款(REFUND) gRPC
    Dispatcher->>RefundSp: 生成建议
    RefundSp->>Compliance: 对话式审查
    Compliance-->>RefundSp: APPROVED
    RefundSp-->>Dispatcher: 退款方案
    Master->>Dispatcher: 分派推荐(RECOMMEND) Kafka(异步)
    Dispatcher->>AuctionMgr: 广播推荐任务
    AuctionMgr->>GenSp: 中标Agent执行
    GenSp-->>AuctionMgr: 推荐列表
    AuctionMgr-->>Master: 结果
    Master->>Master: 聚合结果
    Master-->>User: 退款结果+推荐

反压机制设计

  • Agent 负载上报:通用 Agent 每秒将当前队列深度和 CPU 使用率上报到 Redis Sorted Set,key=agent:load,score=queueDepth/maxCapacity。
  • 投标门槛:当 Agent 自身负载 > 80% 或断路器打开时,不再订阅拍卖广播或直接忽略。
  • AuctionManager 反压:在拍卖前查询 agent:load,若所有 Agent 负载均 > 80%,则停止广播,返回 BACK_PRESSURE 状态给 Master。
  • Master 降级:收到反压后,对于非关键推荐任务,回复预设的静态推荐列表(“热门手机”),并返回 HTTP 429 给网关,提示用户稍后。
  • 自动扩容:K8s HPA 监控 agent_queue_depth 自定义指标,当平均深度 > 5 时,增加 Pod 数量,冷却时间 3 分钟。
  • 告警:当连续 1 分钟反压事件超过阈值,触发 PagerDuty 告警,通知 SRE 手动检查。

模型选择策略

  • Master、合规 Agent:任务复杂、推理要求高,使用 GPT-4o
  • 订单、退款 Agent:涉及业务逻辑和数据查询,使用 GPT-4o(也可部分降级为 GPT-4o-mini 若准确率可接受)。
  • 通用 Agent(推荐、FAQ):简单且高吞吐,使用 GPT-4o-mini,成本仅 GPT-4o 的 1/10,延迟更低。

成本估算
假设日常 10,000 次请求,大促 100,000 次请求。

  • 日常:复杂请求占比 30% (3,000次),平均 Token 12K,GPT-4o 成本 0.03/1Ktokens;简单请求7,000次,平均Token5KGPT4omini0.03/1K tokens;简单请求 7,000次,平均 Token 5K,GPT-4o-mini 0.003/1K tokens。
    日成本 = 3000*(120.03) + 7000(5*0.003) = 108+108 + 105 = $213
  • 大促:复杂请求仍 3,000 次,简单请求膨胀至 97,000 次,通用 Agent 使用 GPT-4o-mini,成本 = 3000*(120.03) + 97000(5*0.003) = 108+108 + 1455 = $1,563

通过市场式竞价避免了为峰值部署大量高价 GPT-4o 实例,成本可控。若全部采用 GPT-4o,大促成本将达 $3,000+。

加分回答:可进一步引入流量预测,大促前手动扩容通用 Agent 池,降低冷启动影响;并设置全局每日 Token 预算上限,超限后自动降级为缓存静态回复,防止成本失控。

多 Agent 协作架构速查表

模式适用场景核心组件关键挑战关联系列
对话式创意碰撞、模糊决策、需要双方协商ConversationManager, UserProxyAgent, AssistantAgent无限对话、收敛性系列四第8篇个人知识助手
层级式结构化任务分解、强管控、权限隔离MasterAgent, TaskDecomposer, TaskDispatcher, ResultAggregatorMaster单点、子任务失败降级系列四第6篇规划系统
市场式大规模同质化 Agent、动态负载均衡、成本最优AuctionManager, BidStrategy, 负载排名投标延迟、恶意投标系列二服务发现

延伸阅读

  • 《AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation》
  • CrewAI 官方文档
  • Microsoft Magentic-One 系统架构
  • 《Designing Data-Intensive Applications》第3-4章(分布式系统的通信与协调模式)
  • Spring Cloud Stream 官方文档