PRD:Slack 渠道 — 每 Specialist 独立 Slack App(1:1)+ 面向客户的「Chat on Slack」入口
English: slack-per-specialist-app-prd.md(英文版为评审基准版本)
| 状态 | Draft — 待评审(rev 2:已应用查漏补缺) |
| Owner | Franky(产品) |
| 日期 | 2026-07-03 · rev 2 2026-07-07 |
| 架构依据 | ADR-030 + Slack 子文档(Draft)— 本 PRD 是该设计的产品化 |
| 关联 | #1471(ADR-030)、#1158(Slack inbound 重构)、#1159(Marketplace 上架)、#3161/#3188(按 thread 建会话)、#3630(workspace 冲突 UX)、#3482(渠道设置清理)、#1232(PostHog) |
1. 问题陈述
当前平台对所有 Specialist、所有客户 org 只有一个共享的 "H-work" Slack app。由此产生三个产品问题:
- 多 Specialist 的 org 塌缩成一个 bot。 Slack 路由先按
team_id → org,再回落到 primary OSA(除非频道显式绑定slack_channel_bindings)。非主 Specialist 在 Slack 上实际不可达——同时雇了 Kai(KYC)和 Mei(Finance)的客户只能得到一个通用 bot。 - bot 不是 persona。 客户 雇的是有名有姓的 Specialist("Kai"),但在 Slack 上对话的是通用的 "H-work" app + 通用图标。persona 身份——产品的核心——恰恰在日常工作发生的渠道上丢失了。
- 没有可发现的入口。 Specialist Profile 上没有任何东西告诉客户成员"可以在 Slack 上聊、怎么开始";安装状态(装没装)在管理员设置页之外完全不可见。
本 PRD 引入:每个 Specialist 一个 Slack App(1:1),profile 镜像 Specialist(名称、头像、简介);管理员自助流程(开关 → 自动生成 app → 安装);以及 Specialist Profile 上的「Chat on Slack」入口,按安装状态如实置灰/可用。
2. 目标
- G1 — 多 Specialist Slack。 一个客户 org 能在同一个 workspace 里与每个已分配 Specialist 以独立 bot 身份对话(N 个 Specialist = N 个 bot)。度量:归属到非主 OSA 的 Slack 会话 > 0;已迁移 org 的 primary-OSA 回落命中 → 0。
- G2 — 自助配置 ≤ 5 分钟。 客户管理员端到端激活一个 Specialist 的 Slack(开关 → 安装),无需 AM 介入。度量:≥ 80% 的安装无工单完成。
- G3 — 两次点击进入对话。 客户成员从 Specialist Profile ≤ 2 次点击打开 Slack 会话;入口状态永远真实(置灰 ⇔ 未安装)。度量:入口点击 → Slack DM 打开率 ≥ 70%。
- G4 — persona 保真。 Slack 上的 Specialist 名称/头像/简介与平台一致;变更 24h 内传播(best-effort 同步成功率 ≥ 95%)。
- G5 — 确定性隔离。 入站一次查表解析
(api_app_id, team_id) → OSA(ADR-030 不变量 I-3);按 thread 建会话语义(#3161 Option B)保持不变。
3. 非目标(v1)
- Org 前门 bot(每 org 一个 bot + 话题路由到多 Specialist)— 推迟到 ADR-028;与
specialist_id NOT NULL冲突且需要分类器。 - Slack Marketplace 上架 — 默认发行模式为
unlisted_distributed(可经 OAuth URL 安装、不在 Marketplace 展示)。Marketplace 为后续按 Specialist 选择性开启(#1159)。 - Enterprise Grid 组织级安装 — schema 已预留(
enterprise_id),UX 不做。 - 按 org 覆盖 bot 显示名 — endpoint 列已支持;v1 UI 不暴露(默认 bot 名 = Specialist first name)。
- 强制迁移现有共享 app 安装 — v1 与旧共享 app 双跑;迁移 +
slack_channel_bindings降级是后续独立阶段(ADR-030 Phase 6)。 - 改动 DM/频道 threading 语义 — #3161 Option B 原样保留。
4. 用户故事
客户管理员(org_admin)
- 作为客户管理员,我想为某个特定 Specialist 打开 Slack,让团队在已有的工作场所里找到该 Specialist。
- 作为客户管理员,我希望 Slack app 自动按 Specialist 的名称/头像/简介生成,不需要我做任何 Slack 开发者操作。
- 作为客户管理员,我需要清晰的安装步骤(一次 OAuth 点击)和可见的状态(Ready to install / Installed / Uninstalled),随时知道还差什么。
- 作为客户管理员,我希望在安装前被提醒我的 Slack 套餐 app 数量上限可能不够,避免流程中途卡死。
- 作为客户管理员,我要能在同一界面卸载/重装,生命周期管理不需要找支持。
客户成员 6. 作为客户成员,我想在 Specialist 的 profile 上看到「Chat on Slack」按钮,不用问别人就能发起 Slack 对话。 7. 作为客户成员,当 Slack 尚未配置好时,我希望按钮置灰并说明原因("请管理员先完成 Slack 安装"),而不是点进死胡同。
Ops(SuperAdmin / AM) 8. 作为 SuperAdmin,我需要 Specialist app 舰队视图(persona 状态)+ 每 org 安装视图(endpoint 状态),以排查生成/安装失败。 9. 作为 AM,我想看到我负责的客户里哪些 Specialist 已激活 Slack,便于在例会上推动采用。
Expert — 工作流不变:入站 Slack 消息进队列时已归属正确的 Specialist;回复发回源 thread。
5. UX 流程
5.1 管理员配置流程(客户门户 → Settings → Channels → Slack)
单一 Slack 卡片改为按 Specialist 列表。每行:Specialist 头像/名称 + 状态 + 操作。
开关 ON
→ [Generating Slack app…] persona 生成中(自动;分钟级)
└─ 失败 → [Generation failed] (重试 / 联系支持)
→ [Ready to install] (Install to Slack →) OAuth 装入客户 workspace
└─ OAuth 被拒 / scope 未全授 → 停留在 Ready,报错 toast,可重试
→ [Installed ✓] workspace 名 · 安装人 · 日期
操作:Reinstall · Uninstall
→ (检测到 Slack 侧卸载)→ [Uninstalled from Slack] (Reinstall →)
状态模型(对应 ADR-030):persona pending/provisioning/provisioned/failed × endpoint pending/oauth_*/provisioned/suspended/revoked。UI 折叠为:Off / Generating / Ready to install / Installed / Uninstalled / Failed。
关键规则:
- App(persona)按 Specialist 全平台一个 — 首个 org 打开开关时生成;之后的 org 直接进入 "Ready to install"。
- 安装(endpoint)按 OSA 每客户一次 — 每个 org 把该 Specialist 的 app 装进自己的 workspace。
- 有活跃安装时关闭开关 = 确认弹窗 → 卸载(
auth.revoke)+ 隐藏入口。
5.2 用户使用流程(Specialist Profile)
- 客户成员打开 Specialist Profile(客户门户)。
- Profile 显示 「Chat on Slack」 入口:
- endpoint
provisioned→ 按钮可用。点击 → 深链slack://app?team={team_id}&id={api_app_id}&tab=messages,web 兜底https://slack.com/app_redirect?app={api_app_id}&team={team_id}(复用现有/client/slack-connect中转页模式)。 - 无 endpoint / 未 provisioned → 按钮置灰 + 提示:"该 Specialist 的 Slack 尚未配置——请你的 workspace 管理员安装 Slack app。" 若浏览者是管理员,则显示 「Set up →」 快捷入口跳 Settings → Channels。
- endpoint
suspended(Slack 侧被卸载)→ 同上置灰(管理员看到 "Reinstall →")。
- endpoint
- 点击可用入口打开该 Specialist bot 的 DM——对话走正常的 入站 → agent → Expert 审核 管道。
设计说明(跨渠道一致性): 入口应做成渠道通用的 profile 组件("Contact via …"),而不是 Slack 专属的一次性控件——后续 WhatsApp/Email 的 per-OSA 入口(ADR-030 姊妹方案)可以直接插入,无需重新设计。
5.3 文案(v1,产品文案以英文为准)
| 位置 | 文案 |
|---|---|
| 入口(可用) | Chat on Slack |
| 入口(置灰,成员) | tooltip:Ask your workspace admin to finish Slack setup for {Specialist}. |
| 入口(置灰,管理员) | Set up Slack →(跳 Settings → Channels) |
| 安装按钮 | Install to Slack |
| 生成中 | Generating {Specialist}'s Slack app… |
| 已卸载横幅 | {Specialist}'s Slack app was uninstalled from {workspace}. Reinstall to resume. |
(中文本地化文案为 P1,见 §6。)
6. 需求
Must-have(P0)
R1 — 按 Specialist 自动生成 app。 某 Specialist 首次被任一 org 打开开关时,经 App Manifest API 创建其 Slack App:名称 "{firstName} by h.work"、bot 显示名 {firstName}、简介取自 Specialist bio;scopes = 现行 slack/manifest.ts 集合;per-app webhook URL(…/slack/webhook/{api_app_id});自动激活发行(unlisted_distributed);signing_secret/client_secret 入 Secrets Manager;persona 置 provisioned。
- Given 一个尚无 Slack persona 的 Specialist,when 管理员打开开关,then 5 分钟内该行显示 "Ready to install"——或显示 "Generation failed" 且可重试;绝不静默卡住。
- 若某步骤无法 API 自动化(见 O1 app 图标),persona 进入
pending_ops并生成 ops 任务;管理员侧显示 "Generating" 附说明,而非失败。 - 生成必须幂等且并发安全:两个 org 同时为同一 Specialist 打开开关,只产生一个 persona(分布式锁 / 唯一约束,遵循 active-active 规则);重复开关绝不产生重复 app。
R2 — 按 OSA 安装(OAuth)。 "Install to Slack" 生成带签名一次性 state(含 osa_id,5 分钟 TTL)的 OAuth URL。回调校验:api_app_id 与 persona 匹配;授予的 scopes ⊇ manifest scopes;(persona_id, team_id) 未被占用。成功后:token 入 Secrets Manager,endpoint provisioned,写审计日志。
- Given app 为 Ready,when 管理员完成 OAuth,then 状态翻为 Installed,profile 入口激活(轮询或 socket,不依赖手动刷新)。
- Given OAuth 被拒,then 状态保持 Ready 且错误可重试——绝无死胡同。
R3 — 按状态门控的 profile 入口(核心 UX 需求)。
- Given 该 OSA 有
provisioned的 Slack endpoint,when 成员查看 Specialist Profile,then "Chat on Slack" 可用并打开 bot DM(深链 + web 兜底)。 - Given 无 provisioned endpoint,then 入口置灰并显示管理员提示(§5.3);管理员看到配置快捷入口。
- Given app 在 Slack 侧被卸载,then 入口在
app_uninstalled事件后 1 分钟内回到置灰态。
R4 — 确定性入站路由。 事件到达 per-app webhook 路径;用该 app 的 signing_secret 验签;(api_app_id, team_id) 单行查表解析唯一 OSA。per-app 事件不做 primary-OSA 回落。按 thread 建会话语义(#3188)与 HP 员工 DM 拦截(#2164)不变。
- 不变量测试:
endpoint.specialist_id === conversation.specialist_id;endpoint.org_id === conversation.org_id。
R5 — 出站身份。 Expert 放行的回复经该 endpoint 的 bot token 派发(3 层模式:熔断 → 重试 → DLQ),落在源 thread,以 Specialist 的 bot 呈现(原生 bot 用户,非逐条消息伪装)。
R6 — 生命周期处理。 app_uninstalled → endpoint suspended(UI:Uninstalled;入口置灰)。tokens_revoked → revoked。重装恢复同一会话历史(按 (persona_id, team_id) 键控)。
- OSA 终止 / Specialist 解除分配 → 显式吊销(ADR-030 §9.3 Path B:
auth.revoke,endpointrevoked,入口移除)。 - Org 停用(
deactivateOrg)→ 该 org 全部 Slack endpoint 转suspended,与该 org 其它集成的暂停一致;恢复时重新激活。 - Specialist 平台级归档/退役 → persona
deprecated(不再允许新安装);存量安装按 OSA 终止路径收尾。
R7 — 身份同步(best-effort)。 Specialist 名称/头像/简介变更传播到每个安装的 bot profile(users.profile.set / users.setPhoto),感知限流,结果记入 profile_sync_state;失败绝不阻断消息收发。
R8 — 旧模式共存。 使用旧共享 app 的 org 不受影响。org 可在旧安装仍存在时采用 per-Specialist app(双跑);既有会话零回归。
R9 — 漏斗埋点(服务端)。 配置漏斗与入口使用在上线即可度量,不引入新的客户端依赖,与现有渠道指标族(#3541/#3542)一致:
- 计数器:
slack_persona_generation_total{outcome}、slack_install_total{outcome}(started/completed/failed/denied)、slack_entry_redirect_total(/client/slack-connect中转页按 endpoint 计数)。 - 审计日志:开关开/关、生成、安装、卸载、吊销(操作者 + org + specialist)。
- Given 功能上线,then §11 的每个领先指标都能由这些计数器 + 现有表算出——无需人工拉数。
Nice-to-have(P1)
- Ops 舰队面板:persona 状态、各 org endpoint 状态、
scopes_drifted标记、profile 同步失败。 - 管理员通知(站内 + 邮件):安装完成 / 失败 / app 被卸载。
- 安装前提醒:目标 workspace 可能已达 app 数上限(Slack 免费版限制;见 O2)。
- §5.3 文案中文本地化。
- Bot 欢迎消息 / App Home:首次打开 DM 时问候(点入口后见到空 DM 会困惑;介绍该 Specialist 是谁、能问什么)。
- 客户端埋点(PostHog,#1232):入口曝光 → 真实 CTR(R9 只覆盖点击侧)。
Future(P2)
- 旗舰 Specialist 上架 Marketplace(#1159)。
- 按 org 覆盖 bot 显示名(endpoint 列已备)。
- Enterprise Grid 组织级安装。
- 迁移工具:旧共享 app org → per-Specialist app;
slack_channel_bindings降级为 scope-hint(ADR-030 §10.3,Phase 6)。 - Org 前门模式(ADR-028)。
7. 现状基线(改什么)
| 维度 | 今天 | v1 之后 |
|---|---|---|
| Slack app | 全平台 1 个 "H-work" app | 每 Specialist 1 个 app(persona),全平台级 |
| 凭证 | integration_credentials UNIQUE(org, 'slack');workspace ↔ org 1:1(uq_integration_slack_team,#3630 冲突) | osa_channel_endpoints 按 OSA;唯一性 (persona_id, team_id) — 一个 workspace 可装多个 Specialist app |
| Specialist 路由 | team_id → org,频道绑定否则 primary OSA | (api_app_id, team_id) → OSA,单查表,无回落 |
| bot 身份 | 通用 H-work | Specialist 名称/头像/简介 |
| 客户入口 | 无(仅管理员设置页) | Specialist Profile 上按状态门控的「Chat on Slack」 |
| Threading | 按 thread 建会话(#3161 Option B) | 不变 |
| Mention 门控 | 频道内 @mention/engagement 门控 | 频道不变;与 Specialist bot 的 DM 同今天一样全量接收 |
8. 平台约束与风险
- App 图标自动化(开放 — O1)。 Manifest API 可设名称/简介,但历史上不支持设置 app 图标;bot 头像可能需要建 app 时人工上传,或逐安装
users.setPhoto(本身受 token 类型/workspace 策略限制——ADR-030 §15.7)。v1 必须容忍pending_ops人工步骤而不阻塞管理员流程。 - Slack 免费版 app 数上限(O2)。 免费 workspace 限制已装 app 数量(约 10 个)。Specialist 多的客户若用免费版 Slack 可能中途撞限;需安装前提醒 + 优雅报错。
- unlisted 商业 app 舰队(风险)。 Slack 2025+ 对非 Marketplace 商业 app 有更严限流/政策倾向(ADR-030 §13)。缓解:按方法限流、Marketplace 可选通道、持续监控。
- App-config token 轮换。 Manifest API 自动化需要 app configuration token(12 小时轮换)——需要刷新 worker + runbook。
- Scopes 只增不减。 manifest scope 变更 →
scopes_drifted→ 协调各 org 管理员重装授权(ADR-030 §14.4)。 - Webhook 3 秒 ACK。 per-app webhook URL 建 app 时注册;沿用今天的 ACK-再入队模式。
9. 安全与隐私
- 安装权限。 仅客户管理员(
org_admin/owner)可开关、安装、卸载、重装;成员只读(沿用 #2242 模式)。后端OrgRolesGuard是权威控制——UI 置灰不是安全边界。 - bot 数据访问范围。 Specialist bot 只能读取自己的 DM 和被显式邀请进入的频道。此预期写入安装授权文案与客户 onboarding 文档(ADR-030 §14)。
- workspace 成员即可访问。 客户 Slack workspace 里的任何成员都能 DM Specialist bot 并发起会话——与今天一致;v1 无按用户白名单。(客户若需更严控制,是未来需求,不做隐含假设。)
- 爆炸半径与轮换。 单安装 token 泄露 → 影响一个 OSA(重装即轮换)。单 app
signing_secret泄露 → 该 Specialist 全部安装可被伪造 webhook;signing-secret 轮换 runbook 是 GA 门槛(ops 交付物)。密钥存 Secrets Manager(per-app 与 per-install 引用),绝不入库。 - 卸载后的数据保留。 卸载暂停消息收发,但会话历史按平台常规保留策略保留;重装恢复同一历史(
(persona_id, team_id)键控)。硬删除走平台数据删除流程,不随渠道卸载发生。 - 租户隔离。 保证不变,见 ADR-030 §14:同 Specialist 跨 org 由安装隔离(token/team_id/RLS);同 org 跨 Specialist 由 app 隔离(api_app_id/signing_secret/bot user)。
10. 发布与回滚
- Feature flag,按 org 白名单。 per-Specialist Slack UI 置于每 org 开关之后。试点:1 个内部测试 org + 1–2 个友好客户 org。漏斗指标干净运行 2 周(R9:完成率 ≥ 80%、生成失败 < 5%)后扩大。
- 回滚 = 关 flag。 UI 回到旧共享 app 卡片;per-app endpoint 转
suspended(不删除),重新启用即恢复;per-app webhook 继续 200 ACK 并结构化丢弃日志(绝不对 Slack 4xx 风暴)。 - 旧模式是安全网。 v1 不碰旧共享 app 安装(R8),回滚不可能让任何 org 的 Slack 渠道失联——最坏回到今天的行为。
- 发布期间不做强制迁移(见非目标):旧 org 迁移是后续独立阶段、独立计划。
11. 成功指标
领先指标(上线 2 周评估): 开关→安装完成率 ≥ 80%;配置时长中位数 ≤ 5 分钟;已启用 org 的 profile 入口点击率;归属非主 OSA 的 Slack 会话占比 > 0;生成失败率 < 5%。
滞后指标(1 个季度评估): 每 org Slack 会话量(对比上线前);活跃使用 ≥ 2 个 Specialist bot 的多 Specialist org 数;slack-setup 类工单(目标:下降);per-app 流量的 primary-OSA 回落命中 = 0。
度量方法: 漏斗率来自 R9 计数器(Prometheus/Grafana);入口点击来自 slack_entry_redirect_total;会话归属由 conversations.specialist_id 联 OSA 主标志计算;配置时长取审计日志时间戳(开关打开 → 安装完成)。基于曝光的 CTR 依赖客户端埋点(#1232,P1)——在此之前 CTR 仅统计点击侧。
12. 开放问题
| # | 问题 | Owner | 阻塞? |
|---|---|---|---|
| O1 | App 图标/头像能否端到端 API 设置(Manifest API / 其它)?若不能,人工步骤具体是什么、谁执行? | 工程 | 是 — 决定 R1 的 pending_ops 形态 |
| O2 | Slack 免费版已装 app 上限现值 + 检测方式(安装前能否得知 workspace 套餐?) | 工程 | 否 — 提醒文案可后补 |
| O3 | 客户管理员打开开关是否直接触发平台级 app 创建,还是首次生成需 SuperAdmin/ops 审批? | 产品(Franky) | 是 — 定义 R1 触发方式 |
| O4 | Profile 入口的确切位置/设计(哪些 profile 界面:chat 侧栏卡片、profile 弹窗、移动端) | 设计 | UI 开发前需定 |
| O5 | 已在用旧共享 app 的 org:启用 per-Specialist app 后旧通道在该 workspace 是隐藏/替换,还是并存至 Phase-6 迁移? | 产品 + 工程 | 是 — R8 共存规则 |
| O6 | App 命名冲突(两个 Specialist 都叫 "Kai")——Slack app 名不要求唯一,但上架/授权页可能混淆;是否需要后缀规则? | 产品 | 否 |
| O7 | 是否接受 unlisted 舰队的政策风险(§8.3)直接上线,还是限制首发数量? | 产品 | 否 |
| O8 | 跨 org 共享 workspace。 (persona_id, team_id) 唯一性使得 org A 装 Kai 的 app、org B 装 Mei 的 app 进同一个 Slack workspace 成为可能(今天被 uq_integration_slack_team 禁止,#3630)。允许(per-app 隔离足够)还是保留 org↔workspace 排他规则? | 产品 + 工程 | 是 — 隔离规则影响 R2 校验 |
| O9 | Ops(AM Setup Wizard)是否也能代客户启用 Slack / 生成安装链接(如把 OAuth URL 邮件给客户管理员),还是 v1 严格客户管理员自助? | 产品 | 否 |
13. 阶段划分与依赖
对应 ADR-030 Slack §16(Phases 1–7)。建议的 v1 切分:
- v1(本 PRD): Phase 1–3(schema;backfill 影子验证;
SlackPersonaServiceapp 生成 + 按 OSA OAuth 安装)+ Phase 4 入站重构(与 #1158 协同 — per-app webhook 是其自然落点)+ 客户端 UI(管理员按 Specialist 列表 §5.1 + profile 入口 §5.2)+ R9 埋点 + §10 flag/试点。仅新安装;旧模式不动。 - v1.1: Phase 5 身份同步 + P1 项(舰队面板、通知、欢迎消息、PostHog 曝光)。
- v2: Phase 6 迁移 + bindings 降级;Phase 7 Marketplace(#1159);P2 项。
依赖: ADR-030 Slack 子文档评审通过(当前 Draft);#1158 重构排期;per-app secrets 的 Secrets Manager 就绪;app-config token 供给(ops);signing-secret 轮换 runbook(GA 门槛,§9.4)。
14. 参考
- ADR-030 — 渠道凭证隔离 · Slack 子文档
- ADR-020 — 隔离矩阵 · ADR-007 — Expert access
- #3161 / #3188 — Slack 按 thread 建会话(Option B)· #2164 — HP 员工 DM 拦截 · #3630 — workspace 冲突 UX · #3541/#3542 — 渠道指标族 · #1232 — PostHog
api/src/channels/slack/(现行实现)·frontend/src/app/client/settings/channels/(现行管理员 UI)