一、背景:为什么需要多Agent协同?

在企业级智能客服场景中,单一大模型Agent往往面临"样样通、样样松"的困境。用户的问题可能涉及订单查询、售后退换、技术支持、财务开票等多个领域,单个Agent既需要理解意图,又需要调用工具,还要处理业务逻辑,结果就是响应慢、准确率低、体验差。

多Agent协同的核心价值:

  • 🎯 专业分工:每个Agent聚焦特定领域,能力更精准
  • 并行处理:复杂任务可拆解为多Agent并行执行
  • 📈 可扩展性:新增业务场景只需添加新Agent,不影响现有系统
  • 🔒 安全可控:不同Agent分配不同数据权限,降低风险

本文将基于 Spring AI + Function Calling 技术栈,手把手带你构建一个企业级多Agent智能客服系统。


二、方案设计:整体架构与角色分工

2.1 系统架构图

┌─────────────────────────────────────────────────────┐
│                    用户请求入口                        │
└────────────────────────┬────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────┐
│              接待 Agent (ReceptionAgent)            │
│  职责:意图识别、情绪分析、任务分发、会话管理          │
└────────────────────────┬────────────────────────────┘
                         │
           ┌─────────────┼─────────────┐
           ▼             ▼             ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│  订单Agent   │ │  售后Agent   │ │  技术支持Agent│
│  OrderAgent  │ │ AfterSaleAgent│ │ SupportAgent │
└──────────────┘ └──────────────┘ └──────────────┘
           │             │             │
           └─────────────┼─────────────┘
                         ▼
┌─────────────────────────────────────────────────────┐
│              Function Calling 工具层                 │
│  订单查询API  │  物流查询API  │  知识库检索         │
└─────────────────────────────────────────────────────┘

2.2 核心角色定义

Agent角色 核心职责 拥有工具
接待Agent 意图识别、情绪感知、任务分发、会话上下文管理 意图分类器、情绪分析
订单Agent 订单查询、物流跟踪、退款进度查询 orderQuery、logisticsQuery
售后Agent 退换货申请、售后政策解答、投诉受理 afterSaleApply、policyQuery
技术支持Agent 产品使用问题、故障排查、解决方案推荐 knowledgeBaseSearch、troubleshoot

三、核心代码实现

3.1 环境依赖与配置

首先添加Spring AI依赖(Maven):

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0-M3</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

Spring AI 配置类:

/**
 * Spring AI 配置类
 * 配置多Agent所需的ChatClient、FunctionCallback等
 */
@Configuration
@Slf4j
public class SpringAiConfig {

    /**
     * 通用ChatClient,用于接待Agent
     */
    @Bean("generalChatClient")
    public ChatClient generalChatClient(OpenAiChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("""
                    你是一个智能客服接待员,需要识别用户意图并分发到对应专业Agent。
                    请用友好、专业的语气与用户交流。
                    """)
                .build();
    }

    /**
     * 订单专用ChatClient,绑定订单相关工具
     */
    @Bean("orderChatClient")
    public ChatClient orderChatClient(OpenAiChatModel chatModel,
                                      OrderQueryFunction orderQueryFunction,
                                      LogisticsQueryFunction logisticsQueryFunction) {
        return ChatClient.builder(chatModel)
                .defaultSystem("""
                    你是专业的订单客服专员,负责处理订单查询、物流跟踪、退款查询等问题。
                    回答要准确、简洁,涉及金额和时间请务必核实。
                    """)
                .defaultFunctions("orderQuery", "logisticsQuery")
                .build();
    }

    /**
     * 技术支持ChatClient,绑定知识库和排障工具
     */
    @Bean("supportChatClient")
    public ChatClient supportChatClient(OpenAiChatModel chatModel,
                                        KnowledgeBaseFunction knowledgeBaseFunction,
                                        TroubleshootFunction troubleshootFunction) {
        return ChatClient.builder(chatModel)
                .defaultSystem("""
                    你是资深技术支持工程师,负责解答产品使用问题、排查故障。
                    请分步骤引导用户排查,必要时提供解决方案。
                    """)
                .defaultFunctions("knowledgeBaseSearch", "troubleshoot")
                .build();
    }
}

3.2 Function Calling 工具实现

以订单查询为例,实现Spring AI的FunctionCallback:

/**
 * 订单查询工具函数
 * 实现FunctionCallback接口,供大模型调用
 */
@Component("orderQuery")
@Slf4j
public class OrderQueryFunction implements FunctionCallback<OrderQueryFunction.Request, OrderQueryFunction.Response> {

    @Override
    public String getName() {
        return "orderQuery";
    }

    @Override
    public String getDescription() {
        return "根据订单号查询订单详情,包括商品信息、金额、状态、下单时间";
    }

    @Override
    public Class<Request> getInputType() {
        return Request.class;
    }

    @Override
    public Response apply(Request request) {
        log.info("调用订单查询接口,订单号: {}", request.orderId());
        
        // 模拟业务逻辑:实际项目中此处调用订单服务API
        if ("ORD123456".equals(request.orderId())) {
            return new Response(
                request.orderId(),
                "Spring AI 实战教程(实体书)",
                new BigDecimal("99.00"),
                "已发货",
                LocalDateTime.of(2026, 6, 10, 14, 30),
                "SF1234567890"
            );
        }
        return new Response(request.orderId(), null, null, "未找到订单", null, null);
    }

    /**
     * 请求参数定义 - 大模型会自动填充此字段
     */
    public record Request(
        @JsonProperty(required = true, value = "orderId")
        @JsonPropertyDescription("订单号,格式为ORD开头+6位数字")
        String orderId
    ) {}

    /**
     * 响应结果定义 - 返回给大模型的结构化数据
     */
    public record Response(
        String orderId,      // 订单号
        String productName,  // 商品名称
        BigDecimal amount,   // 订单金额
        String status,       // 订单状态
        LocalDateTime createTime, // 下单时间
        String trackingNo    // 物流单号
    ) {}
}

物流查询工具实现:

/**
 * 物流查询工具函数
 */
@Component("logisticsQuery")
@Slf4j
public class LogisticsQueryFunction implements FunctionCallback<LogisticsQueryFunction.Request, LogisticsQueryFunction.Response> {

    @Override
    public String getName() {
        return "logisticsQuery";
    }

    @Override
    public String getDescription() {
        return "根据物流单号查询物流跟踪信息";
    }

    @Override
    public Class<Request> getInputType() {
        return Request.class;
    }

    @Override
    public Response apply(Request request) {
        log.info("调用物流查询接口,单号: {}", request.trackingNo());
        
        // 模拟物流信息
        List<TrackingInfo> tracks = List.of(
            new TrackingInfo(LocalDateTime.of(2026, 6, 12, 9, 0), "快件已到达【北京朝阳营业部】,正在派送"),
            new TrackingInfo(LocalDateTime.of(2026, 6, 11, 18, 30), "快件已从【北京转运中心】发出"),
            new TrackingInfo(LocalDateTime.of(2026, 6, 10, 20, 0), "快件已揽收")
        );
        return new Response(request.trackingNo(), "顺丰速运", "派送中", tracks);
    }

    public record Request(
        @JsonProperty(required = true, value = "trackingNo")
        @JsonPropertyDescription("物流单号")
        String trackingNo
    ) {}

    public record Response(
        String trackingNo,
        String carrier,
        String status,
        List<TrackingInfo> trackingHistory
    ) {}

    public record TrackingInfo(LocalDateTime time, String description) {}
}

3.3 Agent 抽象与实现

定义Agent接口:

/**
 * Agent 通用接口
 */
public interface Agent {
    /**
     * 获取Agent名称
     */
    String getName();

    /**
     * 获取Agent描述(用于路由决策)
     */
    String getDescription();

    /**
     * 处理用户消息
     * @param userMessage 用户消息
     * @param context 会话上下文
     * @return Agent响应
     */
    AgentResponse process(String userMessage, ChatContext context);
}

接待Agent实现 - 核心路由逻辑:

/**
 * 接待Agent - 负责意图识别与任务分发
 */
@Component
@Slf4j
public class ReceptionAgent implements Agent {

    private final ChatClient generalChatClient;
    private final List<Agent> specialistAgents;

    @Autowired
    public ReceptionAgent(@Qualifier("generalChatClient") ChatClient generalChatClient,
                          List<Agent> specialistAgents) {
        this.generalChatClient = generalChatClient;
        this.specialistAgents = specialistAgents;
    }

    @Override
    public String getName() {
        return "接待客服";
    }

    @Override
    public String getDescription() {
        return "负责接待用户、识别意图、分发到专业客服";
    }

    @Override
    public AgentResponse process(String userMessage, ChatContext context) {
        // 步骤1:识别用户意图,判断应该路由到哪个专业Agent
        String intentPrompt = buildIntentPrompt(userMessage);
        
        String intentResult = generalChatClient.prompt()
                .user(intentPrompt)
                .call()
                .content();

        log.info("意图识别结果: {}", intentResult);

        // 步骤2:根据意图路由到对应专业Agent
        Agent targetAgent = routeToAgent(intentResult);
        
        if (targetAgent != null) {
            log.info("路由到专业Agent: {}", targetAgent.getName());
            // 将当前上下文传递给专业Agent处理
            AgentResponse specialistResponse = targetAgent.process(userMessage, context);
            
            // 包装响应,添加路由说明
            return AgentResponse.builder()
                    .agentName(targetAgent.getName())
                    .content(specialistResponse.getContent())
                    .routed(true)
                    .build();
        }

        // 步骤3:无法识别意图时,由接待Agent直接回复(澄清问题)
        String reply = generalChatClient.prompt()
                .user("用户问:" + userMessage + ",请礼貌地询问用户具体需要哪方面帮助," +
                        "可选方向:订单查询、售后问题、技术支持")
                .call()
                .content();

        return AgentResponse.builder()
                .agentName(getName())
                .content(reply)
                .routed(false)
                .build();
    }

    /**
     * 构建意图识别提示词
     */
    private String buildIntentPrompt(String userMessage) {
        StringBuilder sb = new StringBuilder();
        sb.append("请分析以下用户问题属于哪个客服类别,只返回类别名称:\n");
        sb.append("用户问题:").append(userMessage).append("\n\n");
        sb.append("可选类别:\n");
        
        for (Agent agent : specialistAgents) {
            sb.append("- ").append(agent.getName()).append(":").append(agent.getDescription()).append("\n");
        }
        
        sb.append("\n如果都不匹配,请返回'接待客服'");
        return sb.toString();
    }

    /**
     * 根据意图识别结果路由到对应Agent
     */
    private Agent routeToAgent(String intentResult) {
        return specialistAgents.stream()
                .filter(agent -> intentResult.contains(agent.getName()))
                .findFirst()
                .orElse(null);
    }
}

订单Agent实现:

/**
 * 订单专业Agent - 处理订单相关问题
 */
@Component
@Slf4j
public class OrderAgent implements Agent {

    private final ChatClient orderChatClient;

    @Autowired
    public OrderAgent(@Qualifier("orderChatClient") ChatClient orderChatClient) {
        this.orderChatClient = orderChatClient;
    }

    @Override
    public String getName() {
        return "订单客服";
    }

    @Override
    public String getDescription() {
        return "处理订单查询、物流跟踪、退款进度、发票开具等订单相关问题";
    }

    @Override
    public AgentResponse process(String userMessage, ChatContext context) {
        log.info("订单Agent处理用户请求: {}", userMessage);
        
        // 调用带Function Calling的ChatClient,自动完成工具调用
        String response = orderChatClient.prompt()
                .user(userMessage)
                // 携带历史会话上下文
                .messages(context.getConversationHistory())
                .call()
                .content();

        return AgentResponse.builder()
                .agentName(getName())
                .content(response)
                .routed(false)
                .build();
    }
}

3.4 会话上下文管理

/**
 * 会话上下文 - 管理多轮对话历史
 */
@Data
@Builder
public class ChatContext {
    /**
     * 会话ID
     */
    private String sessionId;
    
    /**
     * 用户ID
     */
    private String userId;
    
    /**
     * 当前对话历史
     */
    private List<Message> conversationHistory;
    
    /**
     * 当前活跃的Agent
     */
    private String activeAgent;
    
    /**
     * 扩展属性
     */
    private Map<String, Object> attributes;

    /**
     * 添加消息到历史
     */
    public void addMessage(Message message) {
        if (conversationHistory == null) {
            conversationHistory = new ArrayList<>();
        }
        conversationHistory.add(message);
        // 保留最近20条消息,防止上下文溢出
        if (conversationHistory.size() > 20) {
            conversationHistory = conversationHistory.subList(
                conversationHistory.size() - 20, 
                conversationHistory.size()
            );
        }
    }
}

四、运行效果与测试

编写测试用例验证多Agent协同效果:

@SpringBootTest
@Slf4j
class MultiAgentCustomerServiceTest {

    @Autowired
    private ReceptionAgent receptionAgent;

    @Test
    void testOrderQuery() {
        ChatContext context = ChatContext.builder()
                .sessionId("test-001")
                .userId("user-123")
                .conversationHistory(new ArrayList<>())
                .build();

        String userMessage = "帮我查一下订单ORD123456的物流到哪了";
        AgentResponse response = receptionAgent.process(userMessage, context);

        log.info("=== 测试:订单物流查询 ===");
        log.info("用户问题:{}", userMessage);
        log.info("处理Agent:{}", response.getAgentName());
        log.info("回复内容:\n{}", response.getContent());
        
        // 验证:应该路由到订单客服
        assertEquals("订单客服", response.getAgentName());
    }
}

运行输出示例:

=== 测试:订单物流查询 ===
用户问题:帮我查一下订单ORD123456的物流到哪了
处理Agent:订单客服
回复内容:
您好,已为您查询到订单信息:

📦 订单号:ORD123456
🛍️ 商品:Spring AI 实战教程(实体书)
💰 金额:¥99.00
📊 状态:已发货
📅 下单时间:2026-06-10 14:30

🚚 物流信息:
- 承运商:顺丰速运
- 当前状态:派送中
- 物流轨迹:
  06-12 09:00  快件已到达【北京朝阳营业部】,正在派送
  06-11 18:30  快件已从【北京转运中心】发出
  06-10 20:00  快件已揽收

您的包裹正在派送中,请保持电话畅通哦~

五、生产级优化建议

5.1 性能优化

  1. Agent路由缓存:相同意图的对话可直接复用上次路由结果,避免重复意图识别
  2. 流式响应:使用 stream() 替代 call(),提升首字响应速度
  3. 工具结果缓存:相同参数的Function Calling结果可缓存(如订单详情10分钟内有效)
// 流式响应示例
Flux<String> stream = orderChatClient.prompt()
        .user(userMessage)
        .stream()
        .content();

5.2 稳定性保障

  1. 熔断降级:当某个专业Agent不可用时,自动降级由接待Agent兜底
  2. 超时控制:为每个Agent调用设置独立超时时间
  3. 重试机制:Function Calling失败时的自动重试策略

5.3 监控与可观测性

  1. 调用链追踪:记录每个Agent的调用耗时、Token消耗、工具调用次数
  2. 质量评估:定期抽检Agent回复质量,识别bad case
  3. 成本监控:按Agent维度统计Token消耗,优化Prompt降低成本

六、总结

本文基于Spring AI框架,实现了一个完整的多Agent协同智能客服系统。核心技术点包括:

  • ✅ 使用Spring AI ChatClient构建不同角色的Agent
  • ✅ 通过Function Calling实现业务工具与大模型的集成
  • ✅ 设计接待Agent实现智能意图路由
  • ✅ 会话上下文管理支持多轮对话
  • ✅ 生产级优化建议,保障系统稳定运行

多Agent协同是企业级AI应用的必然趋势,Spring AI提供的Function Calling和灵活的ChatClient抽象,让多Agent系统的构建变得更加简单。

如果觉得文章对你有帮助,欢迎点赞👍、收藏⭐、关注👀 一键三连!


参考资料:

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐