补充评审:Slack 每 Specialist 独立 App — 可行性结论与关键修订建议
| 性质 | PRD 可行性评审补充(工程视角),非替代 PRD |
| 评审对象 | slack-per-specialist-app-prd.zh.md(rev 2) |
| 作者 | 工程 |
| 日期 | 2026-07-13 |
| 用途 | 与产品(Franky)对齐范围、术语、以及一个必须先拍板的架构决策 |
| 依据代码 | api/src/channels/slack/、api/src/organizations/organizations.service.ts、api/src/onboarding/onboarding.entities.ts、api/src/specialists/specialist.entity.ts、frontend/src/app/ops/、frontend/src/app/client/settings/channels/ |
| 依据 ADR | ADR-030(Draft)+ Slack 子文档(Draft)、ADR-036 |
1. 一句话结论
需求方向成立、数据模型撑得住,但 PRD 把它写成了"UI + 埋点为主"的产品需求;实际上它的底座(一张核心表 + 一整套凭证/路由改造)尚未落地,本质是一个后端地基工程。范围被 PRD 低估。可以推进,但需先拍板一个关键决策(§4)。
2. 可行性拆解
✅ 已经就绪的
- N:M 基数天然支持"N 个 Specialist = N 个 bot"。OSA(
OrgSpecialistAssignment)唯一约束只有(orgId, specialistId)(onboarding.entities.ts:61):一个 org 可挂多个 Specialist,一个 Specialist 可服务多个 org。 - 已有可对标的先例:雇用 Specialist 时会自动生成 per-OSA 的 email 身份(alias + Gsuite 开通),发生在
organizations.service.ts的_enqueueWorkspaceProvisioning。Slack 的 per-Specialist bot 可以照抄这条 provisioning 链路。
🔨 必须从零新建的(PRD 默认它们已存在,实际没有)
osa_channel_endpoints这张表根本不存在:全仓无 entity、无 migration,仅在 ADR-030(状态 Draft)和两处 CLAUDE.md 里以 "in-flight" 规划形态出现。PRD 的 R1–R6 全部压在这张表 + Secrets Manager(per-app / per-install)之上。- 入站路由要"回滚"一个刚做过的决定:今天故意只按
team_id解析 org、刻意不用api_app_id(slack.service.ts:798,#2403);PRD 要改成(api_app_id, team_id) → OSA,即把刚移除的维度重新引入,并且要每 app 各自signing_secret验签。 - App 自动生成子系统:调 Slack App Manifest API 建 app、存密钥、并发幂等 —— 全新。
⚠️ 会拖垮 PRD 目标、但被当"非阻塞"处理的两个坑
- App 图标无法纯 API 设置(PRD O1)→ 需要人工
pending_ops步骤。 - Slack 免费版 ~10 app 上限(PRD O2)→ 多 Specialist 客户会中途撞墙。付费版(Pro / Business+ / Enterprise Grid)无此上限,问题只存在于免费 workspace;因此本坑的影响面取决于目标客户的 Slack 套餐,属于"部分客户会撞、非全量"。处置:安装前检测 workspace 套餐并提示(PRD O2 待确认能否在安装前得知套餐),而非硬性限制。
- 两者都会直接冲击 G2 的"≤5 分钟、80% 无工单",见 §4 的处置建议。
范围判断
PRD 的 v1 实际等于"先把 ADR-030 Phase 1–3 从零建出来,再叠 UI"。重头在后端;前端面板改造反而是最轻的一块。建议 PRD 显式承认这个依赖,并把 ADR-030 Slack 子文档评审通过列为硬前置。
3. 术语澄清(PRD 混用,需在文档层统一)
PRD 通篇用"Specialist"指代两个不同层级的实体,是后续设计歧义的根源。应区分:
| 概念 | 定义 | 层级 | 键 |
|---|---|---|---|
| Specialist catalog archetype(目录原型) | is_catalog=true, org_id=NULL 的平台模板,可被多个 org 反复雇用 | 平台级、唯一 | catalog_slug |
| live clone / OSA(org 克隆实例) | 某 org 雇用时由 _materializeCatalogSpecialist 克隆出的 is_catalog=false, org_id=<org> 行 + 一条 OSA | per-(org×specialist) | (org_id, specialist_id) |
- 一个 archetype 被 N 个 org 雇用 → 产生 N 个 clone/OSA。
- 现库里约 360~370 个 archetype(
seed-specialist-catalog.ts有 371 条)——规模是"几百",不是几十。这个数量级直接决定 §4 的最佳实践。 - 另需澄清 Expert ≠ Specialist:一个 org 有几个人类 Expert(
expert_access授权的审核员)与 app bot 数量完全正交,不产生冲突。PRD 语境下"多个专家"= 多个 Specialist(AI 人格 / 多条 OSA)。
4. 关键决策(需产品先拍板):App 身份在"什么时刻"生成?
这是本需求最该先定、且 PRD 当前选择偏差最大的一点。
4.1 前提:bot 必须分成两层,分别挂在不同实体上
| bot 的层 | 内容 | 挂在哪 | 跨 org |
|---|---|---|---|
| App / persona | api_app_id、bot 名=firstName、头像、manifest、signing_secret | archetype 级(全平台一个,键 catalog_slug) | ✅ 共享 |
| Install / endpoint | OAuth access_token、team_id、会话历史 | OSA 级(每雇用一次) | ❌ 各自独立 |
由此得出一个必须写进 PRD 的事实纠正:不同 org 是共享同一个 app 的(同 api_app_id、同 bot 身份),各自独立的只是 install(token/workspace/历史)。隔离靠"安装隔离",不靠"app 隔离"。
4.2 App 身份的生成时机——三个选项
| 方案 | 触发时机 | 几百 archetype 规模下的后果 |
|---|---|---|
| A. 建 archetype 时全量生成 | createSpecialist/seed | ❌ 360 个 app 一次性建出、绝大多数从未被雇用 → 浪费 Slack app 舰队配额,放大 §8.3 unlisted 舰队政策风险 |
| B. 首次被任一 org 物化时生成一次(推荐) | _materializeCatalogSpecialist 首次命中该 archetype | ✅ 只为真正被雇的 archetype 建 app;后续 org 复用;由平台侧异步跑;人工图标/审批步骤在客户安装之前完成 |
| C. 客户 admin 在渠道面板开开关时生成(PRD 现 R1) | 客户点开关 | ❌ 生成延迟 + O1 人工图标 + O3 审批全落在客户关键路径上,直接打脸 G2 |
4.3 建议:采用 B
App 身份按 archetype 生成一次,时机为"首次被任一 org 物化/雇用",由平台异步 provisioning 完成,与客户的安装动作解耦。 三点理由:
- 粒度对:app ↔ archetype(键
catalog_slug),非 per-org、非 per-clone。per-org 差异(如显示名 override)放 install/endpoint 层。顺带解决 archetype-vs-clone 的键歧义。 - 不浪费:360 个里真正被雇的可能只有几十个,B 只为这几十个建 app,避开舰队配额/政策风险。
- 把人工步骤挪出客户关键路径:图标上传、可能的 SuperAdmin 审批(O3)在"首次被雇用"这个平台侧环节由 ops 兜底;客户 admin 到渠道面板时,面对的是"已就绪、只差 OAuth"的 app,自助那步才可能真正 ≤5 分钟。
4.4 落点(代码位置已核实)
- App 生成挂在
_materializeCatalogSpecialist(organizations.service.ts:2737),由assignSpecialist(:2315)触发;做成幂等 per archetype(分布式锁 / 唯一约束,仅首次物化真正建 app,后续复用)。旁边已有一批同类 fire-and-forget provisioning(Gsuite、Vercel domain),并入即可,与 email alias 生成对称。 - 注意:archetype 层没有 draft/published 生命周期(
status恒为available),没有干净的"上架/发布"钩子——物化是最接近"投入使用前"的天然节点。 - Install 层仍留客户
client/settings/channels面板自助 OAuth,写osa_channel_endpoints(待建)。客户面板只保留"安装/卸载"。
4.5 对 PRD 的具体改动
- R1:把触发从"首个 org 打开开关时生成"改为"首次被 任一 org 雇用/物化时、平台侧异步生成一次"。
- §5.1:客户渠道面板的状态机去掉 "Generating",客户侧起点即 "Ready to install"(app 已由平台备好);生成态与失败态移到 ops 侧可见。
- O3(是否需 SuperAdmin 审批):在 B 方案下自然归属 ops/平台侧,不再阻塞客户流程。
5. 仍需产品拍板的开放项(承接 PRD §12)
| # | 问题 | 建议倾向 |
|---|---|---|
| O8 | 跨 org 共享同一 workspace(新唯一键 (api_app_id, team_id) 使之技术可行,今天被 uq_integration_slack_team 禁止) | 需明确:放开(靠 per-app 隔离)还是保留 org↔workspace 排他。影响 R2 校验。 |
| O6 | 同 org 内两个 Specialist 同名(如两个 "Kai")→ 两个都叫 "Kai" 的 bot,授权页/@提及无法区分 | 建议同 org 内做后缀消歧("Kai (KYC)"),不要留到上架才管;PRD 把它列为"不阻塞"偏乐观 |
| — | app persona 的身份字段(名/头像/bio)以 archetype 还是 clone 为准(影响 R7 同步语义) | 采用 B 后以 archetype 为准,per-org override 落 endpoint 层 |