# 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 英文问价(英文轮内) - [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...") - [ ] 模板句跟随客户语言(西语客户收到西语版模板句;系统提供的补全号码逐字照抄)——2026-10-08 语言放开后为**验收项**(原"保持英文"残差已废止) (已两轮 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-`、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 后复跑换号场景。