AI Agent 开发实战(十一):Multi-Agent 协作编排
单个 Agent 再强,也只能干一件事。真正复杂的问题,需要多个 Agent 协作完成。今天聊 Multi-Agent 的编排模式:谁当领导、谁干活、谁拍板、出了错怎么兜底。读完这篇,你能在自己的项目里设计出靠谱的多 Agent 协作方案。
一、为什么需要 Multi-Agent?
单个 Agent 的局限:
markdown
单 Agent 瓶颈
│
├── 1. 上下文窗口有限
│ └── 塞太多指令 → 遵循率下降
│
├── 2. 工具集臃肿
│ └── 100 个工具选哪个?选择成本高
│
├── 3. 角色混乱
│ └── 让一个 Agent 同时写代码 + 做测试 + 写文档 → 质量全拉
│
└── 4. 容错差
└── 一个环节出错,整个链路崩
Multi-Agent 的核心思想:拆职责、专精化、可组合。
Multi-Agent 优势
│
├── 职责单一 → 每个 Agent 只做一件事,Prompt 更精准
├── 工具隔离 → 每个 Agent 只访问自己需要的工具
├── 并行执行 → 无依赖的步骤可以同时跑
├── 容错兜底 → 单个 Agent 失败不影响整体
└── 可测试性 → 每个 Agent 可以独立测试和优化
二、四种经典编排模式
2.1 顺序链(Pipeline)
最简单的模式:Agent A 的输出作为 Agent B 的输入,逐级传递。
css
顺序链
│
├── 输入 → [翻译Agent] → [润色Agent] → [排版Agent] → 输出
│
├── 特点
│ ├── 简单可靠
│ ├── 每步只依赖上一步
│ └── 延迟 = 各步之和
│
└── 适用场景
├── 内容生产流水线
└── 数据处理管道
代码示例:
java
public class PipelineOrchestrator {
private final List<Agent> agents;
public String execute(String input) {
String current = input;
for (Agent agent : agents) {
current = agent.run(current);
if (current == null) {
throw new AgentException(
"Agent [" + agent.getName() + "] returned null");
}
}
return current;
}
}
2.2 分发-聚合(Fan-out / Fan-in)
一个协调者把任务分给多个专业 Agent,各自独立完成后汇总。
css
分发-聚合
│
├── 协调者
│ ├── → [代码审查Agent]
│ ├── → [安全扫描Agent]
│ ├── → [性能分析Agent]
│ └── → [风格检查Agent]
│
├── 各 Agent 并行执行
│
├── 协调者聚合结果
│ ├── 合并各 Agent 报告
│ ├── 去重和冲突处理
│ └── 生成最终报告
│
└── 适用场景
├── 代码审查
├── 多维度分析
└── 并行信息收集
代码示例:
java
public class FanOutOrchestrator {
private final List<Agent> workers;
private final Agent aggregator;
public String execute(String input) {
// 1. 并行分发给所有 Worker
List<CompletableFuture<String>> futures = workers.stream()
.map(worker -> CompletableFuture.supplyAsync(
() -> worker.run(input)))
.toList();
// 2. 等待所有 Worker 完成
List<String> results = futures.stream()
.map(CompletableFuture::join)
.toList();
// 3. 聚合结果
String combinedInput = String.join("\n---\n", results);
return aggregator.run(combinedInput);
}
}
2.3 路由模式(Router)
一个路由 Agent 根据输入判断该交给谁处理,类似微服务网关。
css
路由模式
│
├── 输入 → [路由Agent]
│ │
│ ├── 技术问题 → [技术支持Agent]
│ ├── 订单相关 → [订单处理Agent]
│ ├── 投诉建议 → [客服Agent]
│ └── 无法判断 → [通用Agent]
│
├── 特点
│ ├── 动态选择,按需分发
│ ├── 路由 Agent 的判断力是关键
│ └── 可以多级路由
│
└── 适用场景
├── 客服系统
├── 智能工单分配
└── 多技能助手
代码示例:
java
public class RouterOrchestrator {
private final Agent router;
private final Map<String, Agent> routeTable;
public String execute(String input) {
// 1. 路由 Agent 决定走哪条路
String route = router.run(input);
// 2. 查表获取目标 Agent
Agent target = routeTable.get(route);
if (target == null) {
target = routeTable.get("default");
}
// 3. 执行目标 Agent
return target.run(input);
}
}
2.4 辩论-裁决(Debate / Jury)
多个 Agent 对同一问题给出不同方案,由一个裁决 Agent 或投票机制选最优。
css
辩论-裁决
│
├── 输入 → [方案A Agent] → 方案 A
│ → [方案B Agent] → 方案 B
│ → [方案C Agent] → 方案 C
│
├── [裁决Agent] 评估各方案
│ ├── 打分
│ ├── 指出各方案优劣
│ └── 选出最佳或合并
│
├── 特点
│ ├── 提高决策质量
│ ├── 适合开放性、创造性问题
│ └── 成本高(多次 LLM 调用)
│
└── 适用场景
├── 方案评审
├── 代码架构设计
└── 创意生成
三、编排模式对比
| 维度 | 顺序链 | 分发-聚合 | 路由 | 辩论-裁决 |
|---|---|---|---|---|
| 复杂度 | 低 | 中 | 中 | 高 |
| 延迟 | 高(串行) | 低(并行) | 低 | 高(多轮) |
| 成本 | 1x | Nx | 1-2x | Nx+1 |
| 容错 | 差(链式) | 好(隔离) | 中 | 好 |
| 适用 | 流水线 | 多维度 | 分类 | 决策 |
| 关键风险 | 单点故障 | 聚合冲突 | 路由误判 | 成本失控 |
四、状态管理:Agent 间怎么传数据?
Multi-Agent 最大的坑不是编排,而是状态传递。
4.1 三种状态传递方式
css
状态传递方式
│
├── 1. 直接传参(最简单)
│ ├── Agent A 的输出 → Agent B 的输入
│ ├── 适合简单字符串传递
│ └── 问题:结构复杂时容易丢信息
│
├── 2. 共享上下文(最常用)
│ ├── 所有 Agent 读写同一个 Context 对象
│ ├── 类似黑板模式(Blackboard Pattern)
│ └── 问题:并发写入、覆盖冲突
│
└── 3. 消息总线(最解耦)
├── Agent 通过消息队列通信
├── 发布-订阅模式
└── 问题:实现复杂、调试难
4.2 共享上下文实现
java
public class AgentContext {
private final Map<String, Object> data = new ConcurrentHashMap<>();
private final List<String> executionLog = new CopyOnWriteArrayList<>();
// 存数据
public void put(String key, Object value) {
data.put(key, value);
executionLog.add(
Thread.currentThread().getName() + " set " + key);
}
// 取数据
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> type) {
return (T) data.get(key);
}
// 获取执行日志(调试用)
public List<String> getExecutionLog() {
return Collections.unmodifiableList(executionLog);
}
}
4.3 上下文设计原则
| 原则 | 说明 | 反例 |
|---|---|---|
| 命名空间隔离 | 各 Agent 用前缀区分 key | 两个 Agent 都写 result |
| 只追加不覆盖 | 用 list 而非单值 | 后写的覆盖先写的 |
| 不可变传递 | 传深拷贝而非引用 | Agent A 修改了 Agent B 正在读的对象 |
| 记录来源 | 每个 key 标记写入者 | 不知道数据谁写的 |
五、错误处理:一个 Agent 挂了怎么办?
5.1 三级容错策略
yaml
容错策略
│
├── Level 1: 重试
│ ├── 对失败 Agent 自动重试(最多 N 次)
│ ├── 适合:LLM 调用超时、网络抖动
│ └── 不适合:逻辑错误、参数错误
│
├── Level 2: 降级
│ ├── 主 Agent 失败 → 切换到备选 Agent
│ ├── 复杂 Agent 失败 → 切换到简单版本
│ └── 适合:非核心路径
│
└── Level 3: 隔离
├── 失败 Agent 的结果标记为 error
├── 其他 Agent 继续执行
└── 适合:分发-聚合模式
5.2 容错代码示例
java
public class ResilientOrchestrator {
private static final int MAX_RETRY = 2;
public String executeWithRetry(Agent agent, String input) {
Exception lastError = null;
for (int i = 0; i <= MAX_RETRY; i++) {
try {
return agent.run(input);
} catch (AgentException e) {
lastError = e;
log.warn("Agent [{}] attempt {} failed: {}",
agent.getName(), i + 1, e.getMessage());
}
}
// 重试耗尽,走降级
Agent fallback = agent.getFallback();
if (fallback != null) {
log.warn("Falling back to [{}] for input",
fallback.getName());
return fallback.run(input);
}
throw new AgentException("All retries exhausted", lastError);
}
}
六、实战:代码审查 Multi-Agent 系统
把四种模式组合起来,设计一个完整的代码审查系统。
6.1 系统架构
css
代码审查 Multi-Agent 架构
│
├── [路由Agent] 判断变更类型
│ ├── 前端变更 → [前端审查组]
│ │ ├── [样式检查Agent]
│ │ ├── [组件规范Agent]
│ │ └── [可访问性Agent]
│ │
│ ├── 后端变更 → [后端审查组]
│ │ ├── [代码规范Agent]
│ │ ├── [安全扫描Agent]
│ │ └── [性能分析Agent]
│ │
│ └── 全栈变更 → 两组都执行
│
├── [聚合Agent] 汇总各组结果
│ ├── 去重
│ ├── 按严重度排序
│ └── 生成审查报告
│
└── [辩论Agent](可选)
└── 对冲突意见进行裁决
6.2 核心实现
java
public class CodeReviewOrchestrator {
private final Agent router;
private final Map<String, List<Agent>> reviewGroups;
private final Agent aggregator;
public ReviewResult review(String codeDiff) {
// 1. 路由:判断走哪个审查组
String group = router.run(codeDiff);
// 2. 分发:并行执行对应审查组
List<Agent> agents = reviewGroups.getOrDefault(group,
reviewGroups.get("backend")); // 默认后端审查
List<CompletableFuture<ReviewResult>> futures =
agents.stream()
.map(agent -> CompletableFuture.supplyAsync(
() -> {
try {
return agent.run(codeDiff);
} catch (AgentException e) {
return ReviewResult.error(
agent.getName(), e);
}
}))
.toList();
// 3. 聚合:汇总所有结果
List<ReviewResult> results = futures.stream()
.map(CompletableFuture::join)
.toList();
// 4. 合并
String combined = results.stream()
.map(ReviewResult::getReport)
.collect(Collectors.joining("\n\n"));
return aggregator.run(combined);
}
}
6.3 Agent Prompt 设计示例
安全扫描 Agent 的 System Prompt:
markdown
你是一个代码安全审查专家。你的职责:
1. 检查 SQL 注入风险
2. 检查 XSS 漏洞
3. 检查硬编码密钥/密码
4. 检查不安全的反序列化
5. 检查权限绕过风险
输出格式:
- 严重级别:CRITICAL / HIGH / MEDIUM / LOW
- 问题描述
- 所在位置(行号)
- 修复建议
只报告安全问题,不关心代码风格。
七、编排框架对比
7.1 主流框架
| 框架 | 语言 | 编排方式 | 特点 |
|---|---|---|---|
| LangGraph | Python | 图(有向无环图) | 灵活、可循环、有状态 |
| CrewAI | Python | 角色协作 | 上手快、角色定义直观 |
| AutoGen | Python | 对话驱动 | Agent 间自然语言交流 |
| Spring AI | Java | 编程式 | 企业级、与 Spring 生态融合 |
| Semantic Kernel | C# | 编程式 | 微软生态、插件化 |
7.2 Spring AI 实现 Multi-Agent
java
@Configuration
public class MultiAgentConfig {
@Bean
public Agent codeReviewAgent(
ChatClient chatClient,
List<ToolCallback> tools) {
return Agent.builder()
.name("code-reviewer")
.chatClient(chatClient)
.systemPrompt("""
你是代码审查专家,专注于安全和规范。
严格按照审查清单逐项检查。
""")
.tools(tools)
.build();
}
@Bean
public Agent securityAgent(ChatClient chatClient) {
return Agent.builder()
.name("security-scanner")
.chatClient(chatClient)
.systemPrompt("""
你是安全扫描专家,只关注安全漏洞。
""")
.build();
}
@Bean
public Agent aggregatorAgent(ChatClient chatClient) {
return Agent.builder()
.name("aggregator")
.chatClient(chatClient)
.systemPrompt("""
你是审查报告聚合器。
合并多份审查报告,去重,按严重度排序。
""")
.build();
}
}
八、踩坑实录
坑 1:Agent 间上下文泄露
css
问题:Agent A 的中间状态泄露到 Agent B 的 Prompt
原因:共享上下文对象,未做命名空间隔离
修复:
├── 每个 Agent 读写自己的 namespace
├── 敏感中间态不写入共享上下文
└── 聚合前做数据脱敏
坑 2:无限递归
arduino
问题:路由 Agent 把请求路由给自己 → 无限循环
原因:路由表配置错误
修复:
├── 设置最大路由深度(如 3 层)
├── 路由历史写入 Context,检测循环
└── 无法路由时强制走 default
坑 3:聚合结果矛盾
arduino
问题:安全 Agent 说"拒绝",性能 Agent 说"通过"
原因:各 Agent 优化目标不同
修复:
├── 明确优先级:安全 > 正确 > 性能 > 风格
├── 聚合 Agent 按优先级裁决
└── 冲突时标记为"需人工确认"
坑 4:成本爆炸
问题:4 个 Agent 并行审查 × 3 轮辩论 = 12 次 LLM 调用
原因:没有成本预算机制
修复:
├── 设置 Token 上限
├── 非核心路径用小模型
├── 缓存相同输入的结果
└── 辩论最多 2 轮,避免无限讨论
九、设计 Checklist
| # | 检查项 | 说明 |
|---|---|---|
| 1 | Agent 职责是否单一? | 一个 Agent 只做一件事 |
| 2 | 工具集是否最小化? | 只给必要的工具 |
| 3 | 状态传递是否安全? | 命名空间隔离、不可变 |
| 4 | 容错策略是否到位? | 重试 + 降级 + 隔离 |
| 5 | 路由是否可观测? | 记录路由决策和原因 |
| 6 | 成本是否可控? | Token 上限 + 缓存 |
| 7 | 是否有兜底? | 失败时的 default 行为 |
| 8 | 调试是否方便? | 执行日志 + 状态快照 |
十、面试速答模板
Q:Multi-Agent 和单 Agent 有什么区别?
核心区别是职责拆分和协作。单 Agent 所有能力塞一个 Prompt,工具多选择难、上下文容易乱。Multi-Agent 每个只做一件事,Prompt 更精准、工具更聚焦、容错更好,但引入了编排和状态传递的复杂度。
Q:Multi-Agent 编排有哪些模式?
四种经典模式:顺序链(Pipeline,串行传递)、分发-聚合(Fan-out/Fan-in,并行执行后汇总)、路由(Router,按类型分发)、辩论-裁决(Debate,多方案选最优)。实际项目通常是组合使用。
Q:Agent 间状态怎么传?
三种方式:直接传参(最简单,适合字符串)、共享上下文(最常用,类似黑板模式)、消息总线(最解耦,发布订阅)。关键原则:命名空间隔离、只追加不覆盖、不可变传递、记录来源。
下一篇我们聊 Agent 可观测性与调试:Multi-Agent 系统出了问题怎么查?执行日志怎么设计?如何给 Agent 加"黑匣子"?让每个决策都有据可查。
本文是 AI Agent 开发实战系列第 11 篇,系列目录: