49c0e5559c
Co-Authored-By: Claude Code <noreply@anthropic.com>
7.4 KiB
7.4 KiB
改动文档:客户已留资后不再重复索要联系方式
- 日期:2026-09-08
- 分支:
chany_dev_dev - 状态:160 项测试全绿(基线 133,净增 27);真机 GLM 冒烟未执行(清单见 §6,部署前置项)
- 实施档案:superpowers/plans/2026-09-08-stop-reasking-contact.md(经独立评审两轮+自查实证一轮收敛;执行期评审又抓出 4 处计划级缺陷,见 §5)
1. 背景与问题
客户已发 WhatsApp 号码后继续聊,AI 仍反复索要 WhatsApp/邮箱(生产实测:客户被迫三次重发同一号码,AI 五次开口要号)。逐轮取证定位六条根因:
| # | 根因 | 一句话 |
|---|---|---|
| 1 | 捕获缺口 | 倒装("0712 345 678 is my whatsapp")/无is/msg-me/wa冒号等形态正则漏收,extractor 再漏则画像恒空,全防线失效 |
| 2 | 同轮竞态 | 提醒/has_contact 用上一轮画像,发号当轮完全不设防 |
| 3 | App 脚本打架 | 已留资仍无条件注入逐字"Drop your WhatsApp"脚本,与同提醒里"禁止再索要"互相矛盾(GLM 听逐字不听抽象) |
| 4 | 触发词过宽 | "I'll call you later" 误命中索要触发 → 莫名注入要号脚本 |
| 5 | 零号码墙自破 | 墙替换句不看 has_contact,文案本身就是索要——回显形态稍有出入即在刚发号后再要一次 |
| 6 | 生产日志主因 | 4 次违规索要全部是 GLM 自由生成(逐字背 system.md 留资模板),确定性门控零拦截,提醒禁令被无视 |
2. 三层实现
2.1 数据层(尽早尽全地看见"已留资")
- 同轮即时扫描(conversation.py):构建提醒前对当前消息跑一次纯正则
scan_user_messages,只并入本轮内存视图(提醒/has_contact 路由),不落库——持久化与邮件触发时序零变化 - 正则补四形态(contact_pipeline.py):倒装(仅显式渠道词 whatsapp/wa/wechat/微信,防"2500000 is my number one choice"排位表述误绑)、
my number X(无is)、text/msg/ping me X、wa: X、中文倒装"13800138000 我的微信" - key 模糊归一:精确别名优先 + contains 兜底——extractor 即兴 key("联系电话"/"WhatsApp账号")不再静默丢值
2.2 提醒/过滤层(拆掉三条确定性索要引擎)
- App 提醒门控:已留资(confirmed/suspect)时索要脚本换成"经理会主动联系你"版,与"禁止再索要"不再打架;web 分支不动
- 零号码墙路由:已留资时替换句用新常量
NO_CONTACT_ALREADY(F6 同款修法) - 再索要墙(核心新增):
has_contact时输出流中的索要句逐句静默丢弃(丢句不掐断);整条回复全是索要句时兜底流出NO_CONTACT_ALREADY防空回复;模式集与 system.md 全部留资模板+生产日志 4 句原话逐字对齐(实证 13 命中/12 放行);未留资闸门全关——首次合法索要不受影响 - 触发词收窄:第一人称将来时(
i'll/i will/let me,含弯引号变体)定宽负向后顾排除
2.3 prompt 层(软加固)
- system.md"假称已留过号"改为先回看记录:确有号码→确认收到绝不要求重发
- 提醒"已提供"段附逐字话术(直接引用
NO_CONTACT_ALREADY常量,与墙替换句同源不漂移)
3. 文件清单
| 文件 | 动作 |
|---|---|
app/services/conversation.py |
同轮扫描/App门控/墙路由/再索要墙接线/触发词/逐字话术 |
app/services/contact_pipeline.py |
正则四形态+倒装正则+key模糊归一(含 PHONE_ADJACENCY_SCAN_RE 仅扫描层) |
app/services/boundary_filter.py |
NO_CONTACT_ALREADY + REASK_PATTERNS/asks_contact |
app/prompts/system.md |
先查记录话术(1 处) |
tests/test_contact_pipeline.py |
+11 |
tests/test_lead_email_trigger.py |
+13 |
tests/test_boundary_filter.py |
+3 |
前端零改动、无 schema、无新配置。提交序列(逐个可回滚):
06a758f 同轮扫描 → 2ec78cd+cecb804 正则扩充(+评审修正) → 3f87f03 key归一 → 7ccef5a App门控 → 519d280 墙路由 → 92a5bb6+694246b+7b96491 再索要墙(+评审修正) → ca935db 触发词 → 7492274 prompt → bc4e02b 逐字话术 → 0aa0cef 端到端。
4. 效果对账(对生产日志场景)
用 2026-09 生产日志逐句重放:首次问价要号(合法)保留;后续 4 次违规索要——App 脚本/墙替换句类由 §2.2 门控根除,GLM 自由生成类由再索要墙确定性丢弃;客户仅需发一次号码。三条索要引擎(价格墙替换句、App脚本/零号码墙替换句、GLM 生成句)全部确定性关闭,残余仅剩"正则未覆盖表述的当轮失守(下一轮兜底)"与"再索要墙模式漏拦(GLM 改写措辞)"。
5. 执行期评审抓出的计划缺陷(记录防重蹈)
- 计划原版裸
my number X分支放进共享正则会授予latest_stated_contacts覆盖权,击穿改号更正邮件链(实证复现['lead'] != ['lead','fix','fix'])→ 拆为仅扫描层PHONE_ADJACENCY_SCAN_RE whats app双空格/NBSP 变体 KeyError 会崩溃每消息热路径 → 归一化 helper + 回归测试- 计划的 web 分支测试断言
"...WhatsApp button" in text.lower()永不通过(needle 大写)→ 修正 - 再索要墙首版无闸门反向钉死/全丢兜底 → 补三测试(闸门双向变异验证)
6. 已知限制与残余(原样转录计划"残余风险")
- 同轮扫描仍是正则——未覆盖表述当轮失守,下一轮轮末管道兜底;正则永远不是全集
- prompt 层不可单测——GLM 依从性靠真机冒烟;确定性防线才是主修
NO_CONTACT_ALREADY英文单语——与既有 WEB/APP 常量一致(如需中英路由属独立增强)- 墙路由 confirmed-only——suspect 时墙仍可能问一次,有意(suspect 本该礼貌确认)
- contains 兜底可能误绑怪 key(如"无email")——概率极低且 value 仍过 split_validate
- 新正则进入 latest_stated_contacts 即获覆盖权——倒装只收显式渠道词,排位表述风险已实证排除并钉死
- 同轮改号当轮视图显示旧值——"已存优先"方向的两害取轻(不影响禁令触发)
- "We will call you" 仍触发——we 形将来时不排除,已留资时被 App 门控兜住
- 再索要墙是模式匹配不是语义理解——GLM 改写措辞可能漏拦/误拦;护栏:仅已留资启用、渠道词强制、丢句不掐断(误拦只损失一句);拦截日志
Re-ask wall dropped sentence(warning 级)回流补模式 - 再索要墙 tail 位闸门的 has_contact=False 分支无独立测试钉死(loop 位已钉;两处共享同一表达式,风险极低,记录在案)
7. 部署与回滚
重启生效(prompt/代码启动加载);无迁移、无配置、无前端;回滚 = revert 对应 commit(见 §3 序列)。
8. 真机 GLM 冒烟清单(⚠️ 未执行——部署前置项)
| 场景 | 期望 |
|---|---|
| 发号后继续问价(同轮:"my whatsapp is +86..., how much?") | 确认收到 + 不再要号,报价转经理话术 |
| 倒装发号("0712 345 678 is my whatsapp")后闲聊 2 轮 | 全程零索要 |
| App 会话发号后再问"how can I contact you" | "经理会主动联系你"版话术,无 Drop your WhatsApp |
| "I'll call you later" | 不触发索要脚本,正常道别 |
| 发号后说 "I gave you my number already" | 先查记录确认收到,不要求重发 |
| 生产日志复现:how the price → 发号 → BYD Song L → zero → 续聊 | 仅首次要号,后续零索要(与 2026-09 日志逐句比对) |