服务通知
- 系统找用户:工作区邀请、加入申请结果、好友申请等产品事件发生时,用户在 「消息」 里能看到 固定入口「服务通知」,不必记得去某个模块里翻。
来源 docs/core-mechanisms/服务通知.md
状态:规格已确认(2026-05-25)。
真值:用户可见能力见
docs/产品规格.md§4.7;本文供设计与实现对照。表述:界面与帮助文案用用户表达;表名、API、会话类型等实现术语仅在本文 §6~§8 给出。
1. 目标
- 系统找用户:工作区邀请、加入申请结果、好友申请等产品事件发生时,用户在 「消息」 里能看到 固定入口「服务通知」,不必记得去某个模块里翻。
- 与聊天分离:服务通知 不是 同事私聊、 不是 业务智能体对话、 不是 帮助智能体问答;形态接近微信 「服务通知」——以 系统代发的说明 + 可点操作 为主。
- 可信、可审计:通知正文来自 服务端模板(或结构化字段渲染),默认不 用大模型现场编造事件内容,避免「助手幻觉成已批准/已拒绝」。
1.1 非目标(首期不做)
- 不替代短信/邮件 验证码 通道(仍走 OTP)。
- 不要求用户与服务通知 日常闲聊;可选二期支持「帮我汇总最近通知」类 只读摘要 问答。
- 不默认做浏览器 Push、企业微信/钉钉等 站外推送(列为三期扩展)。
- 不把协作详情页(工作区协作内的横幅、我的申请列表)删掉——服务通知负责 触达,各模块仍保留 处理界面。
2. 与现有能力的关系
| 现有能力 | 用户以为 | 实际 today | 与服务通知关系 |
|---|---|---|---|
| 帮助智能体 | 问产品怎么用 | 未选工作区/引导场景的可对话助手 | 并列:帮助 = 你问我答;服务通知 = 系统找你说 |
| 消息(AI 对话) | 所有「消息」 | 仅 chat_sessions 助手回复未读 + 20s 轮询 | 服务通知 并入消息入口,但 单独分组/置顶 |
| 联络 | 同事/群聊 | im_* 未读不进顶栏角标 | 好友申请等待 迁入 服务通知触达,处理仍在联络 |
| 工作区协作 | 邀请与申请 | 仅打开该页才有横幅/侧栏列表 | 邀请/申请 结果 由服务通知推送;页内 UI 保留 |
3. 产品决策(已确认,2026-05-25)
| # | 主题 | 结论 |
|---|---|---|
| 1 | 用户可见名称 | 固定称 「服务通知」;消息列表中图标/标题一致,不随工作区变化 |
| 2 | 是否「智能体」 | 产品上是固定入口;实现上可用 session_kind=notification + 系统 persona,不占用用户「我的智能体」配额;不走用户智能体 Runtime 自由生成 |
| 3 | 是否可回复 | 首期不可回复(只读时间线);二期可选「问:我漏了什么」只读摘要 |
| 4 | 知识文档 | 不使用 三层知识目录注入;模板 + 动作链接即可 |
| 5 | 未选工作区 | 仍投递 账号级通知(邀请、好友申请等);消息入口仍可见服务通知 |
| 6 | 与 §4.6 统一对话 | 列表 UI 长期可合并,但 会话类型 必须区分 notification vs direct vs agent chat |
| 7 | 已读 | 进入服务通知会话即更新已读水位;顶栏「消息」角标 = AI 未读 + 服务通知未读 +(后期)IM 未读 |
| 8 | 保留时长 | 至少 90 天 可查(实现可配置);用户 不可删单条(首期),避免误删审批凭证 |
4. 用户可见体验
4.1 入口
- 顶栏「消息」:未读角标包含 服务通知未读条数(可与 AI 未读合并显示总数,进入后分开展示来源)。
- 消息模块列表:服务通知 固定 置顶(在帮助智能体、各业务智能体、会话列表之上或独立分组首项)。
- 副标题/预览:展示 最新一条 通知摘要(如「「研发部」邀请你加入工作区」)。
4.2 会话内
- 时间线:卡片式或气泡式均可,但视觉上 弱对话感(系统发送、灰底/图标区分)。
- 每条通知含:
- 标题(一句结论) - 说明(可选,模板填充) - 时间 - 主操作按钮(如「查看邀请」「去工作区协作」)—— 使用 mindlink://action/…(扩展白名单,见 §5.2)
- 无输入框(首期);底部可放「前往工作区协作查看全部待办」类静态链接。
4.3 与各模块分工
| 场景 | 服务通知做什么 | 用户仍去哪处理 |
|---|---|---|
| 收到工作区邀请 | 推送「xxx 邀请你加入「yyy」」 | 工作区协作 / 通知内「接受/拒绝」若一期不做则仅跳转 |
| 加入申请已批准/拒绝 | 推送结果 | 工作区协作「我的申请」 |
| 有人申请加入你的工作区 | 推送给 管理员 | 工作区协作「加入申请」审批 |
| 好友申请 | 推送「xxx 想加你为好友」 | 联络 |
5. 通知类型(事件清单)
5.1 一期(建议首批实现)
| 事件类型(实现 id) | 触达对象 | 用户可见标题示例 | 主操作 |
|---|---|---|---|
workspace.invitation.received | 被邀请人 | {邀请人} 邀请你加入工作区「{工作区名}」 | 去处理(协作页 / 接受拒绝) |
workspace.invitation.accepted | 邀请人 | {对方} 已加入工作区「{工作区名}」 | 打开工作区 |
workspace.join_request.submitted | 工作区管理员 | {申请人} 申请加入「{工作区名}」 | 去审批 |
workspace.join_request.approved | 申请人 | 你已加入工作区「{工作区名}」` | 打开工作区 |
workspace.join_request.rejected | 申请人 | 「{工作区名}」暂未能通过你的加入申请` | 查看说明 / 协作页 |
workspace.join_request.cancelled | 工作区管理员 | {申请人} 已撤回加入申请 | (可选)无 |
5.2 动作链接(扩展白名单)
在现有 docs/core-mechanisms/帮助动作链接.md 白名单上 增量 注册,例如:
| action | 用户结果 |
|---|---|
module.workspace | 打开工作区协作 |
workspace.invitation.accept?id=… | 接受邀请(带 id,服务端校验) |
workspace.invitation.decline?id=… | 拒绝邀请 |
workspace.join_request.review?id=… | 打开协作页并定位到该申请 |
module.im | 打开联络 |
安全:与帮助链接相同——仅白名单;带 id 的操作须 二次校验 当前用户权限。
5.3 二期及以后(占位)
im.friend_request.received/acceptedagent.train_job.completed/failedworkspace.member.removed(被移出工作区)workflow.task.assigned/workflow.task.cancelled(工作流待办;已实现)- 管理员广播(企业公告,可选)
6. 消息形态与文案规则
6.1 模板优先
- 每条通知存
event_type+payload(JSON);展示时 服务端或前端 用模板渲染中文标题/正文。 - 模板版本化(
template_version),便于改文案不重写历史。 - 禁止 把通知正文交给 LLM 生成(除二期明确的「摘要问答」且须标注「由助手整理,以详情页为准」)。
6.2 与用户表达对齐
- 用 工作区、邀请、加入申请,不用
workspace_id、join_request作主语。 - 编号类信息放 详情/折叠,不占标题首屏。
6.3 去重与合并
- 同一
dedupe_key(如invitation:{id})在 pending 态 更新 而非重复刷屏。 - 可选:同一工作区多条申请 合并为「你有 3 条加入申请待处理」(二期)。
7. 未读、同步与实时性
| 项 | 建议 |
|---|---|
| 未读计数 | 按通知消息 created_at > last_read_at;与 chat 未读 分表统计,API 可合并 |
| 前端刷新 | 短期:轮询(与现有 20s 对齐或独立 30s);中期:SSE/WS 通知事件 |
| 离线 | 用户下次登录拉取 GET /notifications 或会话历史即可 |
8. 实现模型(草案)
以下为研发对照,不是 对用户文案。
8.1 数据(示意)
方案 A(推荐首期):独立表 + 固定会话
notification_messages
id, user_id, event_type, dedupe_key,
title, body, payload_json,
action_primary_json, -- { "label", "action", "params" }
read_at NULL,
created_at
每用户一条 chat_sessions 或等价 notification_inbox 指针,用于消息 UI 挂载;session_kind = 'notification'。
方案 B:直接写入 chat_messages 且 role=system —— 易与助手消息混淆,不推荐。
8.2 API(示意)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/notifications | 分页列表(含未读数) |
| GET | /api/v1/notifications/unread-count | 顶栏角标 |
| POST | /api/v1/notifications/read | 标记已读(进入会话或全部已读) |
| GET | /api/v1/chat/unread-summary | 扩展:增加 notification_unread 字段(或独立汇总接口由前端合并) |
写入:仅在 业务 handler 成功提交事务后 调用 notify.Emit(ctx, event),避免「通知发了但业务失败」。
8.3 与「通知智能体」实现关系
- 产品名:服务通知(入口)
- 实现 persona(可选):
user_agents.is_system=1且agent_kind=notification的 不可删除 实例,或 无 agent 记录 仅用固定 UI 壳 - Runtime:通知写入 不调用
POST /chat/ 流式 LLM;与帮助智能体、业务智能体 隔离
9. 与统一对话(§4.6)的演进
flowchart LR
subgraph now [当前]
M[消息模块 chat]
H[帮助智能体]
I[联络 im]
W[协作页内横幅]
end
subgraph target [目标]
U[统一消息入口]
U --> N[服务通知 置顶只读]
U --> H2[帮助 / 智能体 / 同事会话]
N --> Actions[mindlink://action]
end
M --> U
I --> U
W --> N- 短期:在现有 消息模块 增加 服务通知 置顶会话,不必等 IM 全量合并。
- 长期:
GET /im/conversations与通知 inbox 同一列表组件,用conversation.kind/notification过滤 Tab。
10. 分期与验收
10.1 一期(MVP)
范围:§5.1 六类工作区事件 + 置顶入口 + 未读进顶栏 + 动作跳转协作页。
验收(用户可执行):
- 用户 A 邀请 B → B 在 消息 → 服务通知 收到条目,顶栏角标 +1。
- B 打开服务通知 → 角标清除(或按条已读策略)。
- 点击「去处理」→ 进入工作区协作并能看到对应邀请/申请。
- 管理员批准加入申请 → 申请人在服务通知收到「已加入」;协作页「我的申请」状态一致。
- 通知正文与数据库事实一致,无 LLM 参与生成。
10.2 二期
- 好友申请迁入;通知内 接受/拒绝(少跳转)。
- 「最近通知摘要」只读问答(可选,单独开关)。
- 合并未读 API;IM 未读进顶栏。
10.3 三期
- 站外 Push / 邮件摘要;管理员公告。
11. 风险与约束
| 风险 | 缓解 |
|---|---|
| 与帮助智能体混淆 | 固定名称、固定置顶、无输入框、视觉区分 |
| 重复通知 | dedupe_key + 状态机(pending → resolved) |
| 权限泄露 | 通知 payload 不含 secrets;动作链服务端校验 |
| 性能 | 异步写入(事务后 goroutine/队列);列表分页 |
12. 参考
- 现状缺口分析:对话中「消息提示机制」梳理(2026-05-25)。
帮助动作链接.md— 跳转协议与白名单。统一对话与联络.md— 长期 IM 合并。docs/产品规格.md§4.5 工作区协作、§4.6 统一对话。
*评审通过后:在 产品规格.md 增加用户可见 §;在 实施计划.md / 实施验收.md 挂阶段与用例;实现前再开 API 字段级契约页(可选)。*