Files
wecom_it_smart_desk/docs/01-产品文档/04-坐席工作台/PRD-REQ-坐席-010-AI回复三态开关.md
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

15 KiB
Raw Blame History

PRD - AI 回复三态开关(坐席工作台)

REQ编号: REQ-坐席-010 版本: v1.0 优先级: P0(含现状行为缺口修复) 日期: 2026-08-04 负责人: 产品经理 许清楚(Xu 范围: 简单 PRD(无竞品/市场分析)


一、需求概述

坐席工作台(src/frontend-agentVue3 + Element Plus + Vite)需要新增一个 「AI 回复三态开关」,让坐席在单会话内控制 AI 是否继续自动回复、以及自动回复谁可见。

⚠️ 正交性约束:本开关控制的是「AI 自动回复」的总开关,与工具栏已有的 4 个 AI 主动触发按钮💡自动补齐 / 🎚️语气 / 🖌️润色 / 🔄改写)完全正交,后者是对坐席输入框内文字的主动加工,不在此开关作用范围内,二者不可混淆。


二、三态定义

状态值(枚举) 名称 行为
employee_only 仅回复员工(默认 AI 只回复员工(等同现状)
employee_and_agent 回复员工 + 推送坐席 AI 回复员工,同时把 AI 回复推送给坐席端,让坐席也看到 AI 在说什么,便于介入
off 关闭自动回复 停止所有 AI 自动回复
  • 作用域单会话——每个 Conversation 独立保存自己的模式,不全局、不跨会话继承。
  • 持久化:切换即时生效并落库,刷新/切换会话后保持。

三、产品目标(Product Goals

# 目标 衡量标准
G1 补齐模式控制缺口:让坐席能显式、即时地控制 AI 自动回复的开关与可见范围 三态可切换、即时生效、单会话独立持久化,100% 覆盖两条接入路径
G2 修复接单后 AI 仍自动回复的异常:坐席接管(serving)或手动关闭后,H5/WebSocket 路径不得再自动回复员工 serving 态 + off 态下,路径 B 触发率为 0(现状为 100% 误触发)
G3 低认知负担的坐席操作:用 1 个图标按钮 + 1 个 Popover 完成全部控制,与既有工具栏风格一致 坐席 1 次点击即可看到/切换当前态,无需进入设置页

四、用户故事(坐席视角)

# 角色 故事 价值
US1 坐席 作为坐席,我希望在工具栏一键查看并切换「AI 自动回复」的当前模式,这样我能立刻知道 AI 现在是怎么回的 状态可见、可控,减少意外
US2 坐席 作为坐席,当我接单(serving)后,我希望 AI 停止自动回复员工,这样不会和我抢话、避免重复/矛盾答复 避免人机混答的混乱
US3 坐席 作为坐席,当我还不想亲自回复、又想盯着 AI 时,我希望切到「回复员工+推送坐席」,这样 AI 回复员工的同时我也能在工作台看到,随时介入 人机协作、留痕可见
US4 坐席 作为坐席,我希望切换只对当前会话生效、且立即保存,这样我处理不同会话时各自独立、刷新不丢失 单会话独立、可靠持久化
US5 坐席 作为坐席,当我需要完全静默(如转人工/安抚/敏感场景)时,我希望一键「关闭自动回复」,这样 AI 绝不自动发声 可控的完全静默

五、技术规范

5.1 数据模型(约束)

  • src/backend/app/models/conversation.pyConversation 模型当前无 ai_reply_mode / reply_scope 类字段。
  • 必须新增正交枚举字段(不得复用 status 状态机):
字段 类型 默认值 说明
ai_reply_mode enum(employee_only / employee_and_agent / off) employee_only AI 自动回复模式,与 status 正交
  • 存量会话迁移:默认值 employee_only(等同现状),不影响既有业务。

5.2 关键代码事实(已查证,作为约束纳入,无需重查)

AI 自动回复存在两条完全独立的路径,开关必须同时作用两条

路径 入口 当前门控 回复去向
路径 A(企微 App 接入) src/backend/app/services/message_router.py:176 仅当 conversation.status == "ai_handling" 触发 _try_ai_reply wecom_service.send_text_message(user_id=from_user_id) 只推员工
路径 BH5 / WebSocket 接入) src/backend/app/api/h5.py:939src/backend/app/api/ws.py:472 零 status 门控(直接 asyncio.create_task(process_h5_ai_reply(...));已 grep 确认 h5_ai_task.py 内 status 仅用于写回 queued / WS 展示,从不读 status 拦截) 把 AI 回复推给员工(ai_reply+ 广播 new_message(sender=ai) 给坐席端

🔴 现状缺口(P0 必修复):坐席接单(status→serving)后,路径 A 的 AI 已停(受 status 门控),但路径 B 的 AI 仍在自动回复员工——这是未预期行为。本次需求必须修复:路径 B 在 status == serving(已接管)或 ai_reply_mode == off 时不得触发 AI 自动回复

5.3 需求池(按优先级)

P0Must have

ID 需求 验收标准
P0-1 新增正交枚举字段 ai_reply_modeemployee_only / employee_and_agent / off,默认 employee_only),不触碰 status 状态机 模型迁移落地;存量会话默认值=employee_only;字段可被 ORM 读写
P0-2 API 设置单会话模式:提供接口设置指定 Conversation 的 ai_reply_mode,即时生效并落库,返回更新后值 调用后 DB 立即更新;返回新值;并发切换以最后一次为准
P0-3 路径 A 门控message_router.py:176_try_ai_replystatus==ai_handling 基础上,额外增加 ai_reply_mode != off 判断;当 off 时即使 ai_handling 也不触发 off 态下路径 A 触发率=0employee_only / employee_and_agent 下路径 A 行为同现状(仅推员工)
P0-4 路径 B 门控 + 接单后停 AI(修复缺口)h5.py:939ws.py:472 触发 process_h5_ai_reply 前,增加拦截——当 ai_reply_mode == off conversation.status == serving禁止触发 serving 态 与 off 态下路径 B 触发率=0;修复「接单后 AI 仍自动回复员工」异常
P0-5 工具栏 🤖 按钮 + PopoverReplyBox.vue 工具栏新增 🤖 图标按钮,点击 el-popover 弹出三态单选(显示当前态、可切换),切换即时调 API 持久化 按钮可见、Popover 三态可点选;切换后当前态实时刷新;失败有提示/回滚
P0-6 单会话持久化:每个 Conversation 独立保存 ai_reply_mode,切换仅作用于当前会话、立即写后端落库;切到另一会话读取该会话自己的模式 多会话间互不影响;刷新/重进会话后保持已选模式
P0-7 employee_and_agent 的坐席可见性:当模式为 employee_and_agent 时,AI 自动回复在推员工的同时,需同步让坐席端可见(确保 AI 回复消息持久化并广播 new_message(sender=ai) 给坐席) 坐席工作台能看到 AI 的自动回复内容,便于介入(如路径 A 当前仅推员工,需确认坐席侧展示链路)

P1Should have

ID 需求 验收标准
P1-1 乐观更新 + 失败回滚:切换时 UI 先即时反映,API 失败时回滚到原值并 ElMessage 报错 网络异常下状态不漂移
P1-2 AI 回复来源标识:在坐席端 AI 自动回复消息旁展示「AI 自动回复」标识/来源标签,尤其 employee_and_agent 时让坐席明确这是 AI 发的、便于接管 坐席可区分 AI 自动消息与人工消息
P1-3 切换轻提示:切换成功后 ElMessage.success("已切换为:AI回复-仅员工/员工+坐席/关闭") 坐席明确当前生效模式
P1-4 在途任务边界:切换 off/serving 后,对已提交但尚未完成的 H5 AI 回复 task 不强制中断,仅阻止新触发(明确边界,避免打断在途回复造成半截消息) 不出现半截/重复消息

P2Nice to have

ID 需求 验收标准
P2-1 按钮当前态可视化🤖 按钮用色点/图标变化表达当前态(如 off 置灰、employee_and_agent 高亮),hover tooltip 显示当前态文字 无需打开 Popover 即可一眼识别模式
P2-2 快捷键切换:支持快捷键在三种模式间循环切换 坐席可无鼠标操作
P2-3 操作审计留痕:记录「谁、何时、从 X 切到 Y」的切换日志 管理/复盘可追溯
P2-4 会话详情/设置可查看:在会话详情或设置区也可只读查看当前 ai_reply_mode 多入口可见

六、UI 设计稿

6.1 工具栏按钮位置(基于既有 ReplyBox.vue 工具栏结构)

既有工具栏结构:[常规工具组 toolbar-left] | tb-sep | [AI 工具组 AiAssistToolbar: 💡自动补齐 🎚️语气 🖌️润色 🔄改写]

🤖 开关是「状态控制」而非「文字加工动作」,建议作为最左侧独立段(与常规工具组之间用 tb-sep 分隔),使其在一眼可见的位置且不与 4 个主动 AI 按钮混淆:

┌──────────────────────────────────────────────────────────────────────────────────┐
│  回复框 ReplyBox                                                                  │
│ ┌── 拖拽手柄 ────────────────────────────────────────────────────────────────┐   │
│ │ [🤖 AI自动回复] │sep│ ✂️截图 📷拍照 │sep│ 😊表情 📎文件 │sep│ 👥邀请 │sep│ 💡 🎚️ 🖌️ 🔄 │  ← 工具栏 chat-toolbar
│ └──────────────────────────────────────────────────────────────────────────┘   │
│ ┌──────────────────────────────────────────────────────────────────────────┐   │
│ │  textarea ……                                            🎤        发 送   │   │
│ └──────────────────────────────────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────────────────────────────────┘

说明:
- 🤖 按钮使用既有 .tb-btn 样式(32×28hover 变 --accent),单独成组,左侧以 tb-sep 与常规工具组隔开。
- 当前态表达(P2-1,P0 至少保证 Popover 内清晰显示):
    employee_only     → 🤖 正常色
    employee_and_agent → 🤖 高亮(如蓝色描边/小蓝点)
    off               → 🤖 置灰(如灰色 + 斜杠感)
- 点击 🤖 触发 el-popoverreference=🤖 按钮,placement=bottom-start)。

6.2 Popover 三态布局(ASCII 草图)

┌───────────────────────────────────────┐
│  🤖  AI 自动回复模式                    │
│  ─────────────────────────────────────  │
│  (●) 仅回复员工            (默认)      │
│        AI 只回复员工,与现状一致          │
│                                          │
│  (○) 回复员工 + 推送坐席                 │
│        AI 回复员工,坐席端同步可见        │
│                                          │
│  (○) 关闭自动回复                        │
│        AI 不再自动回复任何消息            │
│  ─────────────────────────────────────  │
│  当前:仅回复员工                        │
└───────────────────────────────────────┘

交互:
- 三态为单选(el-radio-group),默认选中当前会话的 ai_reply_mode。
- 选中即触发:调用 API 写库 → 成功 → 关闭 Popover + ElMessage 提示 + 按钮当前态刷新。
- 失败 → 回滚选中项 + ElMessage 报错,Popover 保持打开。

6.3 状态机上下文(仅作背景,不在本需求改动)

ai_handling → queued → serving → pending_close → resolved,其中 serving = 坐席已接管。本开关为正交ai_reply_mode,不修改该状态机;但路径 B 的拦截条件包含 status == serving(见 P0-4)。


七、待确认问题清单(Open Questions

# 问题 影响 建议/所需决策方
Q1 employee_and_agent 的「推送给坐席」在**路径 A(企微 App 接入)**下如何保证坐席可见?路径 A 当前仅 wecom send 给员工,坐席侧是否已存在该 AI 消息的持久化+广播链路? 影响 P0-7 实现方式 架构师确认广播/持久化机制
Q2 路径 B 在 status == ai_handling / queued 时是否仍应触发 AI(本期是否仅新增 off + serving 两个拦截,其余 status 保持零门控现状)? 影响 P0-4 拦截条件边界 产品经理+架构师确认
Q3 切换 off/serving 后,对已提交未完成的 H5 AI 回复 task 是否中断?(建议不中断在途,仅阻止新触发) 影响 P1-4 边界 架构师确认
Q4 字段/枚举命名确认:ai_reply_mode vs reply_scope?枚举值 employee_and_agent 是否易被误解为「员工和坐席都来对话」?是否需要更直白命名(如 agent_visible)? 影响数据模型与前端文案 产品经理+架构师
Q5 API 形态:扩展现有 PATCH /api/conversations/{id},还是新增专用 PUT /api/conversations/{id}/ai-reply-mode 影响 P0-2 前后端约定 架构师/后端
Q6 存量会话迁移后,路径 B 在 serving 下从「会回复」变为「不回复」属行为变更,是否可接受为预期修复(非 breaking)? 影响 P0-4 / 迁移说明 产品经理确认
Q7 🤖 按钮最终放置位置(最左独立段 / AI 工具组内 / 最右)与当前态的可视化方案(色点/图标/斜杠)? 影响 6.1/6.2 UI 实现 产品经理+前端