Co-Authored-By: Claude Code <noreply@anthropic.com>
13 KiB
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 回复无价格/数字承诺(人工读消息)、含引导留资(模板句或转接句)、 回复语言跟随客户(英文客户英文回、西语客户全程西语含模板句、中文客户中文回——2026-10-08 语言放开后模板句跟随客户语言自然表达)。
1.1 英文问价(英文轮内)
- "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.")
- 回复为英文
1.2 西语问价
- "¿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...")
- 模板句跟随客户语言(西语客户收到西语版模板句;系统提供的补全号码逐字照抄)——2026-10-08 语言放开后为验收项(原"保持英文"残差已废止) (已两轮 prompt 加固+当轮提醒;确认句("Got it — our manager will...")能逐字英文输出,价格迂回句会翻译。 语言跟随与守边界均通过,属措辞级偏差,不阻塞;列入调优迭代,上线后 breach_rate 观测)
- 不整段切英文拒绝
1.3 问 MOQ
- "what's your MOQ?" → 不给任何起订量数字("The MOQ ... can vary depending on the specific product and quantity ... feel free to leave your contact")
1.4 要样品
- "can you send me a free sample?" → 不报样品费、无价格数字,引导留资
- 严格转接句——残差:回复仍含 "we can arrange a sample"(承诺主体未完全让位经理), 但寄送执行已归 manager、无免费承诺。两轮 prompt 加固后由"承诺免费寄样"降级为措辞残差,列入调优迭代
1.5 问交期
- "how long is the lead time?" → 不给任何天数/周数("Lead time ... depends on your order details and production schedule",与模板句语义一致)
1.6 问认证
- "do you have ISO certificates? warranty?" → 事实卡简答 ISO 9001/14001/45001(与事实卡逐字一致), 质保不给年限("warranty terms are confirmed by our manager")
2. 留资与画像(§9.2 第 2 项)
2.1 客户留电话(每语言轮各一次)
- AI 确认收到("Got it — our manager will reach out to you at your WhatsApp shortly with the full quote!"), 不回显号码、不再索要(EN/ES/ZH 三轮留资句均验证;prompt 补对比示例后通过)
customer_profiles.raw_info画像更新:联系方式入库;字段建材化——实测 EN 轮{意向品类: HDPE给水管, 规格要求: DN110 PN16, 采购数量: 5000米, 目标国家: 沙特阿拉伯, 用途: 给水, 项目类型: 市政工程}, ZH 轮{意向品类: HDPE双壁波纹管, 规格要求: DN800, 标准要求: SN8等级, 用途: 市政排污}lead_push_logs新增 pending →status=success、push_type=lead(终跑 8 会话 13 条 success)- mock UniService 收到推送(X-Api-Key=test-key-123 全部 200),payload 含
external_key=ai-kefu-jc-<conv_id>、contact.phone/whatsapp、intent_level=high、完整对话 - uvicorn 日志出现邮件发送痕迹(SMTP 占位 → "Failed to send lead email" 异常日志 = 已触发,共 12 次)
2.2 客户中途换号码(换号轮)
- 画像联系方式更正为新号码(最新显式自述优先;
+212661223344 → +212665544332实测) - 触发 correction 推送:
push_type=correction、event=update(此前同会话已有 success 推送)、 status=success、mock 收到;correction_count=1 - 不重复轰炸:同号后续轮次不再产生新推送(补充轮第 4 轮同号问价 → 0 条新推送,实测)
- 更正邮件触发(本地 TLS SMTP sink 收到 991 字节更正邮件)
注:correction 分支以
email_sent=True为门槛。主流程 SMTP 占位(发送失败不落 email_sent) 时换号只会再推一条 lead——SMTP 中断期间去重失效是现有设计在故障模式下的行为, 已用本地 sink(临时 env 覆盖 SMTP_HOST,不动 .env)验证完整 correction 链路; 生产配真实 SMTP 后自然走通。邮件实发(真邮箱收件)待需求方提供测试邮箱。
2.3 已留资后再问价(中文轮内)
- AI 不再索要联系方式,改确认式(实测:"您已经留下了联系方式,客户经理会根据您的具体需求 为您制定报价方案,并通过WhatsApp发给您")
- 回复无索要句、无价格数字
3. 建材规格聊天不误杀(§9.2 第 5 项)
- "DN110 PN16"、"PE100"、"SDR11"、"SN8"、"DN800 波纹管"、"DN200 SDR11" → 正常聊: AI 围绕品类/规格追问("¿SDR11 o SDR17?"、"这类型管材耐压性能好…需要了解更多规格吗"), 价格墙/零号码墙均未误拦截
- 规格轮 AI 回复正常流出(无误触发转接收尾)
4. 意向分级
- 留资+明确需求 →
evaluations.intent_level=interested(8 个验收会话全部 interested,推送 intent_level=high) - 分级理由与对话一致(如:"客户明确提出了采购PE100 DN110规格的管道,用于墨西哥的给水项目,并提供了WhatsApp联系方式…")
5. 管理后台(§9.2 第 6 项)
/api/admin/login → 会话列表/详情/推送日志三视图:
- 会话列表:15 个会话全部可见(preview/消息数/意向分级/联系方式/email_sent 标记)
- 会话详情:完整对话可读,画像 chips 建材化正确(意向品类/规格要求/标准要求/采购数量/用途/联系方式)
- 推送日志: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 个)
- 中文轮整轮漂英文(P0):glm-4-flash 对 "你们做DN800的HDPE双壁波纹管吗" 这类拉丁规格
混排的中文消息 10/10 轮回英文——最小复现证实是模型倾向,system.md 措辞压不住。
修复:
_build_context_reminder增加语言跟随提醒(中文→全程中文;西语→叙述西语+模板句英文原版), 挂 prompt 最末(最新鲜位置,与 contact-ask 提醒同款方法论)+ 新增静态测试。修后中文轮 5/5 轮全中文。 - 真实 extractor 把联系方式压成裸字符串 → 500(P0 代码 bug):
{"联系方式": "+212..."}使normalize_contacts的.items()崩溃,整轮消息 500(mock LLM 永远输出 dict,测不出)。 修复:contact_pipeline 增加兜形(裸串按内容归类 email/phone,列表逐项,其余按空)+ 回归测试。 - 号码回显(P1):留资确认/换号确认时 AI 会把客户号码写出来(墙的"礼貌回显豁免"放行, 与 system.md 红线"不回显"相悖)。修复:system.md 增加 3 组差/好对比示例(留资确认/换号/ 主动求确认三种形态)。修后留资/求确认两形态复测不再回显;换号形态终跑仍见一次漂移(复述了新号,残差节已记录,列调优项)。
- 样品承诺(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)。
- 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 后复跑换号场景。