基于 Spring AI Alibaba 的企业级智能客服系统设计与实现(情绪感知+意图识别+Agent工具链)
基于 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 整体架构图
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 完整对话流程
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知识库和转人工功能的,关注等更新就行。
更多推荐


所有评论(0)