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

13 KiB
Raw Blame History

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 个)

  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 后复跑换号场景。