多 Agent 协作实战:Supervisor 模式构建专家团队,突破单一 Agent 能力边界
在前一篇文章中,我们构建了一个融合 Memory、Tools、RAG 的完整 Agent。它能够自主决策、调用工具、检索知识,在单线程任务中表现出色。但当你面对更复杂的真实业务场景时,单一 Agent 很快会遇到瓶颈:
- 用户要求"写一个 Spring Boot 项目并生成测试用例和文档",一个 Agent 既当架构师又当程序员又当测试工程师,很容易顾此失彼;
- 上下文窗口有限,不能把所有专业知识和工具塞进同一个 Agent;
- 模型在长流程中容易"迷失方向",遗忘早期决定;
- 不同任务的最优参数差异很大:写代码需要低 temperature,头脑风暴需要高 temperature。
解决这些问题的思路,与人类组织协作的方式完全一致:拆分职责,建立专家团队,并设置一个主管进行调度。本文将详细讲解多 Agent 协作的 Supervisor(主管)模式,并通过 Spring AI 实现一个包含 Researcher、Coder、Reviewer 的专家系统。
一、单一 Agent 的能力边界
先明确:单一 Agent 不是不能做复杂任务,而是做复杂任务的失败率和成本随着任务长度指数级增加。具体瓶颈包括:
1. 上下文长度限制
模型上下文窗口有限。一个 Agent 既需要存储任务背景,又要装入大量专业技术规范、历史对话、工具返回结果,很快就会超出窗口。超出部分要么被截断,要么需要压缩,信息丢失随之而来。
2. 工具与知识冲突
让一个 Agent 同时掌握"公司内部政策"和"Java 代码生成规则"和"网站内容审核规范",这些知识的启发式规则可能互相干扰。模型可能在写代码时忽然蹦出客服话术。
3. 注意力稀释
模型需要同时关注"用户需求"、"代码正确性"、"潜在 bug"、"文档风格",导致顾此失彼。HumanEval 等基准测试显示,分步细化往往比一步到位更可靠。
4. 难以调试与迭代
如果所有逻辑都在一个 Agent 中,某一步出错会影响全局,而且很难定位是哪个环节出了问题。如果拆分为多个 Agent,每一环节都能独立测试和优化。
因此,多 Agent 协作是突破这些瓶颈的自然选择。
二、多 Agent 协作模式概览
多 Agent 系统有若干经典组织架构:
| 模式 | 结构 | 适用场景 |
|---|---|---|
| Supervisor(主管-工人) | 一个主管 Agent 负责拆解任务并分派给多个专家 Agent,最后汇总结果 | 大多数复杂任务,灵活可控 |
| Pipeline(流水线) | 多个 Agent 线性接力,前一个输出作为后一个输入 | 文档处理、数据管道 |
| Debate(辩论) | 多个 Agent 围绕同一问题给出观点并相互质疑 | 决策支持、内容审核 |
| Hierarchical(层级) | 高层主管管理中层主管,中层管理基层执行 | 超大规模任务分解 |
其中 Supervisor 模式 是最实用、最容易落地的一种。它模拟了一个项目经理 + 数个专家的团队,既能发挥每个专家的专业性,又能通过主管动态调整策略。
三、Supervisor(主管)模式的架构与设计
3.1 核心概念
Supervisor 模式包含两类 Agent:
-
Supervisor Agent:负责理解用户需求、制定计划、将子任务分派给合适的专家、接收专家成果并整合为最终回复。它不直接执行具体业务,而是做"决策与协调"。
-
Worker Agent(专家 Agent) :每个 Worker 聚焦于特定领域,如 Researcher、Coder、Reviewer。它们接收明确的任务描述,执行专业操作后返回结果。
用户请求
↓
Supervisor Agent ──路由──→ Researcher Agent → 返回研究报告
│ Coder Agent → 返回代码
│ Reviewer Agent → 返回审查意见
↓
Supervisor Agent 整合结果 → 最终回复
3.2 路由决策方式
主管如何决定将任务分派给哪个专家?有两种策略:
1. 基于规则的路由(Rule-based)
通过关键词匹配或模型意图分类,将问题映射到固定专家。例如:
- 包含"查询""搜索""研究" → Researcher
- 包含"代码""实现""修复" → Coder
- 包含"检查""审查""评价" → Reviewer
优点:简单可控、延迟低、成本低。
缺点:无法应对复杂混合请求,例如"写一个函数并帮我检查"。
2. 基于模型的路由(Model-based)
将专家列表作为工具注册给 Supervisor Agent,让模型自己决定调用哪个专家,甚至按顺序调用多个专家。这是 Spring AI 中最自然的方式,也是本文采用的方式。
ini
Supervisor Agent 的 tools = [research(), code(), review()]
模型根据用户请求自主生成工具调用序列,例如:
css
用户: "写一个快速排序,并审查我的代码。"
模型决策: code() → review()
3.3 专家 Agent 的设计原则
每个专家 Agent 应做到:
- 职责单一:只做一类事,提示词中严格限定范围;
- 上下文隔离:专家不直接访问用户主对话的历史,只接收主管传入的显式任务描述;
- 可独立测试:每个专家的能力可以单独调用、评估;
- 输出结构化:专家返回的结果尽量结构清晰,便于主管整合。
四、专家 Agent 实战设置
我们构建一个软件开发场景的专家团队:
- Researcher:负责搜索网络或知识库,收集技术资料、文档、最佳实践;
- Coder:负责编写代码,可调用代码解释器或生成代码片段;
- Reviewer:负责审查代码质量,指出潜在问题和改进建议。
下面用 Spring AI 实现这三个专家 Agent。
4.1 Researcher Agent
它需要 RAG 或网络搜索能力。我们在这里让其通过工具从向量库检索(复用前文 RAG),也可以加一个 Web 搜索工具。
arduino
@Component
public class ResearcherAgent {
private final ChatClient researcherClient;
public ResearcherAgent(ChatModel chatModel, VectorStore vectorStore) {
this.researcherClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一名专业研究员。你的职责是根据用户给出的研究课题,查找相关资料,梳理成结构化的研究报告。
回答必须基于事实,注明资料出处。如果资料不足,请明确说明。
输出格式:
## 研究结论
...
## 主要依据
- 来源1:...
- 来源2:...
""")
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) // 注入RAG
.build();
}
public String research(String topic) {
return researcherClient.prompt()
.user(topic)
.call()
.content();
}
}
4.2 Coder Agent
Coder 负责生成代码。它可以定义代码生成的 System Prompt,指定语言、风格、输出格式。
arduino
@Component
public class CoderAgent {
private final ChatClient coderClient;
public CoderAgent(ChatModel chatModel) {
this.coderClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一位资深软件工程师。根据需求编写高质量代码。
要求:
1. 代码简洁、健壮,包含必要的注释。
2. 输出代码块(markdown格式)。
3. 提供简短的实现说明。
""")
.build();
}
public String code(String requirement) {
return coderClient.prompt()
.user(requirement)
.call()
.content();
}
}
4.3 Reviewer Agent
Reviewer 审查代码,给出改进意见。
arduino
@Component
public class ReviewerAgent {
private final ChatClient reviewerClient;
public ReviewerAgent(ChatModel chatModel) {
this.reviewerClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一位代码审查专家。请审查以下代码,指出潜在问题(bug、安全漏洞、性能问题、可维护性),
并给出改进建议。用列表形式输出:问题 + 严重程度 + 建议。
""")
.build();
}
public String review(String code) {
return reviewerClient.prompt()
.user("请审查这段代码:\n" + code)
.call()
.content();
}
}
五、Supervisor Agent 组装:将专家封装为工具
为了让 Supervisor Agent 能调用上述专家,我们需要将每个专家的方法封装为 @Tool。Spring AI 的工具调用机制天然支持这一模式。
5.1 将专家方法暴露为工具
创建一个 TeamTools组件,将各专家 Agent 的方法包装为 @Tool方法:
kotlin
@Component
public class TeamTools {
private final ResearcherAgent researcherAgent;
private final CoderAgent coderAgent;
private final ReviewerAgent reviewerAgent;
public TeamTools(ResearcherAgent researcherAgent,
CoderAgent coderAgent,
ReviewerAgent reviewerAgent) {
this.researcherAgent = researcherAgent;
this.coderAgent = coderAgent;
this.reviewerAgent = reviewerAgent;
}
@Tool(description = "调用研究员专家,进行资料搜索、技术调研、信息汇总。适用于需要查找资料、文档、最佳实践的场景。")
public String research(@ToolParam(description = "研究课题或问题") String topic) {
return researcherAgent.research(topic);
}
@Tool(description = "调用开发专家,根据需求生成代码。适用于编写、实现、修改、重构代码的场景。")
public String code(@ToolParam(description = "代码开发需求,尽量详细") String requirement) {
return coderAgent.code(requirement);
}
@Tool(description = "调用审查专家,对代码进行质量审查、bug检测、性能分析与改进建议。")
public String review(@ToolParam(description = "需要审查的代码") String code) {
return reviewerAgent.review(code);
}
}
5.2 构建 Supervisor ChatClient
Supervisor Agent 只负责规划和汇报,不亲自实现业务逻辑。它的配置如下:
python
@Configuration
public class SupervisorAgentConfig {
@Bean
public ChatClient supervisorClient(ChatModel chatModel, TeamTools teamTools) {
return ChatClient.builder(chatModel)
.defaultSystem("""
你是一个团队主管,手下有三位专家:研究员(Research)、程序员(Coder)、审查员(Reviewer)。
当用户提出请求时,你需要:
1. 分析用户意图,判断需要哪些专家协作。
2. 按顺序调用合适的专家工具,每个工具调用之间确保参数传递正确。
3. 将专家的成果整合成清晰、完整的最终报告回复用户。
注意:
- 如果任务需要代码,请调用coder。
- 如果代码写完后需要检查质量,请调用reviewer,并把代码作为参数传入。
- 如果任务需要了解外部资料或公司知识,请调用researcher。
- 不要自己编造专家结论,必须使用工具返回的真实结果。
""")
.defaultTools(teamTools) // 注册专家工具
.build();
}
}
5.3 与记忆结合
为了支持多轮对话,我们可以在 Supervisor 调用时绑定 ChatMemory:
less
@RestController
@RequestMapping("/api/team")
public class TeamController {
private final ChatClient supervisorClient;
public TeamController(ChatClient supervisorClient) {
this.supervisorClient = supervisorClient;
}
@GetMapping("/chat")
public String chat(@RequestParam String sessionId, @RequestParam String message) {
return supervisorClient.prompt()
.user(message)
.memory(sessionId)
.call()
.content();
}
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestParam String sessionId, @RequestParam String message) {
return supervisorClient.prompt()
.user(message)
.memory(sessionId)
.stream()
.content();
}
}
5.4 完整工作流示例
假设用户请求: "请用 Java 写一个计算斐波那契数列的函数,并审查这段代码" 。
Supervisor Agent 的循环可能如下:
- 思考:用户需要代码和审查。我应该先调用 coder,再调用 reviewer。
- 行动 :调用
code(requirement="用Java写斐波那契数列函数") - 观察:Coder Agent 返回代码 + 说明。
- 再思考:代码已生成,现在需要审查。
- 行动 :调用
review(code=上一步返回的代码部分) - 观察:Reviewer 返回问题列表与建议。
- 再思考:信息完整,可以生成最终回答。
- 生成总结:包含代码、审查结果、最终建议。
这样一个复杂任务被分解为两个专家协作完成,且每个专家保持专注,质量远高于单个 Agent 硬着头皮生成。
六、深入:如何保证专家间的上下文传递
在 Supervisor 模式中,专家 Agent 之间不直接通信。它们只接收 Supervisor 传来的参数,返回自己的结果。因此,参数设计是上下文传递的关键。
6.1 简单参数传递
对于 Coder → Reviewer 的流程,Supervisor 需要从 Coder 的输出中提取代码块,传给 Reviewer。模型通常能够识别代码块。为了降低解析难度,我们可以在 Coder Agent 的 System Prompt 中强制要求输出 JSON 结构:
sql
Coder Agent System Prompt 修改为:
"""
输出格式必须严格为JSON:
{
"language": "Java",
"code": "...代码内容...",
"explanation": "...实现说明..."
}
"""
然后,Supervisor 模型可以获得结构化结果,更容易将 code字段提取出来传给 review工具。由于 Spring AI 的 BeanOutputConverter可以帮助解析,也可以在 Coder Agent 内部返回转换后的对象或字符串。简单起见,可以让 Coder 返回纯 Markdown 代码块,模型通常能正确提取。
6.2 使用 JSON Tool 参数
工具参数本身就是结构化的。review工具的 code参数会被模型填充。模型通过观察 coder 工具的结果,将代码部分抽取出来填入 review 参数。这是大模型的能力范围,通常工作良好。
6.3 深层上下文传递
如果任务非常复杂,一个部门可能需要多步协作。例如:
- Researcher 收集资料 → 传给 Coder 作为开发依据 → 传给 Reviewer 审查。
此时,Supervisor 的提示词中应明确指示: "将先前研究结果中的关键结论作为需求的一部分传递给编程专家" 。模型能够自动完成这一聚合。
七、进阶:动态团队协作与条件分支
Supervisor 模式不一定是线性的。模型完全可以根据需要动态决定调用顺序和次数。例如:
- 用户请求:"对比 Spring Boot 2 和 3 的差异,并写一个迁移指南。"
- 模型可以:
research("Spring Boot 2 vs 3 差异")→ 得到资料 →code("基于上述资料编写迁移步骤示例")→review("检查示例代码")。
再如:
- 用户请求:"这段代码有性能问题吗?"
- 模型直接
review(code),无需 research。
因此,只要工具描述清晰,模型的表现就是灵活的。我们还可以添加更多专家工具,例如 architect、tester、documenter等,扩展团队能力。
7.1 并行调用多个工具
Spring AI 的 ChatClient 也支持一次生成多个工具调用。如果用户希望同时查天气和查订单,模型可能同时输出两个工具调用。但专家 Agent 之间存在依赖关系(如 Coder → Reviewer)时,模型会顺序执行。这种机制由模型自动决定,无需额外编码。
7.2 主管的记忆
Supervisor 的 ChatMemory 保存了整个多 Agent 协作历史。在后续对话中,模型可以记住之前生成过什么代码、审查结果如何,从而进行针对性修改。
八、生产环境多 Agent 系统的最佳实践
8.1 防止失控循环
多 Agent 系统中,模型可能反复调用工具而无法收尾。Spring AI 的自动循环有内部轮数限制,但为了安全和成本,建议:
- 在基础设施层面设置超时(如 60 秒);
- 监控每次调用的工具次数,异常时告警;
- 在 Supervisor System Prompt 中严格要求"最多调用 X 次工具"或"一旦获得足够信息立即总结"。
8.2 成本控制
每个专家调用都会产生 token 费用。优化建议:
- 专家 Agent 使用更小的模型(如 gpt-4o-mini),而 Supervisor 使用更强模型(如 gpt-4o);
- 限制专家返回内容长度,例如让 Coder 只返回核心代码,不输出过多解释;
- 对于 Reviewer,可以传入代码片段而非整个文件,减少 token。
8.3 可观测性与调试
多 Agent 系统难以调试,必须记录完整链路:
c
log.info("Supervisor准备调用工具,工具名称: {}", toolName);
log.info("工具参数: {}", args);
log.info("工具返回: {}", result);
也可以自定义 Advisor 记录每次请求响应。实现方式可参考本文 4.2 节。
8.4 专家 Agent 的独立测试
每一个专家都可以单独作为接口暴露,便于测试:
less
@RestController
@RequestMapping("/api/team/agents")
public class AgentController {
private final ResearcherAgent researcher;
private final CoderAgent coder;
private final ReviewerAgent reviewer;
// 构造函数注入...
@PostMapping("/research")
public String research(@RequestBody String topic) {
return researcher.research(topic);
}
@PostMapping("/code")
public String code(@RequestBody String requirement) {
return coder.code(requirement);
}
@PostMapping("/review")
public String review(@RequestBody String code) {
return reviewer.review(code);
}
}
这样可以在集成 Supervisor 前,单独验证每个专家的效果,并针对性地调整其 System Prompt。
8.5 权限与安全
专家 Agent 可能包含更敏感的工具(如 Coder 调用代码执行器、Researcher 调用 Web 搜索)。必须确保这些工具经过授权,不能因为模型被提示词注入而执行危险操作。建议:
- 每个专家 Agent 的 System Prompt 中加入安全约束;
- 高权限专家(如 Coder 中的代码执行)不应随意外放给终端用户;
- 工具执行过程做好审计。
九、总结
多 Agent 协作是解决复杂任务的必然趋势。Supervisor 模式通过一个主管 Agent 动态调度多个专家 Agent,将问题分解、专业分工、结果整合,类似于一个高效的团队。
在 Spring AI 中实现 Supervisor 模式的步骤可以概括为:
- 定义专家 Agent :每个专家是一个独立的
ChatClient,有自己特有的 System Prompt、工具和记忆。 - 将专家封装成工具 :用
@Tool注解暴露专家的核心方法,供 Supervisor 调用。 - 构建主管 Agent :注册专家工具,通过
ChatClient自动的 ReAct 循环实现"思考 → 调用专家 → 查看结果 → 再决策"。 - 加入记忆与会话隔离:使用 ChatMemory + conversationId 维持多轮对话。
- 监控与优化:记录日志、控制成本、独立测试每个专家。
现在我们拥有了一个可扩展的 Agent 团队。将来要新增能力,只需增加一个专家 Agent,再将其封装为工具注册给主管即可。这种架构让 AI 应用拥有了"组织能力",能够应对真实世界中高度复杂的任务。