PRD - AI 回复三态开关(坐席工作台)
REQ编号: REQ-坐席-010
版本: v1.0
优先级: P0(含现状行为缺口修复)
日期: 2026-08-04
负责人: 产品经理 许清楚(Xu)
范围: 简单 PRD(无竞品/市场分析)
一、需求概述
坐席工作台(src/frontend-agent,Vue3 + 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.py 的 Conversation 模型当前无 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) 只推员工 |
| 路径 B(H5 / WebSocket 接入) |
src/backend/app/api/h5.py:939 与 src/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 需求池(按优先级)
P0(Must have)
| ID |
需求 |
验收标准 |
| P0-1 |
新增正交枚举字段 ai_reply_mode(employee_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_reply 在 status==ai_handling 基础上,额外增加 ai_reply_mode != off 判断;当 off 时即使 ai_handling 也不触发 |
off 态下路径 A 触发率=0;employee_only / employee_and_agent 下路径 A 行为同现状(仅推员工) |
| P0-4 |
路径 B 门控 + 接单后停 AI(修复缺口):h5.py:939 与 ws.py:472 触发 process_h5_ai_reply 前,增加拦截——当 ai_reply_mode == off 或 conversation.status == serving 时禁止触发 |
serving 态 与 off 态下路径 B 触发率=0;修复「接单后 AI 仍自动回复员工」异常 |
| P0-5 |
工具栏 🤖 按钮 + Popover:ReplyBox.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 当前仅推员工,需确认坐席侧展示链路) |
P1(Should 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 不强制中断,仅阻止新触发(明确边界,避免打断在途回复造成半截消息) |
不出现半截/重复消息 |
P2(Nice 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 按钮混淆:
6.2 Popover 三态布局(ASCII 草图)
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 实现 |
产品经理+前端 |