基于 Spring AI Alibaba 的企业级智能客服系统设计与实现(情绪感知+意图识别+Agent工具链)

情绪感知 + 意图识别 + Agent 工具链,一个能跑的完整方案


与 Spring AI 入门系列的关系

如果你看过我之前的 Spring AI 入门系列,这篇是直接在那基础上升级的。没看过也没关系,这篇能独立看懂。

Spring AI 入门篇 → Spring AI Alibaba 实战指南:能力对比

维度 Spring AI 入门篇(第1-6篇) 本文(Spring AI Alibaba 实战指南)
目标 掌握 Spring AI 核心概念 构建生产级应用
模型 单一 ChatModel 调用 多模块协同:情绪+意图+Agent
工具 单个 @Tool 示例 完整工具链:订单查询+退款+知识检索
记忆 简单历史拼接 Redis 缓存 + 滑动窗口
RAG 基础 VectorStore 业务知识库 + FAQ 检索
架构 单体 Demo 多模块 Maven 工程
Prompt 固定 System Prompt 情绪自适应动态策略
数据 模拟数据 真实数据库 + MyBatis-Plus

入门篇 → 实战指南:到底升级了什么

┌─────────────────────────────────────────────────────────────────┐
│            从 Spring AI 入门 → Spring AI Alibaba 实战           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Spring AI 入门篇掌握的:                                        │
│  - ChatClient 基础调用                                          │
│  - @Tool 工具定义                                               │
│  - VectorStore + RAG 基础                                       │
│  - BeanOutputConverter 结构化输出                               │
│  - Advisors 中间件                                              │
│                                                                 │
│          ══════════════════════════════                          │
│                    ↓ 实战升级                                    │
│          ══════════════════════════════                          │
│                                                                 │
│  本篇新增的:                                                    │
│  - 情绪感知模块(EmotionAnalyzer)                              │
│  - 意图识别模块(IntentClassifier)                             │
│  - 情绪驱动的自适应 Prompt 策略                                 │
│  - 完整 Agent 工具链(订单+退款+知识库)                         │
│  - 多模块 Maven 架构                                            │
│  - MyBatis-Plus Lambda 查询                                    │
│  - Redis 会话缓存                                               │
│  - 企业级异常处理与响应封装                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

一、项目背景与挑战

1.1 传统客服的痛点

在电商、金融、在线教育等行业,客服是连接用户与企业的关键触点。传统客服系统面临以下挑战:

痛点 具体表现 业务影响
人工成本高 7×24 小时客服团队建设 年成本数十万至百万
响应效率低 高峰期排队等待 5-30 分钟 用户流失率上升
质量不一 客服水平参差不齐 投诉率波动大
情感识别缺失 无法感知用户情绪 错失安抚时机,矛盾升级

1.2 AI 客服到底能省多少?

几个我实际接触到的数字:

  • 成本:AI处理80%标准化咨询后,客服团队人力开支降了70%+
  • 响应:原来高峰期排队5-30分钟,AI来了之后响应3秒内
  • 一致性:不会出现"这个客服说的跟那个客服说的不一样"
  • 情绪感知:这个是传统方案做不到的——实时判断用户情绪,愤怒了自动转人工
  • 7×24:不用排班,全天在线

1.3 为什么选 Spring AI Alibaba?

说实话,Java AI生态目前就两个能用的框架:LangChain4j 和 Spring AI。我选 Spring AI Alibaba 的理由很简单:

对比项 自己封装 Spring AI Alibaba
国产模型对接 每家API自己写一遍 开箱即用 DashScope
多模型切换 硬编码,改代码 改配置就行
工具调用 自己实现 Function Calling @Tool 注解搞定
向量存储 自己对接 pgvector 集成 VectorStore
Spring 集成 手动整合 天然无缝

如果你项目本身就是 Spring Boot 体系,没有不选 Spring AI 的理由。

1.4 大模型选了智谱 GLM-4

几个原因:

  • 国产模型,数据不出境,信创合规
  • GLM-4-Flash 便宜,客服场景高频调用,成本必须控制住
  • 128K上下文,复杂对话不会断
  • Function Calling 支持完善,跟Spring AI的Agent对接没有坑

二、系统架构设计

2.1 整体架构图

外部服务

数据层

AI能力层

业务层

接口层

用户终端
Web/APP/微信

ai-csr-api
REST + WebSocket

会话管理
SessionManager

对话编排
ConversationRouter

响应构造
ResponseBuilder

情绪感知
EmotionAnalyzer

意图识别
IntentClassifier

Agent 工具链
@Tool

RAG 知识库
VectorStore

ai-csr-dal
MyBatis-Plus

PostgreSQL
+ pgvector

Redis
会话缓存

智谱 GLM-4
大语言模型

2.2 技术栈全景

┌─────────────────────────────────────────────────────────────┐
│                        表现层                                │
│  Spring Boot 3.5.7 │ REST API │ WebSocket                    │
├─────────────────────────────────────────────────────────────┤
│                       业务层                                 │
│  会话管理 │ 对话编排 │ 响应构造 │ 情绪策略                     │
├─────────────────────────────────────────────────────────────┤
│                       AI 层                                  │
│  Spring AI 1.1.2 │ 智谱 GLM-4 │ 情绪分析 │ 意图识别 │ Agent    │
├─────────────────────────────────────────────────────────────┤
│                       数据层                                 │
│  PostgreSQL │ MyBatis-Plus │ pgvector │ Redis                 │
└─────────────────────────────────────────────────────────────┘

2.3 多模块项目结构

ai-csr/
├── ai-csr-parent/          # Maven 父 POM,统一管理版本和依赖
├── ai-csr-common/          # 公共基础层
│   ├── constant/           # 枚举常量(订单状态、退款状态、情绪等级)
│   ├── exception/          # 统一异常体系
│   ├── result/             # 统一响应封装
│   └── util/               # 工具类
├── ai-csr-dal/             # 数据访问层
│   ├── entity/             # 实体类(对应数据库表)
│   ├── mapper/             # MyBatis Mapper
│   └── service/            # Service 接口与实现(MyBatis-Plus)
├── ai-csr-ai/              # AI 能力层【核心】
│   ├── config/             # Spring AI 配置
│   ├── emotion/            # 情绪感知
│   ├── intent/             # 意图识别
│   ├── agent/tools/        # Agent 工具集
│   └── rag/                # RAG 知识库(待开发)
├── ai-csr-biz/             # 业务逻辑层
│   ├── session/            # 会话管理
│   ├── conversation/       # 对话编排
│   └── order/              # 订单业务(服务门面)
├── ai-csr-api/             # 接口层
│   ├── controller/         # REST 控制器
│   └── dto/                # 请求/响应 DTO
└── ai-csr-app/             # 启动入口
    └── CSRBotApplication   # Spring Boot 主类

三、核心功能实现

3.1 情绪感知模块

用户消息进来之后,先过一遍情绪分析,输出评分和等级,后面的Prompt策略会根据这个等级走不同分支。

@Service
public class ZhipuEmotionAnalyzer implements EmotionAnalyzer {

    private final ChatModel chatModel;

    @Override
    public EmotionResult analyze(String text) {
        // 构建情绪分析 Prompt
        String prompt = buildEmotionPrompt(text);

        // 调用 GLM-4
        String response = chatModel.call(new Prompt(prompt)).getResult().getContent().getText();

        // 解析结果
        return parseEmotionResult(response);
    }

    private String buildEmotionPrompt(String text) {
        return String.format("""
            分析以下用户消息的情绪状态,返回 JSON 格式:
            {"score": 0-100, "level": "CALM|IMPATIENT|ANXIOUS|ANGRY", "keywords": [...]}

            用户消息:%s
            """, text);
    }
}

情绪等级与策略映射

情绪等级 分值范围 System Prompt 策略 自动动作
CALM 75-100 标准客服语气,专业友好
IMPATIENT 50-74 更耐心,主动提供选项 提示解决方案
ANXIOUS 25-49 表达理解,快速响应 主动转高级客服
ANGRY 0-24 表达歉意,避免争论 自动转人工

3.2 意图识别模块

用户说"我的订单到哪了"和"东西坏了要退款",处理逻辑完全不同。所以需要先识别意图,再路由到对应的处理分支。

@Service
public class ZhipuIntentClassifier implements IntentClassifier {

    private final ChatModel chatModel;

    @Override
    public IntentResult classify(String message, ConversationContext context) {
        String prompt = buildFewShotPrompt(message, context);
        String response = chatModel.call(new Prompt(prompt)).getResult().getContent().getText();
        return parseIntentResult(response);
    }

    private String buildFewShotPrompt(String message, ConversationContext context) {
        return """
            你是一个客服意图分类器。请将用户消息分类:

            意图类型:
            - ORDER_QUERY:查询订单状态、物流信息
            - REFUND:申请退款、取消订单
            - RAG:询问产品功能、使用方法
            - HUMAN_TRANSFER:明确要求转人工
            - GENERAL:闲聊、问候

            示例:
            "我的订单到哪了" → {"intent": "ORDER_QUERY", "confidence": 0.95}
            "东西坏了,要求退款" → {"intent": "REFUND", "confidence": 0.92}
            "这个产品怎么用" → {"intent": "RAG", "confidence": 0.88}
            "转人工" → {"intent": "HUMAN_TRANSFER", "confidence": 0.99}

            用户消息:""" + message;
    }
}

3.3 Agent 工具链

识别出意图之后,Agent需要真正去干活——查订单、申请退款、查知识库。Spring AI的@Tool注解让这件事变得特别简单。

@Component
public class OrderQueryTool {

    private final IOrderInfoService orderInfoService;

    @Tool(description = "查询用户订单信息。")
    public String queryOrder(
            @ToolParam(description = "订单号,不提供则返回最新订单") String orderId) {
        String userId = ConversationContext.getUserId();
        // 业务逻辑...
        return formatOrder(order);
    }
}

@Component
public class RefundTool {

    private final IRefundApplicationService refundService;

    @Tool(description = "申请退款。仅退款或退货退款。")
    public String applyRefund(
            @ToolParam(description = "订单号") String orderId,
            @ToolParam(description = "退款金额") BigDecimal amount,
            @ToolParam(description = "退款原因") String reason,
            @ToolParam(description = "退款类型:REFUND_ONLY(仅退款)/ RETURN_AND_REFUND(退货退款)") String type) {
        // 业务逻辑...
        return "退款申请已提交";
    }
}

3.4 订单查询服务

底层用MyBatis-Plus的LambdaQueryWrapper,类型安全,写起来也不啰嗦。

@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
        implements IOrderInfoService {

    /**
     * 根据订单号查询订单
     */
    @Override
    public OrderInfo getOrderEntityByOrderId(String orderId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderId, orderId);
        return getOne(wrapper);
    }

    /**
     * 查询用户的所有订单(按时间倒序)
     */
    @Override
    public List<OrderInfo> getOrderEntitiesByUserId(String userId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getUserId, userId)
               .orderByDesc(OrderInfo::getCreatedAt);
        return list(wrapper);
    }

    /**
     * 校验订单是否属于指定用户
     */
    @Override
    public boolean checkOrderBelongsToUser(String orderId, String userId) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderId, orderId)
               .eq(OrderInfo::getUserId, userId);
        return count(wrapper) > 0;
    }
}

3.5 退款申请服务

退款这块容易出问题——订单不存在怎么办、退款金额超了怎么办、重复申请怎么办。所以校验逻辑比较多,用事务包一层保证数据不出错。

@Service
public class RefundApplicationServiceImpl extends ServiceImpl<RefundApplicationMapper, RefundApplication>
        implements IRefundApplicationService {

    @Autowired
    private IOrderInfoService orderInfoService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RefundApplication applyRefund(RefundApplication applyVO) {
        // 1. 校验订单存在
        OrderInfo order = orderInfoService.getOrderEntityByOrderId(applyVO.getOrderId());
        if (order == null) {
            throw new BizException(ResultCode.NOT_FOUND, "订单不存在");
        }

        // 2. 校验订单归属
        if (!order.getUserId().equals(applyVO.getUserId())) {
            throw new BizException(ResultCode.FORBIDDEN, "无权操作此订单");
        }

        // 3. 校验订单状态(仅已支付或已收货可退款)
        if (!"PAID".equals(order.getStatus()) && !"DELIVERED".equals(order.getStatus())) {
            throw new BizException(ResultCode.BUSINESS_ERROR, "订单状态不支持退款");
        }

        // 4. 校验无重复申请
        if (hasPendingRefund(applyVO.getOrderId())) {
            throw new BizException(ResultCode.BUSINESS_ERROR, "已有待处理退款申请");
        }

        // 5. 校验退款金额
        if (applyVO.getRefundAmount().compareTo(order.getTotalAmount()) > 0) {
            throw new BizException(ResultCode.PARAM_ERROR, "退款金额超限");
        }

        // 6. 创建退款申请
        RefundApplication refund = new RefundApplication();
        // ... 字段赋值
        refund.setStatus(RefundStatus.PENDING.getCode());

        save(refund);
        return refund;
    }

    /**
     * 检查是否有待处理的退款申请
     */
    @Override
    public boolean hasPendingRefund(String orderId) {
        LambdaQueryWrapper<RefundApplication> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RefundApplication::getOrderId, orderId)
               .eq(RefundApplication::getStatus, RefundStatus.PENDING.getCode());
        return count(wrapper) > 0;
    }
}

四、对话流程设计

4.1 完整对话流程

数据库 GLM-4 Agent工具 对话路由 意图识别 情绪分析 会话管理 API层 用户 数据库 GLM-4 Agent工具 对话路由 意图识别 情绪分析 会话管理 API层 用户 发送消息 加载会话上下文 从Redis获取历史 情绪分析 调用模型 EmotionResult 意图识别 调用模型 IntentResult 路由分发 执行工具 查询数据 返回结果 生成回复 回复内容 最终回复

4.2 情绪感知的自适应 Prompt

public class EmotionPromptStrategy {

    public String buildSystemPrompt(EmotionLevel level) {
        return switch (level) {
            case CALM -> """
                你是一个专业友好的客服助手,请耐心解答用户问题。
                """;

            case IMPATIENT -> """
                用户可能有些着急,请:
                1. 快速给出核心答案
                2. 主动提供可能的解决方案
                3. 避免过多解释,简洁明了
                """;

            case ANXIOUS -> """
                用户可能感到焦虑,请:
                1. 首先表达理解和关心
                2. 快速响应,避免让用户等待
                3. 提供明确的行动指引
                4. 如问题复杂,主动建议转高级客服
                """;

            case ANGRY -> """
                用户情绪激动,请:
                1. 首先真诚道歉
                2. 不要争论或辩解
                3. 承认问题,表达改进意愿
                4. 立即转接人工客服处理
                """;
        };
    }
}

五、数据库设计

5.1 核心表结构

-- 会话表
CREATE TABLE conversation_session (
    id              BIGSERIAL PRIMARY KEY,
    session_id      VARCHAR(64) UNIQUE NOT NULL,
    user_id         VARCHAR(64) NOT NULL,
    source          VARCHAR(20) NOT NULL,  -- WEB/WECHAT/APP/API
    status          VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
    last_message_at TIMESTAMP,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 消息表
CREATE TABLE message (
    id          BIGSERIAL PRIMARY KEY,
    session_id  VARCHAR(64) NOT NULL,
    role        VARCHAR(20) NOT NULL,      -- USER/ASSISTANT/SYSTEM
    content     TEXT NOT NULL,
    intent      VARCHAR(50),
    emotion     VARCHAR(20),
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 订单表
CREATE TABLE order_info (
    id              BIGSERIAL PRIMARY KEY,
    order_id        VARCHAR(64) UNIQUE NOT NULL,
    user_id         VARCHAR(64) NOT NULL,
    product_name    VARCHAR(255) NOT NULL,
    total_amount    DECIMAL(12,2) NOT NULL,
    status          VARCHAR(20) NOT NULL,  -- PENDING/PAID/SHIPPED/DELIVERED/COMPLETED/CANCELLED
    logistics_no    VARCHAR(64),
    logistics_corp  VARCHAR(50),
    paid_at         TIMESTAMP,
    shipped_at      TIMESTAMP,
    delivered_at    TIMESTAMP,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 退款申请表
CREATE TABLE refund_application (
    id              BIGSERIAL PRIMARY KEY,
    order_id        VARCHAR(64) NOT NULL,
    user_id         VARCHAR(64) NOT NULL,
    refund_amount   DECIMAL(12,2) NOT NULL,
    refund_type     VARCHAR(20) NOT NULL,  -- REFUND_ONLY/RETURN_AND_REFUND
    refund_reason   TEXT,
    status          VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    processed_at    TIMESTAMP,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 转人工表
CREATE TABLE human_transfer (
    id              BIGSERIAL PRIMARY KEY,
    session_id      VARCHAR(64) NOT NULL,
    user_id         VARCHAR(64) NOT NULL,
    reason          TEXT,
    emotion_level   VARCHAR(20),
    status          VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    assigned_at     TIMESTAMP,
    accepted_at     TIMESTAMP,
    completed_at    TIMESTAMP,
    created_at      TIMESTAMP NOT NULL DEFAULT NOW()
);

六、配置管理

6.1 AI 配置

spring:
  ai:
    zhipuai:
      api-key: ${ZHIPUAI_API_KEY}
      chat:
        options:
          model: glm-4-flash          # 或 glm-4, glm-4-plus
          temperature: 0.7             # 0-2,越低越确定性
          max-tokens: 2048             # 最大生成 token 数

# 情绪分析阈值(可按业务调整)
emotion:
  anger-threshold: 24      # ≤24 分自动转人工
  anxiety-threshold: 49    # ≤49 分主动安抚
  impatient-threshold: 74  # ≤74 分展示更多耐心

# 会话配置
session:
  history-max-rounds: 20   # 记忆窗口轮数
  history-ttl-minutes: 30  # Redis 缓存过期时间

七、跟入门 Demo 比到底强在哪

7.1 对比一览

能力 Spring AI 入门 Demo 本文 AI-CSR
情绪感知 实时评分 + 分级策略
意图识别 关键词匹配 GLM Few-shot 多分类
工具调用 单个示例 完整工具链(订单+退款+知识库)
Prompt 策略 固定 按情绪动态切换
会话记忆 简单拼接 Redis 缓存 + 滑动窗口
项目结构 单体 多模块,职责清晰
数据访问 模拟 MyBatis-Plus Lambda
异常处理 统一异常体系
响应封装 Result 统一响应

7.2 代码上做了什么

  • MyBatis-Plus Lambda 写查询,编译期就能检查,不会写出字段名拼错
  • 退款这类操作用 @Transactional 包了,出问题自动回滚
  • Result 统一包装返回值,接口风格一致
  • BizException + ResultCode 统一异常,不用到处 try-catch

八、后续计划

已完成

  • Spring AI Alibaba 集成智谱 GLM-4
  • 情绪感知模块
  • 意图识别模块
  • Agent 工具链(订单查询、退款申请)
  • 订单查询服务(MyBatis-Plus)
  • 退款申请服务

正在做

  • RAG 知识库模块(文档上传 → 向量入库 → 检索问答)
  • 转人工功能(自动转接 + 手动转接)

后面要做

  • 多轮对话记忆优化
  • 知识库运营后台
  • 客服工作台(人工接待)
  • 数据统计与监控大屏

九、说几句掏心窝的话

这篇文写了挺久的。不是代码多难写,而是怎么把"情绪感知 + 意图识别 + Agent 工具链"这几个模块串起来这件事,我自己踩了很多坑。

比如情绪分析那个模块,最早我用的是关键词匹配——"烦死了"就判ANGRY,结果用户说"太烦人了居然这么好用"也被判成愤怒。后来换成让大模型来做分类,准确率才上来。这种细节文章里没法全展开,但源码里都有注释。

说个实话:光看这篇文,你大概率跑不起来。

不是文章写得不行,而是有些东西天然就塞不进一篇文里——完整的项目目录结构怎么建、模块之间怎么引用、数据库初始化脚本怎么跑、application.yml里那些配置项的坑……这些东西零零散散加起来,比正文还多。

所以我推荐你拿到源码照着跑一遍。跑通的感受,和光看文章的感受完全不一样。


🔥 拿源码的方式

公众号 「亦暖筑序」 底部菜单 【获取源码】,完整的 Gitee 仓库直接拉。

源码里除了文章提到的这些,还有几个文章里没展开的:

  • 数据库初始化脚本(5张表,带模拟数据,跑起来就能测)
  • 完整的多模块 pom 依赖配置(不用自己一个个试版本了)
  • 情绪分析/意图识别的完整 Prompt 模板(文章里只是核心片段,源码里是完整可运行的)
  • Postman 测试集合(一键导入,所有接口直接测)

说白了吧,看完文章理解思路,拿到源码照着跑,这才是最省时间的路径。不拿源码硬看,你会卡在"这行代码放哪个模块"这种低级问题上浪费半天。


💬 想继续聊的

你现在在做什么类型的AI项目?评论区说一句,后面我写文章可以往那个方向靠。

想看这个项目后续怎么加RAG知识库和转人工功能的,关注等更新就行。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐