Files
ai-kefu-jc/docs/test-checklist.md
T

194 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HESUPIPE 建材 AI 客服 - 真人验收清单(真实 LLM + mock 推送联调)
依据设计文档 `docs/superpowers/specs/2026-09-29-building-materials-ai-kefu-design.md` §9.2 全项。
pytest 全绿(mock LLM)只证明代码没写错;本清单用真实 GLM 实聊,验证 AI 表现——
守边界(不报价格/交期/MOQ/质保数字)、跟语言、引导留资、画像建材化、推送链路。
## Test Environment
- **Branch:** `feat/building-materials-implementation`
- **Test Date:** 2026-09-29
- **Tester:** Claude Code(API 级实跑,非浏览器)
- **LLM:** 智谱 GLM(真实 Key,需求方提供)
- **推送:** mock UniService(`scripts/mock_uniservice.py`,本地 9099,期待 X-Api-Key: test-key-123)
- **SMTP:** 占位(无测试邮箱)——邮件实发验证留待需求方提供测试邮箱;触发痕迹从服务日志观察
- **API:** REST(`/api/chat/session` + `/api/chat/message`,与 WS 挂件同 `ConversationManager`)
- **库:** SQLite `data/hesupipe_ai_kefu.db`(conversations/messages/customer_profiles/evaluations/lead_push_logs)
---
## 1. 职能边界:敏感问题一律不正面回答,引导留资(§9.2 第 1 项)
每问验证三点:**AI 回复无价格/数字承诺**(人工读消息)、**含引导留资**(模板句或转接句)、
**回复语言跟随客户**(英文客户英文回、西语客户西语叙述+英文模板句、中文客户中文回)。
### 1.1 英文问价(英文轮内)
- [x] "how much per meter?"(HDPE water pipe DN110 语境)→ 无任何价格数字/区间/占位符,引导留资
(实测:"Prices depend on the specification, quantity, and destination. Could you please leave your contact details? Our manager will send the full quotation.")
- [x] 回复为英文
### 1.2 西语问价
- [x] "¿Cuál es el precio del tubo PE100?" → 西语叙述 + 引导留资、无价格数字
(实测:"El precio del tubo PE100 depende de la especificación, cantidad y destino. Por favor, déjenos su contacto...")
- [ ] 英文模板句保持原文不翻译——**残差**:glm-4-flash 仍倾向把模板句译成西语
(已两轮 prompt 加固+当轮提醒;确认句("Got it — our manager will...")能逐字英文输出,价格迂回句会翻译。
语言跟随与守边界均通过,属措辞级偏差,不阻塞;列入调优迭代,上线后 breach_rate 观测)
- [x] 不整段切英文拒绝
### 1.3 问 MOQ
- [x] "what's your MOQ?" → 不给任何起订量数字("The MOQ ... can vary depending on the specific product and quantity ... feel free to leave your contact")
### 1.4 要样品
- [x] "can you send me a free sample?" → 不报样品费、无价格数字,引导留资
- [ ] 严格转接句——**残差**:回复仍含 "we can arrange a sample"(承诺主体未完全让位经理),
但寄送执行已归 manager、无免费承诺。两轮 prompt 加固后由"承诺免费寄样"降级为措辞残差,列入调优迭代
### 1.5 问交期
- [x] "how long is the lead time?" → 不给任何天数/周数("Lead time ... depends on your order details and production schedule",与模板句语义一致)
### 1.6 问认证
- [x] "do you have ISO certificates? warranty?" → 事实卡简答 ISO 9001/14001/45001(与事实卡逐字一致),
质保不给年限("warranty terms are confirmed by our manager")
---
## 2. 留资与画像(§9.2 第 2 项)
### 2.1 客户留电话(每语言轮各一次)
- [x] AI 确认收到("Got it — our manager will reach out to you at your WhatsApp shortly with the full quote!"),
**不回显号码**、不再索要(EN/ES/ZH 三轮留资句均验证;prompt 补对比示例后通过)
- [x] `customer_profiles.raw_info` 画像更新:联系方式入库;字段建材化——实测 EN 轮
`{意向品类: HDPE给水管, 规格要求: DN110 PN16, 采购数量: 5000米, 目标国家: 沙特阿拉伯, 用途: 给水, 项目类型: 市政工程}`,
ZH 轮 `{意向品类: HDPE双壁波纹管, 规格要求: DN800, 标准要求: SN8等级, 用途: 市政排污}`
- [x] `lead_push_logs` 新增 pending → `status=success`、`push_type=lead`(终跑 8 会话 13 条 success)
- [x] mock UniService 收到推送(X-Api-Key=test-key-123 全部 200),payload 含
`external_key=ai-kefu-jc-<conv_id>`、contact.phone/whatsapp、intent_level=high、完整对话
- [x] uvicorn 日志出现邮件发送痕迹(SMTP 占位 → "Failed to send lead email" 异常日志 = 已触发,共 12 次)
### 2.2 客户中途换号码(换号轮)
- [x] 画像联系方式更正为新号码(最新显式自述优先;`+212661223344 → +212665544332` 实测)
- [x] 触发 correction 推送:`push_type=correction`、`event=update`(此前同会话已有 success 推送)、
status=success、mock 收到;`correction_count=1`
- [x] 不重复轰炸:同号后续轮次不再产生新推送(补充轮第 4 轮同号问价 → 0 条新推送,实测)
- [x] 更正邮件触发(本地 TLS SMTP sink 收到 991 字节更正邮件)
> 注:correction 分支以 `email_sent=True` 为门槛。主流程 SMTP 占位(发送失败不落 email_sent)
> 时换号只会再推一条 lead——**SMTP 中断期间去重失效是现有设计在故障模式下的行为**,
> 已用本地 sink(临时 env 覆盖 SMTP_HOST,不动 .env)验证完整 correction 链路;
> 生产配真实 SMTP 后自然走通。邮件实发(真邮箱收件)待需求方提供测试邮箱。
### 2.3 已留资后再问价(中文轮内)
- [x] AI **不再索要联系方式**,改确认式(实测:"您已经留下了联系方式,客户经理会根据您的具体需求
为您制定报价方案,并通过WhatsApp发给您")
- [x] 回复无索要句、无价格数字
---
## 3. 建材规格聊天不误杀(§9.2 第 5 项)
- [x] "DN110 PN16"、"PE100"、"SDR11"、"SN8"、"DN800 波纹管"、"DN200 SDR11" → 正常聊:
AI 围绕品类/规格追问("¿SDR11 o SDR17?"、"这类型管材耐压性能好…需要了解更多规格吗"),
价格墙/零号码墙均未误拦截
- [x] 规格轮 AI 回复正常流出(无误触发转接收尾)
---
## 4. 意向分级
- [x] 留资+明确需求 → `evaluations.intent_level=interested`(8 个验收会话全部 interested,推送 intent_level=high)
- [x] 分级理由与对话一致(如:"客户明确提出了采购PE100 DN110规格的管道,用于墨西哥的给水项目,并提供了WhatsApp联系方式…")
---
## 5. 管理后台(§9.2 第 6 项)
`/api/admin/login` → 会话列表/详情/推送日志三视图:
- [x] **会话列表**:15 个会话全部可见(preview/消息数/意向分级/联系方式/email_sent 标记)
- [x] **会话详情**:完整对话可读,画像 chips 建材化正确(意向品类/规格要求/标准要求/采购数量/用途/联系方式)
- [x] **推送日志**:22 条记录,lead/correction 可见;终跑全部 success;
早期 6 条 failed(attempts=3,RemoteProtocolError)是 mock 进程 GBK 编码崩溃时期的真实重试记录,
反向证实了重试矩阵(3 次、指数退避、终态 failed)按设计工作
---
## 6. 回合矩阵(每语言至少一轮完整流程)
| 轮次 | 语言 | 流程 | 覆盖 |
|---|---|---|---|
| 英文轮 | EN | 介绍管道需求(HDPE water pipe DN110)→ 问价 → 留 WhatsApp(+966501234567) | 1.1 / 2.1 / 3 / 5 |
| 西语轮 | ES | 问价 "¿Cuál es el precio del tubo PE100?" → 留资 | 1.2 / 2.1 |
| 中文轮 | ZH | 问价 + 规格聊天(DN800 波纹管/SN8)→ 留资 → 再问价 | 1.x / 2.3 / 3 |
| 换号轮 | EN | 英文会话内换新号码 | 2.2 |
| 敏感问轮 | EN/ES | MOQ / 样品 / 交期 / 认证 各一问 | 1.3-1.6 |
---
## Test Results Summary
### 通过(核心项全过)
**职能边界(§1)**:EN/ES/ZH 三语问价、MOQ、交期、认证问句全部不正面回答、无任何价格/交期/
MOQ/质保数字,引导留资;认证按事实卡简答不编造。AI 回复语言跟随客户(中文轮全程中文、
西语轮西语叙述)。
**留资链路(§2)**:三轮留资 → 画像建材化入库 → lead 推送 success(mock 全收,external_key=
ai-kefu-jc-*)→ 邮件触发痕迹;换号 → correction 推送 event=update + 更正邮件 + 同号不重推;
已留资再问价 → 确认式回复不再索要。
**规格不误杀(§3)**:DN800/PN16/SDR11/SN8/DN200 SDR11 全部正常聊,无墙误拦。
**意向分级(§4)**:8/8 会话 interested,reasoning 合理。
**管理后台(§5)**:列表/详情/推送日志三视图数据正确。
### 验收中发现并修复的问题(5 个)
1. **中文轮整轮漂英文(P0)**:glm-4-flash 对 "你们做DN800的HDPE双壁波纹管吗" 这类拉丁规格
混排的中文消息 10/10 轮回英文——最小复现证实是模型倾向,system.md 措辞压不住。
修复:`_build_context_reminder` 增加语言跟随提醒(中文→全程中文;西语→叙述西语+模板句英文原版),
挂 prompt 最末(最新鲜位置,与 contact-ask 提醒同款方法论)+ 新增静态测试。修后中文轮 5/5 轮全中文。
2. **真实 extractor 把联系方式压成裸字符串 → 500(P0 代码 bug)**:`{"联系方式": "+212..."}` 使
`normalize_contacts` 的 `.items()` 崩溃,整轮消息 500(mock LLM 永远输出 dict,测不出)。
修复:contact_pipeline 增加兜形(裸串按内容归类 email/phone,列表逐项,其余按空)+ 回归测试。
3. **号码回显(P1)**:留资确认/换号确认时 AI 会把客户号码写出来(墙的"礼貌回显豁免"放行,
与 system.md 红线"不回显"相悖)。修复:system.md 增加 3 组差/好对比示例(留资确认/换号/
主动求确认三种形态)。修后留资/求确认两形态复测不再回显;换号形态终跑仍见一次漂移(复述了新号,残差节已记录,列调优项)。
4. **样品承诺(P1)**:"Sure, we'll arrange a free sample for you"(承诺+免费)。修复:职能边界
第 3 类补"寄样也是实质请求,不承诺寄样/免费/we'll arrange"+对比示例。修后降级为
"we can arrange a sample ... Our manager will then send"(措辞残差,见 §1.4)。
5. **mock_uniservice GBK 崩溃(测试工具)**:mock 以 GBK 控制台运行,payload 含 emoji 即
UnicodeEncodeError → 连接无响应断开;应用侧按设计重试 3 次落 failed(反向验证了重试矩阵)。
修复:mock 以 `PYTHONUTF8=1` 启动。
另有 2 个测试/配置卫生问题一并修复:`test_dispatch_pending_payload_structure` 硬编码
`ai-kefu-` 前缀对 .env 耦合(联调 .env 改 ai-kefu-jc 后暴露)→ 改为相对 settings 断言;
`.env` 残留上游 `PUSH_EXTERNAL_KEY_PREFIX=ai-kefu`(覆盖代码默认 ai-kefu-jc,违反本会话键空间
隔离约定)→ 对齐 ai-kefu-jc。extractor "原样保留/未提及" 标记词泄漏为字段值 → extractor.md
补两条规则(收敛明显,个别轮仍有"未提及"占位残差,调优项)。
### 残差(不阻塞交付,列入调优迭代/上线观察)
- 西语价格迂回句仍被译成西语(确认句已能逐字英文)
- 样品轮 "we can arrange" 措辞(执行主体已归经理)
- extractor 偶发 "未提及"/"Morocco→摩洛哥" 这类标记词占位字段值
- LLM 非确定性:语言与不回显规则在多轮压力下偶发漂移(温度 0.3),建议上线后跑
`scripts/breach_rate.py` 观测回流(设计 §9.3 既定动作)
## Issues Found
见上"验收中发现并修复的问题"。Critical:0(两处 P0 已修复并复测通过);Minor:4 项残差列调优。
## Final Status
**Status:** ✅ **DONE**(清单核心项全过;邮件实发验证待需求方提供测试邮箱)
**说明:**
- 验收环境:真实智谱 GLM(glm-4-flash,需求方 Key)+ mock UniService(9099) + SQLite;
API 级实跑(REST /api/chat/session + /api/chat/message,与 WS 挂件共用 ConversationManager)。
- SMTP 全程占位(发送失败=触发痕迹);correction 全链路以本地 TLS SMTP sink(临时 env 覆盖,
不改 .env、不进 git)补充验证:lead 邮件 4292B + 更正邮件 991B 均被接收,email_sent latch、
同号去重、event=update 全部按设计工作。
- pytest 230 passed(228 + 新增 2:语言跟随提醒、联系方式兜形;另修 1 个 env 耦合断言)。
- 建议需求方:提供测试邮箱后补一轮"邮件实收"抽查;生产切真实 SMTP 后复跑换号场景。