作者:IT空门 · 门主
一、前言:单个 Agent 越强,反而越菜?
兄弟们,先抛个扎心问题:
你是不是也遇到过这种情况——
- Agent 工具塞了十几个,模型挑工具挑到“精神分裂”;
- Prompt 越写越长,Token 烧得飞起,回答却越来越像“废话生成器”;
- 同一个 Agent 又要写代码、又要写诗、还要算命,最后活成了“四不像”。
别急着换模型,大概率是 架构问题,不是模型问题。
Spring AI Alibaba 的多智能体(Multi-agent)框架,就是来治这种“全能 Agent 病”的。把一个超级 Agent 拆成多个专业 Agent,再用合适的协作模式串起来,效果往往立竿见影。
这篇文章,我会结合自己 demo 仓库里的 MultiAgent 模块,把 6 种主流多智能体协作模式掰开揉碎讲清楚:
| 1 | 顺序执行(Sequential) | A 干完给 B,B 干完给 C,一条流水线 |
| 2 | 并行执行(Parallel) | 三个小弟同时干活,老大最后汇总 |
| 3 | 路由(LlmRouting) | 让 LLM 当“前台”,自动分诊到对应专家 |
| 4 | 监督者(Supervisor) | 派一个“项目经理”,动态调度多个小弟 |
| 5 | 自定义条件(Customized Conditional) | 不靠 LLM,靠规则关键词精准路由 |
| 6 | 混合模式(Compound) | 监督者里套路由,复杂业务直接起飞 |
这坑我替你踩过了,下面直接上硬菜。
二、环境速览
先把家底亮一下,方便后面所有代码直接跑:
| JDK | 17 |
| Spring Boot | 3.x |
| Spring AI Alibaba | 1.0.x |
| ChatModel | DashScope(通义千问)/ Ollama 均可 |
| 关键依赖 | spring-ai-alibaba-agent-framework |
核心 Maven 依赖:
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>1.0.0</version>
</dependency>
别急着 Ctrl+C / Ctrl+V,先把场景看完。
三、什么是多智能体(Multi-agent)?
写正文之前,先把“多智能体”这个概念压实。 不然你后面看到 SequentialAgent、LlmRoutingAgent 一定会一脸懵。
3.1 一句话理解
Multi-agent 就是把一个“什么都干”的大 Agent,拆成多个“专精一门”的小 Agent,再按业务场景拼成一个协作工作流。
简单说:从“一个人干所有事”变成“一个团队协作”。
3.2 官方定义
Multi-agent 将复杂的应用程序分解为多个协同工作的专业化 Agent。与依赖单个 Agent 处理所有步骤不同,Multi-agent 架构允许你将更小、更专注的 Agent 组合成协调的工作流。
用人话翻译一下:
| 一个 Agent 身兼数职 | 每个 Agent 只干一件事 |
| 工具塞一坨,模型挑花眼 | 各 Agent 工具互不干扰 |
| Prompt 越写越长 | 每个 Agent 提示词短而精准 |
| 一个 Agent 挂了全挂 | 单点失败不影响全局 |
3.3 什么时候必须上 Multi-agent?
别为了“架构高级”强行上 Multi-agent,单 Agent 能解决的就别拆。
以下三种情况出现,基本就是 Multi-agent 出场的信号:
-
单个 Agent 拥有太多工具,难以做出正确的工具选择决策
- 工具超过 10 个,模型就开始“工具选择困难症”了;
- 拆成多个 Agent,每个 Agent 只挂 2~3 个工具,模型瞬间清醒。
-
上下文或记忆增长过大,单个 Agent 难以有效跟踪
- 对话超过 20 轮,Token 烧得飞起;
- 拆成多个 Agent,每个 Agent 只关心自己这一段上下文,省 Token 又高效。
-
任务需要专业化(例如:规划器、研究员、数学专家)
- 业务天然有分工(写代码的、写文档的、测试的);
- 一个 Agent 没法同时是“全栈工程师 + 资深文案 + 顶级测试”,那就各司其职。
经验之谈:当你的单 Agent Prompt 超过 500 字还没把任务说清楚时,就该考虑拆了。
3.4 两大协作模式:Tool Calling vs Handoffs
Spring AI Alibaba 的 Multi-agent 框架底层支持两种核心协作模式,搞懂这个,后面 6 种具体实现就是"排列组合":
| Tool Calling | Supervisor Agent 将其他 Agent 作为工具调用。"工具"Agent 不直接与用户对话——只执行任务并返回结果 | 集中式:所有路由都通过调用 Agent | 任务编排、结构化工作流 |
| Handoffs | 当前 Agent 决定将控制权转移给另一个 Agent。活动 Agent 随之变更,用户可以继续与新的 Agent 直接交互 | 去中心化:Agent 可以改变当前由谁来担当活跃 Agent | 跨领域对话、专家接管 |
怎么选?
| 需要集中控制工作流程? | ✅ 是 | ❌ 否 |
| 希望 Agent 直接与用户交互? | ❌ 否 | ✅ 是 |
| 专家之间复杂的、类人对话? | ❌ 有限 | ✅ 强 |
实战建议:可以混合使用两种模式——用 Handoffs 进行 Agent 切换,并让每个 Agent 将子 Agent 作为工具调用来执行专门任务。
本文的 Sequential / Parallel / LlmRouting / Supervisor / Customized / Compound,本质上都是这两种模式的具体实现。
3.5 本文的 6 种协作模式预告
下面要讲的 6 种模式,就是 Spring AI Alibaba 给我们提供的 “多人协作剧本”:
| Sequential | 流水线 | 工厂的传送带 |
| Parallel | 一起干 | 小组会议同时发言 |
| LlmRouting | 智能分诊 | 公司前台 |
| Supervisor | 动态调度 | 项目经理派活 |
| Customized Conditional | 规则路由 | 按地区转客服 |
| Compound | 混合编排 | 大型项目多团队协作 |
记住这张表,后面看到名字就知道在干啥。
四、模式一:顺序执行(Sequential Agent)
4.1 场景故事
让 AI 写一篇散文,然后让 AI 当评委修改润色。
这就是典型的 流水线:写 → 审 → 输出。
4.2 核心原理
顺序模式下,每个 Agent 按 subAgents 列表顺序执行,前一个 Agent 的 outputKey 输出,会作为状态写入 OverAllState,下一个 Agent 通过 instruction 占位符 {outputKey} 引用。
流程图

一图胜千言:Input → 第一个 subAgent → 第二个 subAgent → … → Output,一条道走到黑,谁也别想插队。
4.3 关键代码
参考文件:[SequentialAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/SequentialAgentChat.java)
// 写作 Agent
ReactAgent writingAgent = ReactAgent.builder()
.name("writingAgent")
.model(reactAgentChatModel)
.description("你是一个专业的写作Agent")
.instruction("你是一个知名的作家,擅长创作高质量的文学作品。请根据用户的提问进行回答:{input}。")
.outputKey("article") // 关键:输出键名
.build();
// 审核 Agent
ReactAgent reviewAgent = ReactAgent.builder()
.name("reviewAgent")
.model(reactAgentChatModel)
.description("你是一个专业的审核Agent")
.instruction("你是一个专业的写作审核Agent,擅长审核和修正文学作品。" +
"请对文章进行评审修正:\\n{article}," + // 占位符引用上一个 Agent 的输出
"最终返回评审修正后的文章内容;不要返回:'评审意见'等文字")
.outputKey("reviewedArticle")
.build();
// 顺序编排
SequentialAgent sequentialAgent = SequentialAgent.builder()
.name("sequentialAgent")
.description("根据用户给的内容写一个文章,然后审核文章,最后返回审核修正后的文章内容")
.subAgents(List.of(writingAgent, reviewAgent))
.build();
// 执行
Optional<OverAllState> result = sequentialAgent.invoke("帮我写一个100字左右的散文");
OverAllState state = result.get();
state.value("article").ifPresent(article ->
log.warn("写作Agent输出: {}", ((AssistantMessage) article).getText()));
state.value("reviewedArticle").ifPresent(reviewedArticle ->
log.warn("评审后文章: {}", ((AssistantMessage) reviewedArticle).getText()));
4.4 关键特性
顺序执行模式有 4 个核心特性,每个都值得背下来:
-
按顺序执行:Agent 按照 subAgents 列表中定义的顺序执行
- 写在前面的先跑,写在后面的后跑,别想插队。
-
状态传递:每个 Agent 的输出通过 outputKey 存储在状态中,可被后续 Agent 访问
- 这是顺序模式能"传话"的根本原因。
-
消息历史:默认情况下,所有 Agent 共享消息历史
- 简单说就是:后面的 Agent 能看到前面 Agent 的对话记录。
-
推理内容控制:使用 returnReasoningContents 控制是否在消息历史中包含中间推理
- 想省 Token 就关掉,想看完整链路就打开,自己权衡。
这 4 个特性决定了顺序模式的边界:能传话、能共享,但没法并行、没法动态跳转。
4.5 占位符机制(核心中的核心)
Spring AI Alibaba 的 instruction 支持三种占位符:
| {input} | 用户输入的原始内容 | 第一个 Agent |
| {outputKey} | 引用其他 Agent 的输出 | 顺序执行中引用前序输出 |
| {stateKey} | 引用状态中的任意键值 | 访问任意状态数据 |
看似简单,实则是多 Agent 数据传递的命脉。
4.6 自定义 Agent 上下文(高级玩法)
Multi-agent 设计的核心是上下文工程——决定每个 Agent 看到什么信息。Spring AI Alibaba 提供了细粒度的控制:
| instruction | 在当前 Agent 节点处插入新的问题说明,支持占位符 | 每个 Agent 的 instruction 要短而精准 |
| returnReasoningContent | 控制子 Agent 的上下文是否返回父流程。设为 false,其他 Agent 看不到这个子 Agent 的推理过程,只能看到 outputKey 输出 | 想省 Token 就关掉,想看完整链路就打开 |
| includeContents | 控制当前子 Agent 执行时,是只基于自己的 instruction 工作,还是带上所有父流程的上下文 | 设为 false 可以让子 Agent 专注于自己的任务,不受父流程复杂上下文的影响 |
| outputKey | 指定输出内容的键名,可被后续 Agent 通过占位符引用 | 使用有意义的命名,如 article、reviewedArticle |
| systemPrompt / instruction | LlmRoutingAgent 和 SupervisorAgent 支持定制,用于覆盖默认实现,控制路由决策行为 | 路由准确性全靠这俩,后面章节专门讲 |
核心原则:系统的质量在很大程度上取决于上下文工程。目标是确保每个 Agent 都能访问执行任务所需的正确数据,不多不少。
4.7 适用场景
- 内容创作流水线(写 → 审 → 翻译)
- 数据处理管道(清洗 → 校验 → 入库)
- 多步骤业务编排(需求 → 设计 → 实现)
五、模式二:并行执行(Parallel Agent)
5.1 场景故事
“我是天蝎座,帮我分析一下我的星座、配对和星盘。”
一个用户问三个问题,为啥要排队回答?
并行模式:三个专家 Agent 同时上,结果最后汇总。
5.2 核心原理
所有子 Agent 同时接收同一份输入,各自执行完后再走 MergeStrategy 合并策略。
流程图

一张图看清 并行模式的本质:一个 Input 扇出到 N 个 subAgent,每个 subAgent 独立产出自己的 Output_1/Output_2/Output_3,最后由 MergeStrategy 汇总。
三种默认合并策略
| DefaultMergeStrategy | 拼接成键值 Map |
| ConcatenationMergeStrategy | 拼接成单一字符串 |
| ListMergeStrategy | 合并为 List |
5.3 关键代码
参考文件:[ParallelAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/ParallelAgentChat.java)
ReactAgent constellationAgent = ReactAgent.builder()
.name("constellationAgent")
.model(reactAgentChatModel)
.instruction("你是一个星座分析大师。用户给的内容是:{input},请根据用户的内容分析星座。")
.outputKey("constellation")
.build();
ReactAgent pairAgent = ReactAgent.builder()
.name("pairAgent")
.model(reactAgentChatModel)
.instruction("你是星座配对大师。用户给的内容是:{input},请根据用户的内容配对星座。")
.outputKey("pair")
.build();
ReactAgent starDiskAgent = ReactAgent.builder()
.name("starDiskAgent")
.model(reactAgentChatModel)
.instruction("你是星盘大师。用户给的内容是:{input},请根据用户的内容分析星盘。")
.outputKey("starDisk")
.build();
ParallelAgent parallelAgent = ParallelAgent.builder()
.name("parallelAgent")
.subAgents(List.of(constellationAgent, pairAgent, starDiskAgent))
.description("并行执行多个创作任务")
.mergeOutputKey("parallel")
.mergeStrategy(deduplicationSummaryMergeStrategy) // 用自定义策略
.build();
5.4 进阶:自定义合并策略(重点!)
默认策略只能“傻拼”,但实际项目里,多个 Agent 输出经常有内容重复。怎么办?
自己写一个 MergeStrategy 接 LLM 做二次摘要去重。
参考文件:[DeduplicationSummaryMergeStrategy.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/tool/DeduplicationSummaryMergeStrategy.java)
@Component
public class DeduplicationSummaryMergeStrategy implements ParallelAgent.MergeStrategy {
private final ChatModel reactAgentChatModel;
@Override
public Object merge(Map<String, Object> subAgentResults, OverAllState overallState) {
// 1. 聚合多 Agent 结果
String aggregated = aggregateSubAgentResults(subAgentResults);
// 2. 调一个总结 Agent 做二次去重
ReactAgent summaryAgent = ReactAgent.builder()
.name("deduplicationSummaryAgent")
.model(reactAgentChatModel)
.instruction("你是内容整合专家,擅长去重与摘要。输入:\\n{input}\\n" +
"请去除重复信息,整合输出一份结构清晰的综合报告。")
.outputKey("summaryReport")
.build();
try {
return summaryAgent.invoke(aggregated)
.map(state -> state.value("summaryReport").orElse(""))
.map(this::extractText)
.orElse("");
} catch (GraphRunnerException e) {
// 兜底:返回原始聚合
return aggregated;
}
}
// … aggregateSubAgentResults / formatAgentOutput / extractText 略
}
这就是“看似能跑,其实已经埋雷”和“程序跑起来了,头发少了”的真实写照——不做二次去重,三个 Agent 输出的星座描述 80% 重复。
5.5 适用场景
- 多视角分析(星座/八字/紫微同时算)
- 多模态内容生成(标题/正文/标签并行产出)
- A/B 内容对比
六、模式三:路由(LlmRoutingAgent)
6.1 场景故事
“我梦见自己在大雾里开车……”(解梦) “帮我分析下我的星座……”(星座) “我是 1990-01-01 出生,帮我算下生命数字……”(数字密码)
每种问题对应一个专家 Agent,让 LLM 充当“前台分诊台”,自动判断该派给谁。
6.2 核心原理
LlmRoutingAgent 接收用户输入,调用 LLM 分析子 Agent 的 description,选择一个最匹配的子 Agent 执行。
流程图

一张图看清 路由模式的精髓:In → LLM Call Router(一次 LLM 调用决策走哪条线)→ 只选一个 LLM Call N → Out。 不是全选,是单选,虚线代表没被选中的路径。
注意:每次只选一个,不是全选。
6.3 关键代码
参考文件:[LlmRoutingAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/LlmRoutingAgentChat.java)
ReactAgent constellationAgent = ReactAgent.builder()
.name("constellationAgent")
.model(reactAgentChatModel)
.description("你是一个专业的星座分析大师,擅长分析星座。") // 重要:路由靠这个判断
.instruction("你是一个星座分析大师,擅长分析星座")
.outputKey("constellation")
.build();
ReactAgent birthAgent = ReactAgent.builder()
.name("birthAgent")
.model(reactAgentChatModel)
.description("你是一个专业的生命数字密码分析大师,擅长分析生命数字密码。")
.instruction("你是一个生命数字密码分析大师")
.outputKey("birth")
.build();
ReactAgent zhugongAgent = ReactAgent.builder()
.name("zhugongAgent")
.model(reactAgentChatModel)
.description("你是一个专业的周公解梦大师,擅长解梦。")
.instruction("你是一个周公解梦大师")
.outputKey("zhugong")
.build();
LlmRoutingAgent llmRoutingAgent = LlmRoutingAgent.builder()
.name("llmRoutingAgent")
.model(reactAgentChatModel)
.description("根据用户需求智能路由到合适的专家Agent")
.subAgents(List.of(constellationAgent, birthAgent, zhugongAgent))
.build();
Optional<OverAllState> result = llmRoutingAgent.invoke("我梦见自己在一片大雾里开车,怎么也找不到回家的路");
6.4 关键特性
路由模式有 4 个核心特性,每个都值得背下来:
-
智能路由:LLM 根据输入内容和子 Agent 的描述自动选择最合适的 Agent
- 不用写 if-else,模型自己"看人下菜碟"。
-
灵活扩展:可以轻松添加新的专家 Agent,LLM 会自动识别并路由
- 加 Agent 不用改路由逻辑,改 subAgents 列表就行。
-
描述驱动:子 Agent 的 description 非常重要,它告诉 LLM 何时应该选择该 Agent
- description 写不好,路由就废一半,下一节专门讲怎么写。
-
单次执行:每次请求只路由到一个 Agent 执行
- 不是"广播"是"派单",别指望 LLM 同时调多个。
这 4 个特性决定了路由模式的边界:能智能分诊,但没有状态记忆、不能多步串联。
6.5 路由准确性优化(避坑重点)
坑:为什么我的路由 Agent 经常选错?
罪魁祸首:description 写得太模糊!
反面教材:
description = "你是星座分析大师" // ❌ 太短,LLM 看不出区别
正确写法:
description = "专门处理星座分析问题,擅长分析星座运势、性格、配对。" +
"适用于用户问'我的星座怎么样'、'天蝎座配什么'等场景。" // ✅ 场景化
6.6 进阶:自定义 systemPrompt / instruction
除了靠 description 自动路由,LlmRoutingAgent 还支持通过 systemPrompt 和 instruction 手动控制路由决策,这是官方文档里的高级玩法:
systemPrompt:设置路由决策的系统提示
替换默认系统提示,提供详细的决策规则和上下文:
final String ROUTING_SYSTEM_PROMPT = """
你是一个智能的内容路由Agent,负责根据用户需求将任务路由到最合适的专家Agent。
## 可用的子Agent及其职责
### constellationAgent
– 功能: 擅长星座分析
– 适用场景: 用户问星座运势、性格、配对
### birthAgent
– 功能: 擅长生命数字密码分析
– 适用场景: 用户问生命数字、数字密码
### zhugongAgent
– 功能: 擅长周公解梦
– 适用场景: 用户说"梦见"、"解梦"
## 决策规则
只返回Agent名称,不要包含其他解释。
""";
LlmRoutingAgent routingAgent = LlmRoutingAgent.builder()
.name("content_routing_agent")
.model(chatModel)
.systemPrompt(ROUTING_SYSTEM_PROMPT) // 关键:自定义系统提示
.subAgents(List.of(constellationAgent, birthAgent, zhugongAgent))
.build();
instruction:设置路由决策的用户指令
作为 UserMessage 添加到消息列表中,提供额外的上下文信息或特定的路由指导:
final String ROUTING_INSTRUCTION = """
请根据用户的需求,选择最合适的Agent来处理任务。
特别注意:
– 如果用户提到"星座"、"运势",选择 constellationAgent
– 如果用户提到"生命数字"、"数字密码",选择 birthAgent
– 如果用户提到"梦见"、"解梦",选择 zhugongAgent
""";
LlmRoutingAgent routingAgent = LlmRoutingAgent.builder()
.name("content_routing_agent")
.model(chatModel)
.instruction(ROUTING_INSTRUCTION) // 关键:自定义路由指令
.subAgents(List.of(constellationAgent, birthAgent, zhugongAgent))
.build();
实战建议:systemPrompt 定义路由决策的整体框架,instruction 提供具体的路由指导。两个可以同时用,效果叠加。
6.7 适用场景
- 多领域客服(售前/售后/投诉自动分流)
- 多技能助手(编程/写作/翻译一键切换)
- 智能问诊(按症状自动派科室)
七、模式四:监督者(Supervisor Agent)
7.1 场景故事
“卜卦占星师,帮我分析一下今年我的运势,我的伴侣是什么星座?”
这问题至少需要两步:
两次执行,先后顺序还不一定。路由模式搞不定(只能选一个),并行模式也不合适(步骤有依赖)。
这时候就要 监督者(Supervisor) 上场——一个“项目经理” Agent,动态决定先调谁、再调谁、最后调谁。
7.2 核心流程
流程图

一张图看懂 监督者的"项目管理循环": 主 Agent(content_supervisor)→ check state(状态评估)→ 选一个子 Agent(reviewer_agent / translator_agent)→ 回到主 Agent → 再次 check state → … → stop(FINISH)。 核心是"循环调度",不是"一次性派单"。
流程详解
监督者模式下,整个调度过程是一个循环,由 mainAgent(监督者)反复决策驱动:
监督者 Agent 接收用户输入或前序 Agent 的输出
- 第一轮接收用户原始需求,后续轮接收子 Agent 的执行结果。
LLM 分析当前状态并决定最合适的子 Agent
- 这步由 mainAgent 完成,它是个纯路由器,输出严格的 JSON 数组。
选中的子 Agent 处理任务
- 每次只调一个子 Agent(一次调用原则)。
子 Agent 执行完成后返回监督者
- 结果写入 OverAllState,作为下一轮决策的输入。
监督者根据结果决定下一步:
- 继续路由到另一个子 Agent(多步骤任务未完成)
- 返回 FINISH 完成任务(所有需求已满足)
这个循环会一直跑下去,直到 mainAgent 输出 ["FINISH"] 才会退出。 千万别让 mainAgent 干业务,否则 JSON 解析必挂。
7.3 核心原理
SupervisorAgent 由两部分组成:
| mainAgent | 纯路由器,只输出 JSON 数组,告诉框架下一步调谁 |
| subAgents | 业务执行者,真正干活的 Agent |
关键约束:mainAgent 绝对不能是业务执行型 Agent! 否则它的业务输出(Markdown 文章等)会破坏 JSON 解析。
7.4 提示词模板(核心资产)
参考文件:[PromptConstant.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/tool/PromptConstant.java)
public static final String SUPERVISOR_PROMPT_TEMPLATE =
"你是一个严格的【任务路由器】,不执行业务,只负责决定下一步调用哪一个子 Agent。\\n" +
"\\n" +
"## 可用子 Agent(必须使用下方 name 字段,不要自创名称)\\n" +
"\\n" +
"{agent_list}\\n" +
"\\n" +
"## 路由原则(极其重要)\\n" +
"1. 【一次调用原则】每次只调用 1 个最匹配的子 Agent。\\n" +
"2. 【无擅自追加】子 Agent 完成后,检查用户原始需求是否已满足。若已满足,直接返回 [\\"FINISH\\"]。\\n" +
"3. 【严格收敛】当且仅当用户需求全部满足时,返回 [\\"FINISH\\"];中间过程禁止 FINISH。\\n" +
"\\n" +
"## 输出格式(极其重要,必须严格遵守)\\n" +
"1. 你的回复必须且只能是一个 JSON 数组。\\n" +
"2. 调用单个 Agent:[\\"agentName\\"]\\n" +
"3. 任务完成:[\\"FINISH\\"]\\n" +
"4. 禁止输出任何 JSON 以外的内容。\\n";
{agent_list} 会在运行时动态替换为子 Agent 列表,增删子 Agent 无需改模板——这才是企业级写法。
7.5 关键代码
参考文件:[SupervisorAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/SupervisorAgentChat.java)
// 三个业务 Agent
ReactAgent constellationAgent = ReactAgent.builder()
.name("constellationAgent")
.model(reactAgentChatModel)
.description("你是一个专业的星座分析大师,擅长分析星座。")
.outputKey("constellation")
.build();
ReactAgent partnerMatchingAgent = ReactAgent.builder()
.name("partnerMatchingAgent")
.model(reactAgentChatModel)
.description("你是一个专业的伴侣匹配专家,擅长根据用户需求匹配伴侣。")
.outputKey("partnerMatching")
.build();
ReactAgent constellationContentEditorAgent = ReactAgent.builder()
.name("constellationContentEditorAgent")
.model(reactAgentChatModel)
.description("资深的卜卦占星师,擅长根据卜卦分析星座运势")
.outputKey("constellationContentEditor")
.build();
List<Agent> subAgents = List.of(constellationAgent, partnerMatchingAgent, constellationContentEditorAgent);
// 动态拼接子 Agent 列表到提示词
String agentListSection = subAgents.stream()
.map(agent -> "- " + agent.name() + ":" + agent.description())
.collect(Collectors.joining("\\n"));
// mainAgent(纯路由器)
ReactAgent mainAgent = ReactAgent.builder()
.name("supervisorRouter")
.model(reactAgentChatModel)
.instruction(PromptConstant.SUPERVISOR_PROMPT_TEMPLATE
.replace("{agent_list}", agentListSection))
.build();
// 监督者
SupervisorAgent supervisorAgent = SupervisorAgent.builder()
.name("supervisorAgent")
.model(reactAgentChatModel)
.description("监督者代理服务")
.mainAgent(mainAgent)
.subAgents(subAgents)
.build();
7.6 关键特性
监督者模式有 4 个核心特性,和路由模式对比着记:
-
循环调度:不是一次性派单,而是"调完一个回来再决定下一个"
- 路由模式是单次执行,监督者是多轮循环。
-
动态决策:mainAgent 根据当前状态(包括前序 Agent 的输出)决定下一步
- 这就是"项目经理"和"前台"的区别——前台只管分诊,项目经理管全程。
-
严格 JSON 输出:mainAgent 必须输出 ["agentName"] 或 ["FINISH"]
- 千万别让 mainAgent 干业务,否则 JSON 解析必挂。
-
状态累积:每轮调用的结果都写入 OverAllState,后续轮次可以访问
- 第二轮 Agent 能看到第一轮 Agent 的输出,信息不丢。
这 4 个特性决定了监督者模式的边界:能多步串联、能动态调度,但mainAgent 必须是纯路由器,不能干业务。
7.7 适用场景
- 任务多步骤且依赖关系不固定
- 需要根据中间结果动态调整下一步
- 业务复杂到单 Agent 根本 hold 不住
八、模式五:自定义条件代理(Customized Conditional Agent)
8.1 为什么需要自定义?
业务方要求:“星座关键词走星座 Agent,生肖关键词走生肖 Agent,不准用 LLM 路由,关键词必须严格匹配。”
官方没现成的“关键词路由” Agent。怎么办?
自己造一个 ConditionalAgent!
8.2 核心思路
继承 FlowAgent,手动用 StateGraph API 构建条件路由图:
START → 根透明节点 → 条件评估节点 → [AgentA | AgentB | …] → END
8.3 关键代码
参考文件:[ConditionalAgent.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/tool/ConditionalAgent.java)
public class ConditionalAgent extends FlowAgent {
private final Map<String, List<String>> conditionKeywords;
private final Map<String, Agent> conditionalAgents;
@Override
protected StateGraph buildSpecificGraph(FlowGraphBuilder.FlowGraphConfig config)
throws GraphStateException {
StateGraph graph = new StateGraph(config.getName(), config.getKeyStrategyFactory());
// 1. 根 Agent 透明节点
graph.addNode(name(), node_async(new TransparentNode()));
// 2. 条件评估节点
String conditionNodeName = name() + "_condition";
graph.addNode(conditionNodeName, node_async(state -> {
String input = Optional.ofNullable(state.value("input", ""))
.map(Object::toString).orElse("").toLowerCase();
String conditionResult = evaluateCondition(input);
return Map.of("_condition_result", conditionResult);
}));
// 3. 边:根 → 条件评估
graph.addEdge(name(), conditionNodeName);
// 4. 子 Agent 节点 + 条件边
Map<String, String> routingMap = new LinkedHashMap<>();
for (Map.Entry<String, Agent> entry : conditionalAgents.entrySet()) {
Agent subAgent = entry.getValue();
FlowGraphBuildingStrategy.addSubAgentNode(subAgent, graph);
routingMap.put(entry.getKey(), subAgent.name());
graph.addEdge(subAgent.name(), END);
}
routingMap.put("default", END);
graph.addConditionalEdges(conditionNodeName, new ConditionEvaluatorAction(), routingMap);
graph.addEdge(START, name());
return graph;
}
/**
* 最佳匹配策略:匹配数最多的关键词组胜出
*/
private String evaluateCondition(String input) {
return conditionKeywords.entrySet().stream()
.map(entry -> Map.entry(entry.getKey(),
entry.getValue().stream().filter(input::contains).count()))
.filter(entry -> entry.getValue() > 0)
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse("default");
}
}
8.4 使用示例
参考文件:[CustomizedAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/CustomizedAgentChat.java)
private static final List<String> CONSTELLATION_KEYWORDS = List.of(
"星座", "星盘", "占星", "卜卦", "天蝎", "白羊", "金牛", "双子", "巨蟹");
private static final List<String> ZODIAC_KEYWORDS = List.of(
"生肖", "属相", "本命年", "属鼠", "属牛", "属虎", "属龙", "属蛇");
ReactAgent constellationAgent = ReactAgent.builder()
.name("constellationAgent")
.model(reactAgentChatModel)
.instruction("你是一个星座分析大师。请根据用户的内容分析星座:{input}")
.outputKey("constellation")
.build();
ReactAgent zodiacAgent = ReactAgent.builder()
.name("zodiacAgent")
.model(reactAgentChatModel)
.instruction("你是一个生肖分析大师。请根据用户的内容分析生肖:{input}")
.outputKey("zodiac")
.build();
ConditionalAgent conditionalAgent = ConditionalAgent.builder()
.name("conditionalAgent")
.description("根据关键词条件路由到星座或生肖 Agent")
.conditionalAgent("constellation", constellationAgent, CONSTELLATION_KEYWORDS)
.conditionalAgent("zodiac", zodiacAgent, ZODIAC_KEYWORDS)
.subAgents(List.of(constellationAgent, zodiacAgent))
.build();
Optional<OverAllState> result = conditionalAgent.invoke("我是属蛇的,帮我分析一下我的生肖运势和属相");
8.5 避坑要点
千万别只用“第一个匹配的关键词”! 通用词如“运势”、“配对”会让精确条件被错覆盖。
正确做法:使用“最佳匹配”策略,匹配关键词数量最多的条件胜出。
九、模式六:混合模式(Compound Agent)
9.1 场景故事
“帮我查询一下长沙的天气,然后分析下适合干啥,再生成一份 HTML 报告和 MD 表格报告。”
这个需求包含:
这就是混合模式:监督者(Supervisor)里嵌套路由(Routing)。
9.2 架构图
SupervisorAgent
├── mainAgent (路由器)
├── WeatherAgent (查天气)
├── WeatherAnalyzerAgent (分析天气)
└── report_router (LlmRoutingAgent)
├── HTMLGeneratorAgent
└── MDTableGeneratorAgent
9.3 关键代码
参考文件:[CompoundAgentChat.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/impl/CompoundAgentChat.java)
// 查天气
ReactAgent weatherAgent = ReactAgent.builder()
.name("WeatherAgent")
.model(reactAgentChatModel)
.outputKey("weather")
.instruction("这是一个查询天气的Agent,输入城市名称,返回天气信息")
.methodTools(compoundAgentTool) // 挂载天气查询工具
.build();
// 分析天气
ReactAgent weatherAnalyzerAgent = ReactAgent.builder()
.name("WeatherAnalyzerAgent")
.model(reactAgentChatModel)
.outputKey("weatherAnalyzer")
.instruction("这是一个分析天气的Agent,输入天气信息,返回天气分析结果")
.build();
// 报告生成(HTML vs Markdown 路由)
LlmRoutingAgent reportAgent = LlmRoutingAgent.builder()
.name("report_router")
.description("根据需求选择报告格式")
.model(reactAgentChatModel)
.subAgents(List.of(htmlGeneratorAgent, mdTableGeneratorAgent))
.build();
// mainAgent:项目经理
ReactAgent mainAgent = ReactAgent.builder()
.name("MainDispatchAgent")
.model(reactAgentChatModel)
.outputKey("supervisor_next")
.instruction("你是主调度Agent,根据用户输入决定下一步执行哪个子Agent。" +
"可用子Agent:WeatherAgent(查询天气)、WeatherAnalyzerAgent(分析天气)、" +
"report_router(生成报告)。你必须且只能以JSON数组格式输出。")
.build();
// 监督者编排
SupervisorAgent supervisorAgent = SupervisorAgent.builder()
.name("supervisor")
.mainAgent(mainAgent)
.subAgents(List.of(weatherAgent, weatherAnalyzerAgent, reportAgent))
.build();
Optional<OverAllState> result = supervisorAgent.invoke("帮我查询一下长沙的天气");
9.4 工具集成小贴士
.methodTools(compoundAgentTool) // 给 Agent 挂载 @Tool 标注的工具
工具类示例([CompoundAgentTool.java](file:///d:/project/learn/spring_ai_alibaba_demo/src/main/java/com/spring/ai/alibaba/agent/springaialibabaagent/MultiAgent/tool/CompoundAgentTool.java)):
@Tool(name = "weather", description = "查询指定城市未来 15 天的天气信息")
public String weather(String city) {
// … 查天气逻辑
}
这是 Spring AI 原生 @Tool 注解,与 Agent 无缝集成。
9.5 适用场景
- 复杂业务编排(多步骤 + 多分支)
- 报告生成系统(查询 → 分析 → 多格式输出)
- 工作流引擎核心
十、六大模式对比与选型
| Sequential | ❌ 串行 | ✅ 强依赖 | 固定顺序 | 流水线 |
| Parallel | ✅ 并行 | ❌ 无依赖 | 无 | 多视角分析 |
| LlmRouting | ❌ 单次 | ❌ 无 | LLM 智能选择 | 多专家分诊 |
| Supervisor | ❌ 串行 | ✅ 动态依赖 | LLM 路由器 | 复杂多步任务 |
| Customized Conditional | ❌ 单次 | ❌ 无 | 关键词匹配 | 规则化路由 |
| Compound | ✅ 可并行 | ✅ 强依赖 | 多层组合 | 复杂业务编排 |
选型口诀
流水线 → Sequential 一起干 → Parallel 智能派单 → LlmRouting 动态调度 → Supervisor 规则路由 → Customized 复杂业务 → Compound
十一、避坑指南(我踩过的坑)
1. mainAgent 不能是业务执行型
❌ 让 mainAgent 既负责路由又负责写文章 → JSON 解析必挂
✅ mainAgent 严格只输出 ["agentName"] 或 ["FINISH"]
2. 占位符名要和 outputKey 严格一致
❌ outputKey = "article",instruction 写 {Article} → 替换失败
✅ 完全小写匹配
3. 并行模式必须做二次去重
❌ 三个 Agent 直接拼接 → 70% 内容重复
✅ 自定义 MergeStrategy 调 LLM 摘要
4. description 写不清楚 = 路由必错
❌ "你是聊天助手"
✅ "专门处理用户闲聊、寒暄、打招呼,不处理具体业务问题"
5. 关键词路由不能用“首个匹配”
❌ 输入"我是属蛇的,帮我看星座" → 误匹配"蛇"
✅ 最佳匹配策略:哪个条件关键词命中最多选哪个
6. Agent name 必须全局唯一
❌ 两个 Agent 都叫 "agent" → 状态覆盖、路由错乱
✅ 命名规范:业务前缀_功能 (例:weather_query_agent)
附录:源码参考
本文所有示例代码均来自作者的 Spring AI Alibaba Demo 仓库,全部可跑、全部脱敏,欢迎 Star ⭐ 和 Fork。
| 顺序 | impl/SequentialAgentChat.java | — |
| 并行 | impl/ParallelAgentChat.java | tool/DeduplicationSummaryMergeStrategy.java |
| 路由 | impl/LlmRoutingAgentChat.java | — |
| 监督者 | impl/SupervisorAgentChat.java | tool/PromptConstant.java |
| 自定义条件 | impl/CustomizedAgentChat.java | tool/ConditionalAgent.java |
| 混合 | impl/CompoundAgentChat.java | tool/CompoundAgentTool.java |
源码下载 / 在线浏览
- CSDN 资源:https://blog.csdn.net/m0_55699184?type=download
- 官方文档:https://java2ai.com/docs/frameworks/agent-framework/advanced/multi-agent
所有代码均已脱敏可跑,复制即用,记得替换成自己的 ChatModel。 如果本项目对你有帮助,欢迎点个 Star,这是对作者最大的支持 🚀
🙏 作者介绍
📌 写文不易,Bug 更不易。
如果这篇文章对你有帮助,可以搜一搜:空门技术栈
这里分享:
- ✅ Java / Spring AI / 企业级项目实战
- ✅ Docker / RAG知识库 / 微服务踩坑
- ✅ Python、前端、AI应用落地
- ✅ 偶尔分享一些「头发保卫战」经验 😆
一个热爱技术、持续填坑的开发者, 陪你一起少踩坑,少加班,多写优雅代码。
📖 推荐阅读
- Spring AI Alibaba 智能体实战:从 0 到 1 搭建你的第一个 AI Agent
- Spring AI Alibaba 高级玩法:MCP 集成 + 多模态 Agent 实战
- AI 为什么总"失忆"?LangChain Memory 完全指南:从 InMemory 到 Redis 实战避坑
- Java 单例模式详解:7 种实现方式 + volatile 原理 + 反射与序列化问题
- 告别手动复制接口文档!Apifox MCP + AI 自动测试让开发效率起飞
- 别再纠结学哪个了!Java AI 三大框架深度对比:Spring AI vs AgentScope-Java
- MySQL MCP Server 从零安装到使用实战,AI 直接查询数据库
- Spring 注入三剑客:@Resource、@Autowired、@RequiredArgsConstructor 到底该用哪个?
- Spring Event 用了三年,同事一句话把我问懵了
- Java 抽象类(Abstract Class)彻底讲透:从基础到多态实战
🤝 技术交流 / 项目合作
平时也会做一些技术项目与咨询,包括:
- Java / Spring Boot 企业级项目开发
- AI 应用开发(LangChain、RAG、Agent、知识库)
- Docker / Linux / 私有化部署
- 系统功能开发、接口对接、性能优化
- 疑难问题排查与技术咨询
如果你:
- 想做 AI 项目,但不确定技术方案
- 项目卡在某个 Bug 很久
- 想把 AI 接入现有系统
- 需要企业级开发支持
欢迎交流。
📮 联系方式:
- Email:2929119150@qq.com
- 也可以私信我
- 技术交流可通过个人主页联系
有些坑,一个人踩是事故;一起踩,就是经验 😎




