PRD:WhatsApp 渠道 — Specialist ↔ 号码 1:1 绑定(号码池模式)+ 配对准入
English: whatsapp-per-specialist-number-prd.md
| 状态 | Draft — 待评审 |
| Owner | Franky(产品) |
| 日期 | 2026-07-08 |
| 架构依据 | ADR-002(Twilio + 号码池 + 配对码,现行实现)· ADR-030 WhatsApp 子文档(Draft,per-OSA endpoint 方向)· WhatsApp 官方限制 |
| 姊妹方案 | Slack per-Specialist App PRD(同一模式:1:1 渠道身份 + 按状态门控的 Profile 入口) |
| 关联 | #1471(ADR-030)、#3630(渠道配置 UX)、#3541/#3542(渠道指标族) |
0. 术语澄清(先说清"绑定对象是谁")
客户门户里的 "Specialist" 是该 org 的运行实例(catalog 原型物化出的克隆,即 OSA 对应的 live Specialist)。因此本 PRD 的「Specialist ↔ 号码 1:1」实际是 每个 (Org × Specialist) 一个专属号码 —— 同一 个原型 Kai 服务两个客户时,两个客户各得一个不同的号码。这与 ADR-030 WhatsApp 子文档的不变量 I-2(每 OSA 一个专属号)完全一致。
1. 问题陈述
当前 WhatsApp 渠道(ADR-002)虽有号码池与配对码机制,但存在三个产品缺口:
- 路由塌缩:一个 org 一份 WhatsApp 凭证,多 Specialist 的 org 在 WhatsApp 上回落到 primary OSA——号码与 Specialist 没有严格的 1:1 关系,客户无法"发消息给特定的 Specialist"。
- 配置不可见、容量不设防:管理员没有按 Specialist 的 WhatsApp 开关;号码池余量对配置流程完全无感——池子空了也能"配置",结果静默失败。
- 准入不明确:配对(HW-XXXX 码)存在但埋在设置页里;谁能和 Specialist 对话、未配对的人发消息会怎样,无统一规则(今天未知发件人会收到配对提示自动回复,行为未经产品确认)。
本 PRD 将现有机制收紧为:严格 1:1 号码绑定 + 池容量门控 + 配对白名单准入 + Profile 对话入口。
2. 目标
- G1 — 确定性归属:每个启用 WhatsApp 的 Specialist 拥有一个专属号码;发到该号码的消息只会进入该 Specialist 的会话。度量:入站按 To 号码单查表命中率 100%,primary-OSA 回落命中 = 0。
- G2 — 容量诚实:管理员在开关处即可看到能否启用;绝不出现"打开了却没有号码"的静默失败。度量:因 无号码导致的配置失败工单 = 0。
- G3 — 准入可控:只有完成配对的用户 WhatsApp 账号能与 Specialist 会话;未配对号码来信不响应(不回复、不建会话)。度量:未配对来信触发的会话数 = 0。
- G4 — 自助配对:客户成员从 Specialist Profile 入口自助完成配对管理(查看/新增/删除),无需管理员或 AM 介入。度量:配对完成率 ≥ 80%,配对后首条消息成功率 ≥ 95%。
- G5 — 号码可回收:Specialist 被回收/删除后号码自动释放回池,经冷却后可复用;释放过程不产生跨客户消息串扰。
3. 非目标(v1)
- 按需购号(Twilio API 动态采购)——v1 只消费预置号码池;池子的扩容走运营流程(见 §8 容量阶梯)。
- ADR-030 完整 endpoint/persona 架构落地——v1 在现有
whatsapp_number_pool+whatsapp_linked_numbers上收紧语义;osa_channel_endpoints迁移是后续架构阶段(设计需与其兼容,见 §6 R1 技术注记)。 - WABA-per-Specialist 身份容器与发信人资料同步——每号码的 sender display name / 头像同步(ADR-030 §2/§6)推后;v1 号码无个性化资料。
- Org 前门模式(一个 org 一个号 + 话题路由)——ADR-028,明确推迟。
- 对未配对来信的引导回复——按产品要求 v1 为静默不响应;一次性配对提示作为开放问题(O2)另议。
- 群聊支持——仅 1:1 私聊。
4. 用 户故事
客户管理员(org_admin)
- 作为客户管理员,我想为某个 Specialist 打开 WhatsApp,让团队能在 WhatsApp 上直接找到该 Specialist。
- 作为客户管理员,当号码池没有空闲号码时,我希望开关直接不可用并说明原因,而不是打开后出错。
- 作为客户管理员,我想在配置框里看到该 Specialist 的专属号码和已配对的用户列表,随时掌握谁在使用这个渠道。
客户成员 4. 作为客户成员,我想在 Specialist Profile 上看到 WhatsApp 入口,点进去就能完成配对并开始对话。 5. 作为客户成员,我想查看/新增/删除我自己的配对号码,换手机号时能自助处理。 6. 作为客户成员,配对完成后我希望直接在 WhatsApp 里和 Specialist 对话,体验与其它渠道一致(AI 起草 + Expert 审核)。
Ops(SuperAdmin / AM) 7. 作为 SuperAdmin,我需要维护号码池(添加号码、查看占用/空闲/冷却状态),并在池余量低时收到告警。 8. 作为 AM,我想看到我负责客户的 WhatsApp 启用与配对情况,便于推动采用。
Expert — 工作流不变:入站消息进队列时已归属正确 Specialist;回复经该 Specialist 的专属号码发回。
5. UX 流程
5.1 管理员配置(客户门户 → Settings → Channels → WhatsApp)
WhatsApp 卡片改为按 Specialist 列表,每行:Specialist 头像/名称 + 开关 + 状态。
池有空闲号码:
开关可用 → 打开 → 系统从池中随机取一个空闲号码,与该 Specialist 绑定
→ [已启用] 显示专属号码 +15551000001 · 已配对用户 N 人 · [配置▸]
池无空闲号码:
开关置灰,提示:「暂时没有可用 WhatsApp 号码,不能启用 WhatsApp 渠道」
(SuperAdmin 侧同步看到池余量告警)
关闭开关(有活跃绑定):
确认弹窗 → 解绑 → 号码进入冷却 → 该 Specialist 的入口隐藏、配对失效
5.2 配置框(点开某 Specialist 的 [配置▸])
- 号码信息:专属 WhatsApp 号码(含复制按钮)。
- 已配对用户列表:org 内每个已配对成员一行 —— 用户名 · 配对的 WhatsApp 号码(脱敏显示,如 +86 138****1234)· 配对时间;管理员可移除任一配对(用户可重新配对)。
- 管理员不能代替用户新增配对(配对必须由本人完成占有验证,见 5.3)。
5.3 用户使用流程(Specialist Profile 的 WhatsApp 入口)
- 客户成员点击 Specialist Profile 上的 「Chat on WhatsApp」 入口(渠道未启用则不展示/置灰——与 Slack PRD 的门控入口同一模式)。
- 弹出我的配对面板:
- 已配对:列出我的已配对 WhatsApp 号码(可删除);主按钮 「打开 WhatsApp 对话」 →
wa.me/{专属号码}深链直接进入与该 Specialist 的聊天。 - 未配对:引导新增配对 ——「新增配对」生成一次性配对码 HW-XXXX(15 分钟有效,沿用现机制),并给出预填了配对码的
wa.me/{专属号码}?text=HW-XXXX深链;用户在 WhatsApp 里点发送即完成占有验证式配对(必须从本人手机发出,杜绝替他人绑号)。 - 配对成功后面板即时刷新(webhook 驱动),主按钮变为「打开 WhatsApp 对话」。
- 已配对:列出我的已配对 WhatsApp 号码(可删除);主按钮 「打开 WhatsApp 对话」 →
- 会话准入:配对号码发来的消息 → 正常进入 该 Specialist 的会话(入站 → agent 起草 → Expert 审核 → 回复)。未配对号码发来的消息 → 不响应:不自动回复、不创建会话;仅内部隔离记录(见 R7)。
5.4 文案(v1)
| 位置 | 文案 |
|---|---|
| 开关置灰提示 | 暂时没有可用 WhatsApp 号码,不能启用 WhatsApp 渠道 |
| Profile 入口 | Chat on WhatsApp |
| 入口(渠道未启用,成员视角) | tooltip:该 Specialist 尚未开通 WhatsApp——请联系你的管理员 |
| 配对引导 | 发送配对码 HW-XXXX 到 {号码} 即可完成绑定(15 分钟内有效) |
| 配对成功 | 配对成功!现在可以在 WhatsApp 上与 {Specialist} 对话了 |
6. 需求
Must-have(P0)
R1 — 号码池与 1:1 绑定。 维护 WhatsApp 号码池(现有 whatsapp_number_pool 收紧语义):号码状态机 空闲 available → 已绑定 bound(specialist_id) → 冷却 cooling → 空闲。一个号码同一时刻至多绑定一个 Specialist(DB 唯一约束);一个 Specialist 至多一个号码。
- Given 号码已绑定 Specialist A,then 任何将其绑定到 B 的尝试都被拒绝(约束层,非仅应用层)。
- 技术注记:绑定关系的读写路径按 ADR-030
osa_channel_endpoints的形状设计(number =external_id),后续迁移平滑。
R2 — 池容量门控开关。 管理员渠道配置中,WhatsApp 开关可用性 = 池中 available 号码数 > 0。
- Given 池空,when 管理员查看渠道配置,then 开关置灰 + 提示文案(§5.4),且后端拒绝启用请求(UI 置灰非安全边界)。
- Given 池空且有 org 想启用,then SuperAdmin 池面板显示待满足需求(低余量告警见 P1)。
R3 — 激活即随机绑定(并发安全、幂等)。 打开开关 → 从池中随机选一个 available 号码原子绑定(UPDATE … WHERE status='available' … LIMIT 1 RETURNING,遵循 active-active 规则,无进程内锁)。
- Given 两个管理员同时为两个 Specialist 开启且池中仅剩 1 个号码,then 恰好一个成功、另一个得到池空提示——绝不双绑同一号码。
- 重复开关不泄漏号码:同一 Specialist 重复激活是幂等的(已有绑定直接返回)。
R4 — 配置框可见性。 配置框展示:专属号码 + 已配对用户列表(用户 · 脱敏号码 · 配对时间),管理员可移除配对(写审计日志)。
R5 — 门控的 Profile 入口。 Specialist Profile 展示 WhatsApp 入口,状态如实:渠道启用 → 可用;未启用 → 不展示或置灰附提示(与 Slack PRD R3 同模式)。点击进入我的配对面板(§5.3)。
R6 — 自助配对管理(占有验证)。 用户在配对面板查看/新增/删除自己的配对:新增 = 生成 HW-XXXX 码(15 分钟 TTL、限速沿用现机制:5 次/小时)+ 预填 wa.me 深链,从本人 WhatsApp 发码完成验证(复用 whatsapp_pairing_codes / whatsapp_linked_numbers);删除即时生效。
- Given 配对码过期或错误,then 该来信按未配对处理(R7),不消耗会话。
- 配对归属 (用户, org) 层级:一次配对即可与该 org 内所有已启用 WhatsApp 的 Specialist 对话(按 To 号码路由到具体 Specialist)——按 Specialist 粒度的准入见开放问题 O3。
R7 — 入站准入(核心规则)。 入站消息路由:To 号码 → 绑定的 Specialist(org+specialist 确定),From 号码 → 配对表 → 用户。两者同 org 且配对有效 → 进入该 Specialist 会话。否则(未配对 / 配对已删 / 跨 org)→ 不响应:不回复、不建会话、不进队列;写内部隔离记录(unresolved_ingress,reason=unpaired_sender,含脱敏 From/To)供 ops 排查与滥用监控。
- 不变量测试:会话的
specialist_id/org_id恒等于号码绑定的 Specialist/org;未配对来信产生 0 条conversations/messages记录。 - 配对码消息(HW-XXXX)是唯一例外:走配对流程,不算会话消息。
R8 — 出站与会话语义。 Expert 放行的回复经该 Specialist 的专属号码发出(Twilio,3 层派发:熔断 → 重试 → DLQ);会话按 (用户, Specialist) 维度隔离;HITL 默认阈值等既有语义不变。24h 客服窗口外的回复需模板消息(计费见 whatsapp-limits §三)。
R9 — 回收与释放。 以下事件触发号码释放:Specialist 被解除分配(OSA 终止)、Specialist 删除/归档、org 停用、管理员关闭开关。释放动作(事务性):
- 解绑(号码 →
cooling); - 吊销该绑定下的全部配对(防止旧配对用户的来信到达"下一个主人");
- 冷却期(默认 30 天,可配)后回到
available;冷却期内来信按未配对静默处理; - 全程审计(谁、何时、为何释放)。
- Given 号码冷却结束被绑到新 Specialist,then 任何旧配对都无法与新 Specialist 对话(配对已在步骤 2 吊销)。
- 注意 Twilio 号码保留在平台账户内(池模式),不做 Twilio 侧 release——避免 63051 类重新注册成本与号码丢失。
R10 — 埋点与审计(服务端)。 与渠道指标族(#3541/#3542)一致:whatsapp_pool_size{status}(gauge)、whatsapp_binding_total{outcome}、whatsapp_pairing_total{outcome}、whatsapp_unpaired_inbound_total;审计:开关开/关、绑定/释放、配对增/删(操作者 + org + specialist)。§11 每个指标可直接算出。
Nice-to-have(P1)
- SuperAdmin 号码池面板:池列表(号码 · 状态 · 绑定对象 · 冷却剩余)、手动添加号码、低余量阈值告警(Slack 通知 AM/ops)。
- 管理员通知:绑定成功 / 池空被拒 / 配对异常波动。
- 配对面板显示二维码(扫码打开
wa.me深链,桌面端体验)。 - 未配对来信的滥用监控:同一 From 高频撞号告警。
Future(P2)
- 按 Specialist 粒度的配对准入(当前为 org 级,见 O3)。
- Twilio API 按需购号自动补池(池水位线自动维持)。
- 迁移到 ADR-030 完整
osa_channel_endpoints+ WABA-per-Specialist persona、发信人资料同步。 - 未配对来信的一次性配对引导回复(若 O2 改变决策)。
- Org 前门模式(ADR-028)。
7. 现状基线(改什么)
| 维度 | 今天(ADR-002 现状) | v1 之后 |
|---|---|---|
| 号码 ↔ Specialist | 池存在但无严格 1:1;路由回落 primary OSA | 严格 1:1(约束层保证);按 To 号码确定性路由 |
| 管理员配置 | 无按 Specialist 开关;池容量无感 | 按 Specialist 开关 + 池容量门控 + 配置框(号码 + 配对列表) |
| 配对 | HW-XXXX 码存在,埋在设置页;发码即绑 | 保留占有验证机制,升级为 Profile 入口内的自助配对面板(查/增/删) |
| 未配对来信 | 隔离 + 自动回复配对提示 | 静默不响应(仅内部隔离记录)——行为变更! |
旧白名单(whatsapp_sender_whitelist) | 遗留回退路由 | v1 不再参与准入(只认配对),保留只读待清理 |
| 号码回收 | 无产品化流程 | R9 全生命周期(释放 → 吊销配对 → 冷却 → 复用) |
| Profile 入口 | 无 | 门控的「Chat on WhatsApp」+ 配对面板 |
8. 平台约束(务必正视)
- 池的天花板是 Meta portfolio 上限:未验证 2 个号、验证后 20 个、再往上人工工单(whatsapp-limits §二/§七 容量阶梯)。启用 WhatsApp 的 Specialist 总数 ≤ 池上限——Business Verification 是本功能规模化的硬前置;池扩容按容量阶梯走(工单 → ISV per-client portfolio)。
- 触达限额按 portfolio 共享:所有池号码共享同一 messaging limit(250 → 2,000 → …);某个 Specialist 的高流量会挤占全池的主动触达额度。窗口内回复不受限——本功能以用户主动发起为主,风险可控,但需监控。
- 模板计费:客服窗口(24h)外的回复按模板逐条计费(2025-07 起);成本随
osa_id归集。 - 63051 闲置锁:池中长期
available的号码 30 天无活动会被 Meta 锁定——池维护任务需周期性保活或接受重激活成本(工程确认,O4)。
9. 安全与隐私
- 配对 = 占有验证:必须从本人手机发码,UI 不允许直接录入任意号码(防冒绑他人号码)。配对码 15 分钟 TTL + 5 次/小时限速(现机制)。
- 权限:开关/配置框 = org_admin;配对面板 = 成员本人(只能管理自己的配对);管理员可移除任何配对但不能代加。后端 Guard 是权威边界。
- 号码复用串扰:R9 的"吊销配对 + 冷却期"是防串扰的核心;冷却期内来信静默丢弃并记录。
- 脱敏:配对列表与日志中的手机号一律脱敏展示;完整号码仅路由层使用。
- 租户隔离:ADR-020 不变量——会话按 (org, specialist) 双过滤;未配对来信绝不产生任何跨租户可见痕迹。
10. 发布与回滚
- Feature flag 按 org 白名单:1 个内部测试 org + 1–2 个友好客户试点,漏斗指标(R10)干净运行 2 周后放 开。
- 回滚 = 关 flag:UI 回到现状;已绑定号码保持绑定(不自动释放),入站准入回退到现行为(含配对提示自动回复)。
- 行为变更提示:「未配对静默」上线时,对已有 WhatsApp 使用的试点 org 需在发布说明中明示(今天会收到配对提示的用户将改为收不到任何回复)。
11. 成功指标
领先(上线 2 周):启用成功率(开关打开→绑定成功)≥ 99%;配对完成率(发起→成功)≥ 80%;配对后首条消息进入队列成功率 ≥ 95%;未配对来信静默处理率 100%(0 响应、0 会话);池空导致的启用被拒次数(用于扩容决策)。
滞后(季度):WhatsApp 会话量/org;启用 WhatsApp 的 Specialist 数与池利用率;配对用户留存(配对后 30 天仍活跃对话);WhatsApp 相关支持工单下降。
度量方法:全部来自 R10 计数器 + 审计日志;无需人工拉数。
12. 开放问题
| # | 问题 | Owner | 阻塞? |
|---|---|---|---|
| O1 | 池的初始规模与扩容触发线(结合 Meta 上限 2→20:验证前只有 2 个号,试点即受限——Business Verification 是否作为上线前置?) | 产品 + ops | 是 |
| O2 | 未配对来信"完全静默"的 UX 风险:真实客户手输错号/换号后发消息石沉大海。是否给首次未配对来信一条一次性配对引导(之后静默)? | 产品(Franky) | 是 — 决定 R7 最终形态 |
| O3 | 配对粒度:v1 为 org 级(配对一次可与该 org 全部已启用 Specialist 对话)。是否需要按 Specialist 粒度准入(用户 A 只许和 Kai 聊、不许和 Mei 聊)? | 产品 | 否 — P2 预留 |
| O4 | 63051 闲置锁对池中空闲号码的实际影响与保活方案(定期自发保活消息?接受重激活?) | 工程 | 否 |
| O5 | 冷却期时长(建议默认 30 天)与是否允许 SuperAdmin 手动提前解冻 | 产品 + ops | 否 |
| O6 | 同一用户手机号在多个 org 都配对时的路由确认(现 whatsapp_linked_numbers 含 orgId,To 号码已定 org——需确认多 org 同号无歧义) | 工程 | 否 |
| O7 | Profile 入口与 Slack 入口的统一设计(「Contact via …」多渠道组件,见 Slack PRD §5.2 设计说明) | 设计 | UI 开发前需定 |
13. 阶段划分与依赖
- v1(本 PRD):R1–R10 全量 + P1 的池面板(SuperAdmin 没有池面板则 R2 无法运营)。前置:Meta Business Verification(O1,决定池 ≤2 还是 ≤20);号码池预置(Twilio 采购 + 入池)。
- v1.1:P1 余项(告警、二维码、滥用监控)+ O2/O3 决策落地。
- v2:ADR-030 endpoint/persona 迁移、按需购号、发信人资料同步、按 Specialist 配对粒度。
依赖:Twilio 号码采购(ops);Meta Business Verification(ops,建议立即启动——审核周期数天到数周);与 Slack per-Specialist PRD 的 Profile 入口组件复用(O7)。
14. 参考
- ADR-002 — WhatsApp Twilio + 号码池 + 配对码(现行实现)
- ADR-030 WhatsApp 子文档 · ADR-030 父文档
- WhatsApp 官方限制(号码/触达/计费/容量阶梯)
- Slack per-Specialist App PRD(姊妹模式)
api/src/channels/whatsapp/(现行实现:号码池、配对码、状态回调)·api/src/channels/channels.controller.ts(routeTwilioWhatsApp 路由优先级)