docs: spec第五轮自查——判定树挪轮前同轮回显/多渠道仅whatsapp确认/b分支严格性/主题行e164/cc_reply漏填兜底

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-08 16:13:17 +08:00
parent 4e40ef4d9c
commit d023349699
@@ -17,7 +17,7 @@
| 职责 | 归属 | 理由 |
|---|---|---|
| 判定(有无码/建议国)、E.164 补全、状态机、邮件渲染 | **Python 确定性**(新模块 `cc_completion` + phonenumberslite) | 状态不可能被幻觉;可离线确定性测试;邮件触发不押在 LLM |
| 客户答语理解(肯定/否定/国名) | **extractor LLM**(schema 加 `cc_reply` 可选字段,单轮无状态分类) | "yeah that's right"/"naur"/"我在迪拜"正则极脆弱,恰是 LLM 强项;extractor 本每轮跑,零额外调用。**双门控**:extractor.md 声明"仅当客户在回应号码确认时填写 cc_reply";Python 侧仅在 `_cc` 等待态(未 confirmed 且 attempts 未耗尽)时消费——防止对无关问题的 yes 被误当确认 |
| 客户答语理解(肯定/否定/国名) | **extractor LLM**(schema 加 `cc_reply` 可选字段,单轮无状态分类) | "yeah that's right"/"naur"/"我在迪拜"正则极脆弱,恰是 LLM 强项;extractor 本每轮跑,零额外调用。**双门控**:extractor.md 声明"仅当客户在回应号码确认时填写 cc_reply";Python 侧仅在 `_cc` 等待态(未 confirmed 且 attempts 未耗尽)时消费——防止对无关问题的 yes 被误当确认;**漏填兜底**(extractor 未识别出客户在答确认)→ 等同无回应,attempts 递增走 fallback |
| 话术生成与确认句必出 | 提醒注入 + **既有墙骨架**(白名单放行/缓冲替换——四轮生产实测过的机制) | GLM 对反惯性指令 0/8 服从的血泪教训,确认回显必须墙保 |
## 3. 判定树(`cc_completion.classify(number, ip_iso)`)
@@ -25,7 +25,9 @@
```
号码
├─ 以 +/00 开头 ──────────────────► HAS_CC(现有流程直走,不动)
├─ 以 IP 国码开头且去码后剩余合法 ──► HAS_CC_NAKED → 静默规范化(无对话动作)
├─ 以 IP 国码开头且去码后剩余合法 ──► HAS_CC_NAKED → 静默规范化(无对话
│ 动作;此分支无确认防线,依赖剩余段对该国 is_valid 严格校验——碰撞需
│ "以X码开头+剩余恰为X国合法号"多重巧合,错误率极低,拨错有客户反馈兜底)
├─ phonenumbers region=IP国 解析合法 ─► NEED_CC,建议=IP 国(高置信)
└─ 均不匹配(VPN/罕见格式)─────────► NEED_CC,建议=naive(去前导0+IP码,回显靠客户纠错)
IP 无效/未知 → NEED_CC 无建议国 → 问"方便发带区号完整号吗"(1 轮为限)
@@ -43,9 +45,10 @@ raw_info["_cc"] = {
```
- **canon 组**:phone 与 whatsapp 同号(canon 相同)共享一个确认流,确认一次
(实现:`_cc` 以 canon 字符串为组键或渠道间 canon 相等时联动,attempts
以组为计数主体);不同号各自独立——**换号(canon 变化)即重置该组状态机
重走判定树**(结构性解决"改号残留":旧补全永不跟随新号)
(attempts 以组为计数主体);**不同号时只有 whatsapp 组走确认流**(主联系
渠道,对话永远只确认一个号),phone 组一律 `ip_proposed` 静默补全+标注、
不确认(第五轮自查简化)——**换号(canon 变化)即重置该组状态机重走判定树**
(结构性解决"改号残留":旧补全永不跟随新号)
- **source 语义钉死**:`prefix_normalized` → **confirmed=true**(码是客户自己
写的,首封**无**"未经确认"标注);`customer_stated`=客户报国名重算后确认;
attempts 耗尽落定时 source 改写为 `fallback`(标注保留"未经客户确认");
@@ -101,7 +104,8 @@ v2 核心是让 AI **回显补全号**,而现行提示词全线教的是"不
| 客户确认(号码未变) | **不补发**(首封已可拨;后续渲染自动去标注) | 状态升级透明 |
| 客户纠正/换号 | 现有**改号更正通道**(canon 变化自然触发) | 现有机制零新逻辑 |
- 渲染点:email_service(正文联系方式行 + 更正/回头客路径,签名加参取 `_cc`);
- 渲染点:email_service(正文联系方式行 + 更正/回头客路径,签名加参取 `_cc`;
**邮件主题行的号码也用 e164、不加标注**——主题拥挤);
lead_push 的 contact 给干净 e164(契约要求"带国际区号且有效"),标注走
profile 派生(出站时由 `_cc` 计算,不落正常键——零 LLM 泄漏面)
- `profile_out` 先消费 `_cc` 生成补全 → 再过滤 `_` 键(顺序钉死)
@@ -111,11 +115,13 @@ v2 核心是让 AI **回显补全号**,而现行提示词全线教的是"不
## 6. 管道接入点与 IP 入口(终审钉死)
- **两个钩子**(锚点错了"首封即发补全号"会静默失效):
1. **轮末**:联系方式四层合并之后、`_maybe_send_lead_email` **之前**
(conversation.py 约 :500-526 区间)同步跑判定树写 `_cc`——首封补全号
与推送派生都依赖它
2. **轮前**:reminder 构建时读 `_cc`,等待态注入回显确认句(4b-3 让位句)
- **两个钩子**(锚点错了"首封即发补全号"会静默失效;第五轮自查优化——
判定树挪轮前,全链同轮完成,客户少等一轮):
1. **轮前**:即时扫描发现裸号后**同步跑判定树**(纯计算毫秒级)建 `_cc`
→ 本轮 reminder 即注入回显确认 → **同轮回显**;与一次性计数写回同事务落库
2. **轮末**:消费 `cc_reply` + 状态更新(确认落定/重算/换号重置)——在
`_maybe_send_lead_email` **之前**(conversation.py 约 :500-526 区间),
首封补全号与推送派生依赖已建好的 `_cc`
- `/session` 路由注入 `Request`,XFF 第一段合并进 `conv.client_info`
(**服务端 XFF 优先于客户端自报 ip**——WS 现状是客户端覆盖服务端、可伪造,
一并修正);chat_rest.py:107 只喂限流修不到数据流