别再让大模型单打独斗了!Java 多 Agent 协作实战:任务拆解+结果聚合

用 Spring AI Alibaba 打造你的智能体"天团"

从"一个模型干所有事"到"多个专家 AI 各司其职"------聊聊多 Agent 协作的 Java 实践

一、为什么一个 Agent 不够用了

在之前的文章中,我们聊过大模型工具调用------让模型自己判断该调用哪个 API,比如查天气、查订单。这在很多场景下已经够用了,比如用户问"北京今天天气怎么样",调一个天气 API 就解决了。

但现实中的业务问题,往往没有这么简单。

拿一个电商客服的场景来说,用户问:"帮我查一下订单 10086 的物流状态,然后如果已经超过预计送达时间,给我发一封催件邮件。"

如果只用一个 Agent 来处理,它需要同时具备三种能力:查订单、判断是否超时、调用邮件服务。听起来好像也不难?但问题在于,大模型的上下文是有容量限制的,当工具数量增加到十几个甚至几十个时,单个 Agent 在众多工具中做出正确选择就变得非常困难

更麻烦的是,如果用户连续问了三个彼此独立的事情:"查订单 10086 的物流、查北京明天天气、帮我查一下 2025 年 Java 岗位平均薪资"。普通对话里,这三个问题没有任何关联,但如果你只用一个 Agent,它的上下文里会塞满这三次调用的历史记录,Token 消耗翻倍、模型更容易"幻觉"。

这就是为什么我们需要多 Agent 协作。

把一个复杂的系统拆成多个更小、更专注的 Agent,每个 Agent 负责自己擅长的那一摊活,然后由"总指挥"来协调它们的工作。一个 Agent 做不好所有事,但多个 Agent 可以

二、三种主流的协作模式

在 2026 年的企业级 AI 实践中,有三种常见的多 Agent 协作模式被广泛使用:Supervisor/Worker(监督者-工人模式)、Peer-to-Peer(对等模式)和 Hierarchical(层级模式)。选择哪种模式取决于任务复杂度和容错要求,而不是看哪个框架支持得好

Supervisor/Worker(监督者-工人模式)

这是最常用、也最容易理解的一种模式。一个中心化的 Supervisor Agent 接收用户的复杂任务,把它拆解成多个子任务,分发给不同的 Worker Agent 执行,最后汇总它们的结果

什么时候用:任务的各个环节之间有明确的依赖关系,需要统一的调度和监控。

Peer-to-Peer(对等模式)

在这种模式下,Agent 之间可以直接通信,不需要一个中心节点来协调。每个 Agent 都可以主动发起对话或请求。A2A(Agent-to-Agent)协议正是为此设计的

什么时候用:Agent 之间需要持续协作、能力互补,比如一个写代码的 Agent 和另一个做代码审查的 Agent。

Hierarchical(层级模式)

这是 Supervisor/Worker 的扩展版,可以在多个层级上嵌套。父 Agent 可以向下级 Agent 委派任务,逐级细化

什么时候用:任务本身就有清晰的多层结构,比如"公司战略规划→部门执行→个人任务"这种天然的上下级关系。

在 Spring AI Alibaba 中,Supervisor 模式具体对应"Agent Tool"模式------Supervisor Agent 将其他 Agent 作为工具来调用,实现集中式任务编排

三、用 Java 实现 Supervisor-Worker 多 Agent 协作

下面用一个"旅游规划助手"的完整例子,演示 Supervisor-Worker 模式的实现。用户输入"我想五一去杭州玩 3 天,预算 5000 元",Supervisor Agent 拆解任务,三个 Worker Agent 并行执行并聚合结果。

3.1 为什么选 Spring AI Alibaba

Spring AI Alibaba 发布于 2025 年 6 月,是国内 Java 领域首个企业级 AI Agent 框架。它内置了 Supervisor、ReAct Agent 等标准模式,支持通过声明式配置实现多 Agent 任务分解与状态同步Graph是 Spring AI Alibaba 的核心组件,负责流程编排、状态流转、并行执行、条件路由等底层能力。在上一篇文章的工具调用基础上,多 Agent 协作可以看作工具调用的"升级版"------把工具换成了 Agent。

3.2 项目搭建

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.10</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.spring</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>1.1.2.0</version>
    </dependency>
    <!-- Jackson 序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>
yaml 复制代码
# application.yml
spring:
  ai:
    dashscope:
      api-key: sk-xxx  # 替换为你的阿里云 DashScope key

3.3 定义子 Agent(Worker Agents)

java 复制代码
// 住宿规划 Agent
@Configuration
public class AccommodationAgent {
    
    @Bean(name = "accommodationAgent")
    public ReactAgent accommodationAgent(@Qualifier("dashscopeChatModel") ChatModel chatModel) {
        return ReactAgent.builder()
                .name("accommodation_agent")
                .model(chatModel)
                .description("住宿规划专家,负责根据目的地、预算和天数推荐酒店或民宿")
                .instruction("""
                    你是一个专业的住宿规划专家。
                    
                    任务:根据用户提供的目的地、预算和旅游天数,推荐合适的住宿方案。
                    
                    输入格式示例:{input}
                    输出要求:
                    1. 推荐的酒店/民宿名称和区域位置
                    2. 预估每晚价格及总预算占比
                    3. 推荐理由(如交通便利性、周边景点)
                    4. 实际预订建议
                    
                    请以结构化的中文文本返回结果。
                    """)
                .outputKey("accommodation")
                .build();
    }
}
java 复制代码
// 行程规划 Agent
@Configuration
public class ItineraryAgent {
    
    @Bean(name = "itineraryAgent")
    public ReactAgent itineraryAgent(@Qualifier("dashscopeChatModel") ChatModel chatModel) {
        return ReactAgent.builder()
                .name("itinerary_agent")
                .model(chatModel)
                .description("行程规划专家,负责设计每日的景点游览路线和活动安排")
                .instruction("""
                    你是一个专业的旅游行程规划专家。
                    
                    任务:根据用户提供的目的地、预算和旅游天数,设计合理的每日行程。
                    
                    输入格式示例:{input}
                    输出要求:
                    1. 按天划分的行程安排,每天包含 3-4 个主要景点
                    2. 每个景点的建议游览时长
                    3. 景点之间的交通建议和大致耗时
                    4. 每日餐饮建议(推荐特色餐厅)
                    5. 每日预算明细(门票、交通、餐饮)
                    
                    请以结构化的中文文本返回结果。
                    """)
                .outputKey("itinerary")
                .build();
    }
}
java 复制代码
// 预算规划 Agent
@Configuration
public class BudgetAgent {
    
    @Bean(name = "budgetAgent")
    public ReactAgent budgetAgent(@Qualifier("dashscopeChatModel") ChatModel chatModel) {
        return ReactAgent.builder()
                .name("budget_agent")
                .model(chatModel)
                .description("预算规划专家,负责根据总预算进行分项预算分配和费用预估")
                .instruction("""
                    你是一个专业的旅游预算规划专家。
                    
                    任务:根据用户提供的总预算和旅游天数,制定详细的费用分配方案。
                    
                    输入格式示例:{input}
                    输出要求:
                    1. 预算拆分:交通(往返)、住宿、餐饮、门票、购物及其他
                    2. 各项预估费用和占比
                    3. 预算控制建议(如省钱技巧、替代方案)
                    4. 应急备用金建议
                    
                    请以结构化的中文文本返回结果。
                    """)
                .outputKey("budget")
                .build();
    }
}

几点注意

  • @Bean 分别注册三个 Worker Agent,各自有独立的 instructionoutputKey
  • 三个 Agent 定义在不同配置类中或用不同 Bean 名称区分
  • outputKey 是占位符引用机制的关键------每个 Agent 的输出会存入 OverAllState 的对应键中

3.4 实现 Supervisor Agent 并配置 Workflow

在 Spring AI Alibaba 中,Graph 是多 Agent 协作的核心引擎。我们需要定义一个工作流,让 Supervisor Agent 按顺序或并行调用 Worker Agent。

java 复制代码
@Configuration
public class SupervisorWorkflowConfig {
    
    @Autowired
    @Qualifier("accommodationAgent")
    private ReactAgent accommodationAgent;
    
    @Autowired
    @Qualifier("itineraryAgent")
    private ReactAgent itineraryAgent;
    
    @Autowired
    @Qualifier("budgetAgent")
    private ReactAgent budgetAgent;
    
    @Bean
    public StateGraph travelPlannerGraph() {
        // StateGraph 是 Spring AI Alibaba 的工作流编排引擎
        // 用于定义 Agent 之间的执行顺序和数据流转
        StateGraph graph = new StateGraph()
                // 添加住宿规划节点
                .addAgentNode("accommodation_planner", accommodationAgent,
                        agentNodeSpec -> agentNodeSpec
                                .instruction("请为以下行程规划住宿方案:{user_input}"))
                // 添加行程规划节点
                .addAgentNode("itinerary_planner", itineraryAgent,
                        agentNodeSpec -> agentNodeSpec
                                .instruction("请为以下行程规划每日安排:{user_input}"))
                // 添加预算规划节点
                .addAgentNode("budget_planner", budgetAgent,
                        agentNodeSpec -> agentNodeSpec
                                .instruction("请为以下行程规划预算分配:{user_input}"))
                // 添加汇总节点(普通节点,非 Agent)
                .addNode("aggregator", this::aggregateResults)
                // 定义执行顺序:三个子 Agent 并行执行
                .addEdge("accommodation_planner", "aggregator")
                .addEdge("itinerary_planner", "aggregator")
                .addEdge("budget_planner", "aggregator")
                // 设置入口和出口
                .setEntryPoint("accommodation_planner")
                .setEntryPoint("itinerary_planner")
                .setEntryPoint("budget_planner")
                .setFinishPoint("aggregator");
        
        return graph;
    }
    
    // 汇总节点逻辑
    private Map<String, Object> aggregateResults(Map<String, Object> state) {
        Map<String, Object> result = new HashMap<>();
        result.put("accommodation", state.get("accommodation"));
        result.put("itinerary", state.get("itinerary"));
        result.put("budget", state.get("budget"));
        result.put("status", "completed");
        
        // 打印完整的规划结果
        log.info("住宿方案: {}", state.get("accommodation"));
        log.info("行程规划: {}", state.get("itinerary"));
        log.info("预算分析: {}", state.get("budget"));
        
        return result;
    }
}
java 复制代码
@Service
public class TravelPlannerService {
    
    @Autowired
    private StateGraph travelPlannerGraph;
    
    public String planTrip(String userInput) {
        // 初始化状态,将用户输入存入 state
        Map<String, Object> initialState = new HashMap<>();
        initialState.put("user_input", userInput);
        
        // 执行工作流
        Map<String, Object> finalState = travelPlannerGraph.execute(initialState);
        
        // 汇总结果
        return buildFinalPlan(finalState);
    }
    
    private String buildFinalPlan(Map<String, Object> state) {
        return String.format("""
                【旅游规划综合方案】
                
                === 住宿建议 ===
                %s
                
                === 行程安排 ===
                %s
                
                === 预算分析 ===
                %s
                
                以上方案综合了住宿、行程和预算三个维度的专业建议,
                请根据个人偏好灵活调整。
                """,
                state.get("accommodation"),
                state.get("itinerary"),
                state.get("budget")
        );
    }
}

3.5 Controller 接入

java 复制代码
@RestController
@RequestMapping("/api/travel")
public class TravelController {
    
    @Autowired
    private TravelPlannerService travelPlannerService;
    
    @PostMapping("/plan")
    public String planTrip(@RequestBody TravelRequest request) {
        return travelPlannerService.planTrip(request.getMessage());
    }
}

3.6 核心机制:占位符与状态传递

在上述代码中,你可能注意到了 {user_input} 这个写法。这正是 Spring AI Alibaba 多 Agent 协作的关键机制------占位符引用

占位符的工作原理 :系统在执行 Agent 的 instruction 时,会自动将 {xxx} 占位符替换为状态(OverAllState)中对应的实际值。这实现了 Agent 之间的数据传递,无需手写胶水代码

占位符 说明 使用场景
{input} 用户输入的原始内容 首个 Agent 或需要用户输入的 Agent
{outputKey} 引用其他 Agent 通过 outputKey 存储的输出 顺序执行中,后续 Agent 引用前面 Agent 的输出
{stateKey} 引用状态中的任意键值 访问状态中的任何数据

这意味着你可以这样串联 Agent:

java 复制代码
// 第一个 Agent:文章写作
ReactAgent writerAgent = ReactAgent.builder()
    .name("writer_agent")
    .instruction("请根据用户的提问进行回答:{input}")
    .outputKey("article")
    .build();

// 第二个 Agent:文章润色,引用第一个 Agent 的输出
ReactAgent reviewerAgent = ReactAgent.builder()
    .name("reviewer_agent")
    .instruction("请对文章进行评审修正:\n{article}")
    .outputKey("reviewed_article")
    .build();

四、进阶:从本地协作到分布式多智能体

在实际生产中,所有 Agent 都部署在同一 JVM 里往往会暴露问题

  • 组织协同困难:不同业务线的 Agent 放在同一个仓库,跨团队协作极其麻烦
  • 可用性难保证:一个 Agent 的故障可能拖垮整个进程
  • 安全风险:内存共享导致权限边界模糊

为了将各 Agent 拆解成独立进程、独立部署,业界引入了 A2A(Agent-to-Agent)协议 。该协议由 Google 开发并捐赠给 Linux 基金会,是一种开放标准,定义了智能体发现、能力描述和任务交互的标准化流程

Spring AI Alibaba 对 A2A 协议有完善的实现,三个核心组件分别是

  • A2A Server:将本地 ReactAgent 暴露为 A2A 服务
  • A2A Registry(注册中心) :支持 Nacos 实现 Agent 注册与发现
  • A2A Discovery(Agent 发现) :消费者从注册中心发现远程 Agent 并调用

五、工程落地注意事项

Agent 粒度是核心设计决策。Agent 不是越细越好。如果粒度过细,Agent 数量会爆炸式增长,编排层逻辑变得极其复杂,通信开销也成倍增加。反之,粒度过粗相当于退回了单 Agent 的老路。通常建议按业务领域切割,比如"订单处理 Agent""物流查询 Agent""售后 Agent",而不是把"查订单状态"和"改订单地址"拆成两个 Agent。

状态传递是关键 。多 Agent 协作的本质是状态在多个 Agent 之间的流转。有了状态,后续 Agent 才能访问前面 Agent 的输出。Spring AI Alibaba 用 OverAllState 统一存储状态,Agent 通过 outputKey 写入,其他 Agent 通过 {outputKey} 占位符读取,整个流程非常顺滑。

容错兜底不可少。Agent 调用是有可能失败的,可能是模型超时、网络抖动,也可能解析失败。建议在 Workflow 中加入超时控制和降级策略,比如某个子 Agent 执行失败时,Supervisor 兜底答复"暂无法获取该项建议"。

成本控制。三个 Agent 并行调用,Token 消耗是正常对话的 3 倍左右。在实际部署时,请根据业务量合理配置限流措施。

六、总结

多 Agent 协作是 AI 应用从"玩具"走向"工程化"的必经之路。

本文要点回顾:

  1. 多 Agent 协作的核心价值在于任务分解 + 能力聚合
  2. 三种主流模式:Supervisor/Worker、Peer-to-Peer、Hierarchical
  3. Spring AI Alibaba 提供 Graph 引擎 + 占位符引用,通过声明式配置即可实现复杂的多 Agent 工作流
  4. A2A 协议让多 Agent 从本地串行升级为分布式协作
  5. 工程化要关注粒度设计状态传递容错兜底成本控制

你的业务里有哪些场景适合用多 Agent 来解决?欢迎在评论区交流。

相关推荐
fliter1 小时前
你想在 Rust 中实现动态库热重载?
后端
用户467245132231 小时前
分布式唯一序列号:万亿级订单不重复的奥秘
后端
XovH1 小时前
第29篇 k8s之Service 与 Endpoints 深入:服务发现原理
后端
右耳朵猫AI1 小时前
Java & JVM技术周刊 2026年第20周
java·开发语言·jvm
人道领域1 小时前
【LeetCode刷题日记】538.把二叉搜索树转换为累加树
java·开发语言·后端·算法·leetcode
铁皮哥1 小时前
【后端开发】什么是守护线程,和普通线程有什么区别?
java·开发语言·数据库·人工智能·python·spring·intellij-idea
西凉的悲伤1 小时前
Spring Boot + ShardingSphere 介绍
java·spring boot·后端·shardingsphere·分库分表
Lsk_Smion1 小时前
力扣实训 _ [33].搜索旋转排序数组 _ [92].翻转链表Ⅱ
java·数据结构·算法
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第86题】【Mysql篇】第16题:MySQL 中锁的种类与行锁实现原理?
java·开发语言·数据库·mysql·面试