从接口压测到全链路质量保障:AI智能客服系统的软件测试实践

一、AI智能客服系统 的测试挑战

智能客服系统远不止普通的 CRUD 业务系统,它是一个典型的 长连接 + 高并发 + AI 推理链路 + 分布式路由 复合体。任何一个环节异常,用户侧感知往往就是"卡死""丢消息""答非所问"。

对测试工程师而言,这套系统存在四个核心难点:

难点 典型表现 对测试的冲击
多渠道接入 WebSocket 主通道 + 第三方 IM SDK 回调 同一套消息逻辑要走完全不同的接入路径
多实例部署 服务水平扩容,连接分散在不同节点 消息可能发到 A 实例,但用户连在 B 实例,需要跨实例路由
AI 链路复杂 敏感词 → 意图识别 → 知识检索 → 大模型生成 每一步都有外部依赖,失败模式呈指数级增长
响应式全栈 WebFlux + Reactor + R2DBC 传统断言方式失效,ThreadLocal 不可用,调试门槛陡增

一句话概括这类系统的测试本质 :在异步、非阻塞、分布式的条件下,保证消息 不丢、不重、不乱序、不超时


二、被测系统技术特征概览

2.1 核心技术栈(通用化)

技术选型 说明
JDK Java 21 LTS,虚拟线程就绪
Web 框架 Spring Boot 3.x + Spring WebFlux 响应式编程模型
服务治理 Spring Cloud + Nacos 注册中心 / 配置中心
RPC Dubbo + Triple(基于 gRPC) 服务间高效通信
数据库 PostgreSQL / MySQL R2DBC 非阻塞访问
缓存 / 消息 Redis(Hash / Stream / 分布式锁) ReactiveRedisTemplate
向量检索 云厂商向量数据库 用于 RAG 知识检索
大模型 主流 LLM 云服务 支持流式输出
认证鉴权 响应式 Sa-Token Reactor Context 传递登录态
对象存储 云厂商 OSS 文件上传 / 下载
调度 XXL-Job 分布式定时任务
监控 Micrometer + Prometheus + 分布式追踪 指标采集与链路追踪
压测工具 自研 Python WebSocket 压测客户端 200 并发 / 200 QPS

2.2 测试关注点映射

模块 核心职责 测试重点 风险等级
会话模块 WebSocket 接入、消息分发、跨实例路由 连接管理、消息可靠性、心跳超时、多端路由 🔴 P0
AI 编排 意图识别、RAG、回复生成、转人工决策 三级降级链路、节点分支断言、策略切换 🔴 P0
LLM 服务 大模型对话 流式输出完整性、取消语义、超时降级 🟡 P1
RAG 模块 向量检索 置信度阈值、多分区并发、命中 / 未命中 🟡 P1
网关 统一入口、限流、鉴权 路由规则、限流阈值、Token 透传 🟡 P1
权限中心 认证、RBAC、部门数据隔离 登录态、权限拦截、上下文透传 🟡 P1
运营后台 前端管理页面 前后端接口契约、权限渲染 🟢 P2

三、IM 核心全链路流程(测试视角)

3.1 消息从发送到接收的完整链路

复制代码
用户浏览器 / APP
    │  WebSocket 连接(wss://域名/ws/chat)
    ▼
WebSocket 接入层
    │  接收 TEXT 帧 → 解析为内部消息对象
    ▼
消息分发器(Dispatcher)
    │  按 userType + messageType 路由到对应处理器
    ▼
用户消息处理器(UserMessageProcessor)
    │
    ├─ 1. 敏感词检测
    │       └─ 命中 → 替换为 ***
    │
    ├─ 2. AI 回复策略选择
    │       ├─ 热门话术缓存命中 → 直接返回
    │       ├─ 本地缓存命中 → 直接返回
    │       └─ 未命中 → 调用大模型
    │
    ├─ 3. AI 编排工作流执行
    │       │
    │       ▼
    │   StateGraph 状态图驱动
    │       ├─ 意图识别节点
    │       ├─ 知识库检索节点(RAG)
    │       ├─ 回复生成节点
    │       ├─ 转人工节点
    │       └─ 兜底回复节点
    │
    ├─ 4. 大模型服务调用
    │       │
    │       ▼
    │   流式返回 Flux<String>
    │       逐 token 推送到前端
    │
    ├─ 5. 消息持久化
    │       │
    │       ▼
    │   R2DBC → PostgreSQL / MySQL
    │
    └─ 6. 消息推送路由
            │
            ├─ 本地连接 → 直接内存推送
            ├─ Redis 连接缓存命中 → 写入消息通道
            └─ 跨节点 → Redis Stream 广播
                    │
                    ▼
            WebSocketSession.send() → 客户端接收

3.2 必须覆盖的 12 条分支清单

编号 分支场景 测试类型 预期结果
1 匿名连接发送消息 功能 不走分布式路由,Handler 直发,返回提示登录
2 已登录连接发送消息 功能 走完整业务编排链路
3 本机连接消息发送 功能 直接内存推送,不写 Redis
4 跨实例连接消息发送 集成 写入 Redis Stream,目标实例消费并推送
5 单端在线 功能 正常收发
6 多端同时在线(PC + 微信) 功能 消息推送到所有在线端,不漏不重
7 热门话术命中 功能 返回热门话术列表,不调 AI
8 本地缓存命中 功能 返回缓存回复,不调 AI
9 大模型策略触发 集成 调 AI 服务链路,流式返回
10 敏感词命中 功能 消息脱敏替换,原始内容仍持久化
11 排队转人工 功能 分布式锁互斥,仅一个请求能进入转人工
12 AI 超时降级 容灾 返回兜底话术,不阻塞用户

四、测试方案分层设计(七层模型)

层级 工具 / 手段 覆盖范围 示例
L1 单元测试 JUnit 5 + Mockito + Reactor StepVerifier 单个类 / 方法的分支覆盖 AI 策略三分支逻辑
L2 切片测试 @WebFluxTest / @DataR2dbcTest Web 层路由、R2DBC Repository WebSocket 握手、消息持久化
L3 集成测试 Testcontainers(PG / Redis / 注册中心)+ Dubbo 本地调用 跨模块端到端编排 会话 → AI 编排 → LLM 完整链路
L4 接口测试 Postman / Newman / Shell 脚本 REST 接口契约 登录、会话 CRUD、知识库管理
L5 WS 协议测试 自研 Python WebSocket 客户端 长连接入参 / 出参 / 时序 握手鉴权、消息推送、心跳
L6 性能压测 自研 Python 压测工具 200 并发 / 200 QPS RT / P95 / P99 / 可用率
L7 容灾测试 手动 / 自动化故障注入 跨实例消息可达性 实例宕机、Redis 抖动、网络隔离

五、重点一:WebSocket 自动化测试设计

5.1 测试设计思路

WebSocket 测试的核心难点在于:它是异步双向流,不是请求-响应模型。需要同时验证:

  • 服务端能正确接收客户端消息
  • 服务端能主动推送消息到客户端
  • 心跳机制正常工作
  • 断连后资源正确释放

5.2 响应式流断言(StepVerifier)

测试目标:验证 WebSocket 文本消息被正确处理并分发。

文字描述测试方案

  1. 准备阶段 :构造一条模拟的 TEXT 帧,内容为 JSON 格式的消息体(如 {"type":"***","content":"你好"}
  2. 执行阶段 :将该消息传入 Flux.just() 模拟客户端输入流
  3. 断言阶段 :使用 StepVerifier 验证输出流:
    • 第一条消息应为 AI 回复类型
    • 消息内容包含预期的回复文本
    • 流在超时时间内正常完成

关键代码片段思路

java 复制代码
// 1. 构造输入 Flux
Flux<WebSocketMessage> input = Flux.just(mockTextMessage(jsonPayload));

// 2. 调用被测 Handler 获取输出 Flux
Flux<WebSocketMessage> output = handler.handle(input);

// 3. StepVerifier 断言
StepVerifier.create(output)
    .assertNext(msg -> {
        String payload = msg.getPayloadAsText();
        assertTrue(payload.contains("AI_REPLY"));
    })
    .expectComplete()
    .verify(Duration.ofSeconds(5));

5.3 心跳与断连测试

测试目标:验证 PING/PONG 心跳机制与超时断连。

测试方案

  1. 模拟客户端连接后不发送任何 PING 帧
  2. 验证服务端在配置的心跳超时时间后主动关闭连接
  3. 验证 Redis 中的连接记录被正确清理

关键断言逻辑

  • 使用 expectNoEvent(Duration) 验证在心跳间隔内无服务端主动消息
  • 超过超时时间后,连接状态变更为 CLOSED
  • Redis 中 key:{value} 对应的 Hash 记录被删除

5.4 多端消息同步测试

测试目标:验证同一用户多端在线时,消息推送到所有终端。

测试方案

  1. 模拟同一用户 ID 建立两个 WebSocket 连接(PC 端 + 移动端)
  2. 向该用户发送一条消息
  3. 验证两个连接都收到推送
  4. 验证消息内容一致、不重复

六、重点二:AI 编排工作流测试

6.1 意图三级降级链路

意图识别是 AI 客服的核心决策点,采用 三级降级 策略:

复制代码
Level 1: 向量匹配(向量数据库语义检索)
    ├─ 命中(score ≥ 阈值) → 直接使用向量匹配结果
    └─ 未命中 ↓

Level 2: 小模型意图分类
    ├─ 置信度 ≥ 阈值 → 使用小模型结果
    └─ 置信度 < 阈值 ↓

Level 3: 大模型兜底(LLM 直接判断)
    └─ 最终结果
测试设计思路

采用 数据驱动 + Mock 控制 的方式,固定 expectedIntent,通过 Mock 控制每一级的输入和输出。

Level 1 向量命中场景

测试步骤

  1. Mock 向量数据库客户端,使其返回高置信度结果(如 score=0.92,阈值=0.85)
  2. 构造输入文本:"我的余额是多少"
  3. 执行意图识别节点
  4. 断言
    • 返回的意图为 query_balance
    • 意图来源标记为 VECTOR_MATCH
    • 不再调用 Level 2 和 Level 3
Level 2 小模型命中场景

测试步骤

  1. Mock 向量数据库返回空(未命中)
  2. Mock 小模型返回中等置信度结果(如 confidence=0.78,阈值=0.7)
  3. 构造输入文本:"我要转账"
  4. 执行意图识别节点
  5. 断言
    • 返回的意图为 transfer_money
    • 意图来源标记为 SMALL_MODEL
    • 不再调用 Level 3
Level 3 大模型兜底场景

测试步骤

  1. Mock 向量数据库返回空
  2. Mock 小模型返回低置信度结果(如 confidence=0.35,阈值=0.7)
  3. Mock 大模型返回 JSON 格式意图判断:{"intent": "complaint", "reason": "服务态度"}
  4. 构造输入文本:"你们什么破服务"
  5. 执行意图识别节点
  6. 断言
    • 返回的意图为 complaint
    • 意图来源标记为 LLM_FALLBACK

6.2 RAG 命中 / 未命中分支测试

RAG 命中场景

测试步骤

  1. Mock 向量数据库返回高分知识片段(score=0.88,阈值=0.75)
    • 内容:"7天无理由退货"
    • 内容:"退货需保留包装"
  2. 构造输入文本:"怎么退货"
  3. 执行 RAG 节点
  4. 断言
    • 上下文中包含知识库内容
    • isKnowledgeHit 标记为 true
    • 后续进入知识增强回复节点
RAG 未命中场景

测试步骤

  1. Mock 向量数据库返回低分结果(score=0.21)
  2. 构造输入文本:"我想去火星旅游"
  3. 执行 RAG 节点
  4. 断言
    • isKnowledgeHit 标记为 false
    • shouldCallLLM 标记为 true
    • 后续进入大模型直答节点

6.3 转人工决策测试

测试目标:投诉类语句触发转人工流程。

测试步骤

  1. 准备启用转人工的 Bot 配置,设置投诉关键词列表(如"投诉""差评""垃圾")
  2. 构造输入文本:"我要投诉你们的服务态度"
  3. 执行转人工节点
  4. 断言
    • 节点状态变更为 TRANSFER_TO_HUMAN
    • 事件列表中包含 TransferToHumanEvent
    • 转人工服务层的 initiateTransfer 方法被调用一次

七、重点三:跨实例消息路由测试

7.1 跨实例路由架构

复制代码
用户 A 连接 → 实例 A(WebSocket 接入)
                    │
                    │ 消息目标用户在实例 B
                    ▼
           消息分发器判断路由
                    │
          ┌─────────┴─────────┐
          │ 目标在本地         │ 目标在远端
          │ → 直接推送         │ → 写 Redis Stream
          └───────────────────┘              │
                                             ▼
                              实例 B 消费 Stream → 推送用户 B

7.2 Redis Hash 连接存储测试

Redis Key 结构设计

复制代码
Key:   im:user:channels:{userId}
Type:  Hash
Field: {channelType}:{deviceId}
Value: JSON {instanceId, sessionId, channelType, deviceId}
TTL:   24h(自动过期,防止僵尸连接)

测试用例设计

步骤 操作 预期结果
1 注册连接(userId, instanceId, sessionId, channelType, deviceId) 返回 true
2 查找连接(userId) 返回完整的 ChannelInfo 对象
3 验证 TTL 接近 24h(允许误差)
4 注销连接(userId, field) 返回 true
5 再次查找 返回空

八、性能与稳定性测试

8.1 压测方案设计

采用自研 Python WebSocket 压测工具,支持以下模式:

模式 并发 QPS 持续时间 目的
标准压测 200 200 10 分钟 验证常规负载能力
长稳压测 50 50 24 小时 验证内存泄漏 / 连接稳定性
降级专项 100 100 5 分钟 模拟 AI 超时,验证降级话术
峰值冲击 500 500 1 分钟 验证限流与熔断策略

压测核心指标

  • 连接建立成功率:目标 ≥ 99.9%
  • 消息往返延迟(RT):P95 < 500ms
  • P99 延迟:< 2s
  • 错误率:< 0.1%
  • 流式首字延迟(TTFT):< 1.5s

8.2 监控指标与告警阈值

指标 采集路径 告警阈值 说明
接口 QPS /actuator/prometheus P95 > 500ms 告警 IM 接口响应时间
AI 接口 RT Prometheus 指标 P95 > 3s 大模型调用延迟
WebSocket 在线数 自定义指标 抖动 > 20% 连接稳定性
Redis Stream 积压 XPENDING > 1000 条 消费能力不足
JVM GC 暂停 jvm_gc_pause_seconds 单次 > 200ms 内存压力
错误率 自定义错误计数 > 0.1% 整体健康度

8.3 分布式追踪排查

TraceId 串联排查流程

  1. 从 Prometheus / Grafana 发现异常请求,获取 TraceId

  2. 在各服务日志中按 TraceId 过滤,按时间排序

  3. 还原完整调用链:

    网关层 | 14:23:01.100 | 收到用户请求 | userId=10086
    会话层 | 14:23:01.105 | WebSocket 消息解析完成
    AI 编排层 | 14:23:01.110 | 意图识别开始 | input="查余额"
    向量检索 | 14:23:01.115 | 向量搜索完成 | topK=5
    AI 编排层 | 14:23:01.130 | 意图=query_balance | source=VECTOR
    LLM 服务 | 14:23:01.135 | 大模型流式开始 | model=qwen-plus
    会话层 | 14:23:01.280 | 推送 AI 回复 | chunks=15


九、典型故障排查指南

9.1 场景一:消息丢失

排查路径

复制代码
1. 确认消息是否到达 WebSocket 层
   → 查看 WebSocket Handler 日志,确认收到 TEXT 帧

2. 确认消息是否进入分发器
   → 查看 Dispatcher 路由日志

3. 确认路由结果
   → 查看路由日志中的标记:LOCAL / REDIS / STREAM

4. 如果走了 STREAM → 检查消费者组是否在线
   → XINFO GROUPS 消费者key

5. 如果消费者在线 → 检查 Pending 是否堆积
   → XPENDING  消费者key 消费组key

6. 如果 Pending 堆积 → 消费者处理阻塞
   → 检查消费者线程池 / 下游服务延迟

9.2 场景二:转人工死锁

排查路径

复制代码
1. 查看 Redis 中的锁状态
   → GET lock:transfer:human:{会话Id}
   → TTL lock:transfer:human:{会话Id}

2. 如果 TTL = -1(永不过期)
   → 看门狗续期逻辑异常,锁永远不会释放
   → 修复方向:unlock 放入 doFinally() 确保执行

3. 如果 TTL 正常倒计时但锁未释放
   → 业务异常导致 unlock 未执行
   → 修复方向:增加锁持有时间上限(强制过期)

4. 如果 Redis 主从切换
   → DEL 命令可能丢失
   → 修复方向:使用 Redlock 或增加重试机制

9.3 场景三:AI 回复超时

排查路径

复制代码
1. 确认超时发生在哪一层
   → 网关超时?AI 编排超时?LLM 调用超时?

2. 查看 LLM 服务日志
   → 是否收到请求?是否开始流式输出?

3. 查看 AI 编排状态机
   → 是否触发了降级节点?
   → 降级话术是否正确返回?

4. 验证超时配置
   → WebClient 超时 < AI 编排超时 < 网关超时
   → 确保每层都有超时保护,不会无限等待

10.2 测试经验

  1. 响应式代码必须用 StepVerifier :传统 assertEquals 无法断言异步流的行为,StepVerifier 是标配
  2. Redis Stream 是跨实例消息的命脉:Pending 监控、ACK 机制、Stream 修剪缺一不可
  3. AI 链路必须三级降级:向量 → 小模型 → 大模型,任何一级都要有超时和兜底
  4. 分布式锁必须有看门狗:没有自动续期的锁在生产环境一定会出问题
  5. 压测要分档位:短促高压验证极限,长稳低压验证泄漏,两者不可互相替代
  6. TraceId 是排查的核武器:从网关到 LLM 全链路串联,5 分钟定位问题根因

写在最后 :智能客服系统的测试,本质上是在 异步、分布式、AI 依赖 三重不确定性中,用工程化的手段建立确定性。分层测试是骨架,Mock 是肌肉,监控是可观测的神经------三者缺一不可。希望本文的实战经验能给正在做类似系统的测试同学一些参考。

相关推荐
声讯电子1 小时前
实测A-59F音频模组:降噪稳、不啸叫、通话清晰
人工智能·语音识别·ai降噪·usb接口·回音消除
神奇霸王龙1 小时前
2026旗舰六阶段MCP流水线实战指南
人工智能·ai·ai作画·prompt·aigc·mcp
山河不见老1 小时前
【Cursor 、Qoder安装问题】Cursor 、Qoder安装卡在“正在准备安装”问题排查及解决
人工智能·windows·编辑器
新知图书1 小时前
2.3 开发工作流
人工智能·agent·ai agent·智能体
tuanxiang1 小时前
用免费 AI 改写工具处理批量文案的踩坑记录,附完整降噪脚本
人工智能
小邓的技术笔记1 小时前
DeepSeek 大模型落地应用与场景实战指南
大数据·人工智能
石榴1 小时前
从 Codex + Obsidian 到本地 RAG:我的私人知识库实践
人工智能
QZSJTR1 小时前
技术解析|从SEO到GEO:生成式搜索时代实体企业数字化适配方案
人工智能
安逸sgr1 小时前
如何减少 Prompt 引起的幻觉和答非所问?
人工智能·ai·大模型·prompt·agent·智能体