让 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
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 知识库和转人工功能的,关注等更新就行。

相关推荐
ltl2 小时前
康威定律与逆康威定律:组织架构决定系统架构
后端
fliter2 小时前
Go 泛型切片函数:你可能忽略的内存陷阱
后端
实在智能RPA2 小时前
内容运营自动化AI工具选型指南:2026企业级智能内容工程实践全解析
人工智能·ai·自动化·媒体
汀、人工智能2 小时前
AI Compass前沿速览:聚焦 HappyOyster、Qwen3.6-35B-A3B 与 Claude Opus 4.7
人工智能
星爷AG I2 小时前
19-12 语篇理解(AGI基础理论)
人工智能·agi
dc_young2 小时前
【具身任务规划】REVER 与 RoboFarseer:用“可验证奖励”把VLM训练成机器人闭环长任务规划器
人工智能·语言模型·机器人
人工智能AI技术2 小时前
智能体开发路线:从 Demo 到生产环境完整路径
人工智能
快乐非自愿2 小时前
4月AI王炸:GPT-6、量子AI、具身智能,三大风口重构技术未来
人工智能·gpt·重构
G***技2 小时前
面向边缘AI视觉的高性能算力模组解决方案——杰和科技LM2-100-V0深度解析
人工智能·ai边缘计算·嵌入式边缘ai