让 AI 客服真能用的 3 个模块:情绪感知 + 意图识别 + Agent 工具链

让 AI 客服真能用的 3 个模块:情绪感知 + 意图识别 + Agent 工具链

Spring AI Alibaba + 智谱 GLM-4,一个能跑的完整方案


先说结论

网上 AI 客服教程一大堆,但基本都是"用户问→大模型答"的套壳 ChatGPT。真落到业务里,这种方案根本没法用。

原因很简单:

  • 用户骂人了,AI 还在礼貌回复 → 用户更火
  • 用户说"订单到哪了"和"东西坏了要退",走的是同一条处理逻辑 → 体验拉胯
  • 没有工具调用,AI 只能说废话 → 什么业务都办不了

这篇文解决这三个问题。用 Spring AI Alibaba + 智谱 GLM-4,串起情绪感知、意图识别、Agent 工具链 三个模块,做一个真能用的 AI 客服。


一、架构长这样

graph TB subgraph 接口层 User[用户终端] API[ai-csr-api<br/>REST + WebSocket] end subgraph 业务层 Session[会话管理] Router[对话编排] Response[响应构造] end subgraph AI能力层 Emotion[情绪感知] Intent[意图识别] Agent[Agent 工具链] RAG[RAG 知识库] end subgraph 数据层 DAL[MyBatis-Plus] DB[(PostgreSQL)] Redis[(Redis)] end User --> API --> Session --> Router Router --> Emotion & Intent & Agent & RAG Agent --> DAL --> DB Session --> Redis

技术栈:Spring Boot 3.5.7 + Spring AI 1.1.2 + 智谱 GLM-4-Flash + MyBatis-Plus + Redis + PostgreSQL


二、模块 1:情绪感知------别让 AI 惹用户更火

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

java 复制代码
@Service
public class ZhipuEmotionAnalyzer implements EmotionAnalyzer {

    private final ChatModel chatModel;

    @Override
    public EmotionResult analyze(String text) {
        String prompt = buildEmotionPrompt(text);
        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);
    }
}

情绪等级怎么用?看这张映射表:

情绪等级 分值 AI 怎么回 自动动作
CALM 75-100 标准客服语气
IMPATIENT 50-74 简洁直接,少废话 提示解决方案
ANXIOUS 25-49 先安抚,再回答 建议转高级客服
ANGRY 0-24 道歉,别争 自动转人工

核心逻辑:愤怒用户交给人工处理,别让 AI 继续火上浇油。

对应的 Prompt 策略:

java 复制代码
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. 立即转接人工客服处理
                """;
        };
    }
}

踩坑提醒:别用关键词匹配做情绪分析。我最早用"烦死了"→ANGRY,结果"太烦人了居然这么好用"也被判成愤怒,尴尬得不行。换成大模型分类之后准确率好很多。


三、模块 2:意图识别------不同问题走不同路

用户说"我的订单到哪了"和"东西坏了要退款",处理逻辑完全不同。意图识别就是干这个的。

java 复制代码
@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;
    }
}

Few-shot 示例是关键。几个样本就能让 GLM-4 的分类准确率到 90%+,比你自己写 if-else 靠谱得多。


四、模块 3:Agent 工具链------让 AI 真正干活

识别出意图后,Agent 得去查订单、退退款、查知识库。Spring AI 的 @Tool 注解让这件事特别简单。

订单查询工具

java 复制代码
@Component
public class OrderQueryTool {

    private final IOrderInfoService orderInfoService;

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

退款申请工具

java 复制代码
@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 "退款申请已提交";
    }
}

底层查询:MyBatis-Plus Lambda

java 复制代码
@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);
    }
}

退款校验逻辑(容易出问题的地方)

退款这块坑多------订单不存在、退款金额超了、重复申请,每一个都得拦住。用事务包一层:

java 复制代码
@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;
}

五、完整对话流程

一次请求的完整链路:

sequenceDiagram participant User as 用户 participant API as API层 participant Session as 会话管理 participant Emotion as 情绪分析 participant Intent as 意图识别 participant Agent as Agent工具 participant GLM as GLM-4 User->>API: 发送消息 API->>Session: 加载上下文(Redis) Session->>Emotion: 情绪分析 → GLM-4 Session->>Intent: 意图识别 → GLM-4 Session->>Agent: 路由分发 → 执行工具 Agent->>GLM: 生成回复 GLM-->>User: 最终回复

六、数据库表设计

5 张核心表,源码里有完整建表脚本 + 模拟数据:

用途
conversation_session 会话管理
message 消息记录(含情绪/意图标记)
order_info 订单数据
refund_application 退款申请
human_transfer 转人工记录

七、关键配置

yaml 复制代码
spring:
  ai:
    zhipuai:
      api-key: ${ZHIPUAI_API_KEY}
      chat:
        options:
          model: glm-4-flash
          temperature: 0.7
          max-tokens: 2048

emotion:
  anger-threshold: 24
  anxiety-threshold: 49
  impatient-threshold: 74

session:
  history-max-rounds: 20
  history-ttl-minutes: 30

GLM-4-Flash 便宜,客服高频调用场景必须控成本。128K 上下文够用。


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

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

说几句掏心窝的话

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

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

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

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

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


源码怎么拿

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

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

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

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


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

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

相关推荐
TMT星球38 分钟前
虎鲸文娱春苗编剧计划全面升级,已孵化20个剧本进入前期开发
大数据·人工智能
奔跑中的小象7 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南7 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang7 小时前
LangGraph 状态机实战
人工智能
陈随易7 小时前
FFmpeg 9.0 发布,代号 Lei,音视频处理再升级
前端·后端·程序员
刹那芳华19927 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作8 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门8 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜8 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟8 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能