## H5 员工端 v4 (2026-07-13 00:48 已部署)
- 人工按钮三态文案统一为"人工坐席"
- 按钮位置移至发送键和语音按钮上方(垂直堆叠)
- 点按钮直接调 store.shakeAgent(),删除 CallAgentModal 弹窗动画
- 截图快捷键提示改为"截图->粘贴:Alt+Shift+A-Ctrl+V ---> Ctrl+V"
- 移动端隐藏截图提示(CSS 媒体查询)
- AI转人工提示改为"已为您呼叫人工坐席,请稍等!"
- 坐席接入提示改为"坐席正在查看您的信息,请等待处理回复!"
- 删除"摇铃呼叫坐席"入口和文案
- 删除孤儿组件 MessageList.vue + shake 动画 CSS
## H5 员工端 v5 (2026-07-13 02:08 已部署)
- RightPanel v2.1:删除"软件安装"和"资源权限"标签页
- 移除标签栏,智能推荐(DynamicRecommend)直接展示
- 删除 SoftwareDownloads/ApprovalLinks 引用和相关 CSS
## AI 对话链路全栈改造 Phase 1-6 (已部署)
- Phase 1: Dify JSON输出 + 后端blocking解析 + 双WS推送 + 错误降级
- Phase 2: 关键词收窄(~25强意图词) + 两级分类Prompt + 删除前端checkApprovalIntent
- Phase 3: WS扩展(ai_thinking+dynamic_recommend) + ai_structured气泡 + RightPanel v2 + 选项回传
- Phase 4: VisionService接入 + 图片消息融合(5秒窗口) + 降级策略
- Phase 5: 坐席端ai_thinking指示器 + ai_structured/byod_card渲染 + handleNewMessage修复
- Phase 6: diagnosis_stage(6值) + response_time_ms计时 + 慢响应告警(>10s)
## 坐席端 v5 (2026-07-13 01:38 已部署)
- ai_structured/byod_card 只读渲染
- AI思考指示器 UI
- handleNewMessage 透传 msg_type/extra_data 修复
- 布局优化v2.0: QuickReplyBar L1+L2悬浮 + ReplyBox左右分区 + 右栏260/560px切换
- 键盘快捷键v2.3: 纯数字路由 + ESC分层撤销 + Shift+Space用event.code
## 上下文感知智能诊断闭环 (2026-07-12 已部署)
- 三层诊断(API→Script→AI) + 三段排队(VIP→info_locked→not locked)
- 答题插队 + 五场景关闭
- 迁移052(6表+6列) + queue_service + quiz_service + closing_service
- H5前端: QueueWaiting + RightPanel双Tab + InputBar三态 + ResolveConfirmCard
- 坐席前端: pending_close结单流程 + 信息锁定(Dify步骤完成+有效回答率≥70%)
## 知识库迭代3 (2026-07-12 已部署)
- 分诊交互(H5+坐席+Dify独立应用)
- 拓扑预览(ECharts只读)
- 代答排除(4种匹配器: keyword/regex/intent/category)
- 迁移051 + 44文件43测试通过
## 后端变更
- 6个Python文件改造(h5_ai_task.py/h5.py/ai_service.py/closing_service.py等)
- funny_phrase_service.py: shake/connected/keyword 默认文案更新
- session_service.py: 企微消息文案同步
- 新增: queue.py/quiz.py/triage.py/exclusion_rules.py 等API端点
- 新增: diagnostic.py/quiz.py/triage_session.py 等模型
- 新增: closing_service/queue_service/quiz_service/triage_service 等服务
## 文档更新
- CHANGELOG.md: 新增 [未发布] 区全部变更记录
- 项目管理主文档 v2.5: 新增v0.7.3版本 + 已完成看板 + 最近搞定
- 版本记录: 新增v0.7.3条目
- AI对话链路实施计划: Phase 1-6 全部标记✅已实施
- 新增架构图/时序图/类图(mermaid)
## 部署路径修正
- 服务器项目根路径: /opt/wecom-it-desk/
- 所有前端dist均为ro bind mount,只能在宿主机源路径操作
- 服务器nginx /h5/ 是静态文件服务(非proxy_pass)
- elFinder上传二进制不可靠(MD5不匹配),改用base64分块上传
54 KiB
坐席端 AI 辅助消息框与布局优化 — 系统架构设计
文档版本: v1.0
日期: 2026-07-11
架构师: 宋献 (Simon)
关联 PRD:docs/02-产品需求/坐席端AI辅助消息框-PRD.md
关联布局方案:docs/02-产品需求/坐席端布局优化建议.md
技术栈: Vue 3 + TypeScript + Element Plus + FastAPI + Dify AI
目录
Part A: 系统设计
1. 架构概述
1.1 功能范围
本设计覆盖两大模块:
| 模块 | 功能 | PRD 参考 |
|---|---|---|
| AI 辅助消息框 | 实时自动补齐、语气调整、文字润色、智能改写 | 坐席端AI辅助消息框-PRD.md §2 |
| 布局优化 | 回复建议区、工具栏重构、右栏训练区、放大/缩小开关 | 坐席端布局优化建议.md §3 |
1.2 系统架构图
┌──────────────────────────────────────────────────────────────────┐
│ 坐席工作台 (Vue3 + Element Plus) │
│ │
│ ┌──────────┐ ┌───────────────────┐ ┌───────────────────────┐ │
│ │ 左栏 │ │ 中栏 │ │ 右栏 │ │
│ │ 会话用户 │ │ ┌──────────────┐ │ │ ┌───────────────────┐ │ │
│ │ 列表 │ │ │ UserInfoBar │ │ │ │ PanelModeToggle │ │ │
│ │ +待办 │ │ ├──────────────┤ │ │ │ (正常/放大) │ │ │
│ │ │ │ │ Troubleshoot │ │ │ ├───────────────────┤ │ │
│ │ │ │ ├──────────────┤ │ │ │ AiTrainingPanel │ │ │
│ │ │ │ │ 消息列表 │ │ │ │ ├ SmartTagEditor │ │ │
│ │ │ │ ├──────────────┤ │ │ │ ├ QualityFeedback │ │ │
│ │ │ │ │ReplySuggestArea│ │ │ │ ├ KnowledgeContrib│ │ │
│ │ │ │ │ (AI推荐+快回) │ │ │ │ └ UsageStats │ │ │
│ │ │ │ ├──────────────┤ │ │ └───────────────────┘ │ │
│ │ │ │ │ ReplyBox │ │ │ │ │
│ │ │ │ │ ┌──────────┐ │ │ │ │ │
│ │ │ │ │ │工具栏 │ │ │ │ │ │
│ │ │ │ │ │常规|AI │ │ │ │ │ │
│ │ │ │ │ └──────────┘ │ │ │ │ │
│ │ │ │ │ textarea │ │ │ │ │
│ │ │ │ └──────────────┘ │ │ │ │
│ └──────────┘ └───────────────────┘ └───────────────────────┘ │
└──────────────────────────┬───────────────────────────────────────┘
│ HTTP / WebSocket
▼
┌──────────────────────────────────────────────────────────────────┐
│ FastAPI 后端 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ wingman.py (路由层) │ │
│ │ POST /wingman/autocomplete ← 自动补齐 │ │
│ │ POST /wingman/tone-adjust ← 语气调整 │ │
│ │ POST /wingman/polish ← 文字润色 │ │
│ │ POST /wingman/rewrite ← 智能改写 │ │
│ │ + 现有: draft / summary / tags │ │
│ └────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼────────────────────────────────┐ │
│ │ WingmanService (服务层) │ │
│ │ generate_completion() ← 新增: 自动补齐 │ │
│ │ adjust_tone() ← 新增: 语气调整 │ │
│ │ polish_text() ← 新增: 文字润色 │ │
│ │ rewrite_versions() ← 新增: 智能改写 │ │
│ │ + 现有: generate_draft / generate_summary / suggest_tags│ │
│ │ │ │
│ │ _call_wingman_api() ← 改造: 支持 temperature 参数 │ │
│ │ _build_context_messages() ← 复用: 角色映射 │ │
│ └────────────────────────┬────────────────────────────────┘ │
│ │ httpx (OpenAI 兼容格式) │
└───────────────────────────┼──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Dify AI (外部服务) │
│ Wingman Agent: /chat/completions (OpenAI 兼容) │
│ RAGFlow: 知识库检索 (智能改写时调用) │
└──────────────────────────────────────────────────────────────────┘
1.3 与现有系统的关系
| 现有组件 | 关系 | 说明 |
|---|---|---|
WingmanService |
扩展 | 新增 4 个方法,改造 _call_wingman_api() 支持 temperature |
wingman.py 路由 |
扩展 | 新增 4 个端点 + Pydantic 请求模型 |
wingman.ts API 层 |
扩展 | 新增 4 个前端 API 调用函数 |
ReplyBox.vue |
改造 | 工具栏重构 + AI 工具按钮 + 补齐逻辑 |
AiAssistantPanel.vue |
全面重构 | 移除回复前功能,改为训练区 |
AiRecommendInline.vue |
删除 | 功能被 AiRecommendBar.vue 替代 |
AiSuggestReply.vue |
删除 | 功能合并到 AiRecommendBar.vue |
QuickReplyPanel.vue |
改造 | 重命名为 QuickReplyBar.vue,横向布局 |
2. 后端架构设计
2.1 WingmanService 扩展设计
现有 WingmanService 包含 4 个方法(generate_draft / generate_summary / suggest_tags / generate_knowledge_suggestion),全部通过 _call_wingman_api() 调用 Dify。本设计新增 4 个方法并改造底层调用。
新增方法签名:
class WingmanService:
# === 现有方法(保持不变) ===
# generate_draft()
# generate_summary()
# suggest_tags()
# generate_knowledge_suggestion()
# === 新增方法 ===
async def generate_completion(
self,
conversation_id: str,
current_text: str,
messages: List[Dict[str, Any]],
max_length: int = 80,
) -> Dict[str, Any]:
"""自动补齐:根据坐席当前输入内容补齐下一句"""
async def adjust_tone(
self,
conversation_id: str,
selected_text: str,
full_text: str,
tone: str, # professional / friendly / concise
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""语气调整:将选中文字改写为指定风格"""
async def polish_text(
self,
conversation_id: str,
text: str,
action: str, # expand / compress / correct
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""文字润色:对输入框全部内容进行扩写/压缩/纠错"""
async def rewrite_versions(
self,
conversation_id: str,
current_text: str,
messages: List[Dict[str, Any]],
generate_count: int = 3,
include_knowledge: bool = True,
) -> Dict[str, Any]:
"""智能改写:基于对话上下文+知识库生成多个备选版本"""
2.2 _call_wingman_api 改造
当前问题:_call_wingman_api() 的 temperature 硬编码为 0.3,所有方法共用。新功能需要不同 temperature:
| 方法 | temperature | 理由 |
|---|---|---|
| generate_completion | 0.2 | 补齐需要高确定性,避免创造性发散 |
| adjust_tone | 0.3 | 改写需保持原意,适度创造性 |
| polish_text | 0.3 | 润色需保持原意,适度创造性 |
| rewrite_versions | 0.6 | 改写需要多样化输出,提高创造性 |
| generate_draft (现有) | 0.3 | 保持不变 |
| generate_summary (现有) | 0.3 | 保持不变 |
改造方案:
# 改造前(当前代码)
async def _call_wingman_api(
self, context_messages: List[Dict[str, str]]
) -> Optional[str]:
payload = {
"model": "Chat",
"messages": context_messages,
"stream": False,
"temperature": 0.3, # ← 硬编码
}
# 改造后
async def _call_wingman_api(
self,
context_messages: List[Dict[str, str]],
temperature: float = 0.3, # ← 新增参数,默认值保持兼容
) -> Optional[str]:
payload = {
"model": "Chat",
"messages": context_messages,
"stream": False,
"temperature": temperature,
}
兼容性:现有 4 个方法调用时不传 temperature 参数,自动使用默认值 0.3,行为不变。
2.3 上下文构建策略
现有 _build_context_messages() 将完整对话历史构建为 OpenAI Chat 格式。新功能需要差异化上下文:
| 方法 | 上下文消息条数 | 附加内容 | 原因 |
|---|---|---|---|
| generate_completion | 最近 5 条 | 当前输入文本拼入最后一条 user 消息 | 补齐需要最近上下文 + 当前输入 |
| adjust_tone | 最近 5 条 | 选中文字 + 完整输入内容作为指令 | 改写需要理解选中文字的上下文 |
| polish_text | 最近 5 条 | 全部输入内容作为指令 | 润色针对输入框全部内容 |
| rewrite_versions | 最近 10 条 | 知识库检索结果(可选) | 改写需要更完整上下文 + 知识库引用 |
实现方式:复用 _build_context_messages() 构建基础对话上下文,然后在方法内部追加功能特定的指令消息:
async def generate_completion(self, ...):
# 1. 构建基础上下文(最近5条对话 → system prompt + 历史消息)
context = self._build_context_messages(messages, self._COMPLETION_SYSTEM_PROMPT)
# 2. 追加当前输入作为最后一条 user 消息
context.append({
"role": "user",
"content": f"坐席正在输入的内容:{current_text}\n请补齐下一句话。"
})
# 3. 调用 Dify(temperature=0.2)
result = await self._call_wingman_api(context, temperature=0.2)
2.4 智能改写的知识库集成
rewrite_versions() 在生成"带知识库引用"版本时需要调用 RAGFlow 检索:
坐席点击"改写"
→ 后端从对话消息提取关键词
→ 调用 RAGFlow API 检索相关知识
→ 将检索结果注入 Dify prompt
→ Dify 生成 3 个版本(含知识库引用版本)
RAGFlow 调用:复用现有 RAGFlowClient(位于 app/integrations/),无需新建。若 RAGFlow 不可用,降级为仅基于对话上下文生成 2 个版本。
2.5 降级策略
与现有方法保持一致:Dify 不可用时返回默认值而非抛异常。
# 降级返回示例(generate_completion)
{
"completion": "",
"confidence": 0.0,
"error": "Wingman 服务暂不可用"
}
# 降级返回示例(rewrite_versions)
{
"versions": [],
"error": "AI 服务暂不可用,请稍后重试"
}
3. 前端架构设计
3.1 组件架构图
Workspace.vue (主布局)
├── ConversationList.vue (左栏 - 会话用户列表)
│ └── TodoPanel.vue (待办面板)
│
├── ChatArea.vue (中栏)
│ ├── UserInfoBar.vue (用户信息栏 - 详情默认折叠)
│ ├── TroubleshootBar.vue (排查步骤栏 - 默认折叠为图标条)
│ ├── MessageList.vue (消息列表)
│ ├── ReplySuggestArea.vue (回复建议区 - 新增)
│ │ ├── AiRecommendBar.vue (AI推荐条 - 合并自 AiRecommendInline + AiSuggestReply)
│ │ └── QuickReplyBar.vue (快速回复条 - 改造自 QuickReplyPanel)
│ └── ReplyBox.vue (输入框 - 改造)
│ ├── 工具栏 (单行左右分区)
│ │ ├── 常规工具组: [截图] [拍照] [表情] [文件] [邀请]
│ │ └── AI工具组: [补齐toggle] [语气] [润色] [改写]
│ ├── GhostTextOverlay.vue (幽灵文字覆盖层 - 新增)
│ ├── ToneAdjustPopover.vue (语气调整浮层 - 新增)
│ ├── PolishPanel.vue (润色精修面板 - 新增)
│ └── RewritePanel.vue (改写选择面板 - 新增)
│
└── AiAssistantPanel.vue (右栏 - 全面重构)
├── PanelModeToggle.vue (模式切换: 正常/放大 - 新增)
└── AiTrainingPanel.vue (训练区主体 - 新增)
├── SmartTagEditor.vue (智能标注)
├── QualityFeedback.vue (质量反馈)
├── KnowledgeContribute.vue (知识贡献)
└── UsageStats.vue (使用统计)
3.2 Composable 设计
新增 3 个 composable 封装 AI 辅助逻辑:
| Composable | 职责 | 状态范围 |
|---|---|---|
useAutoComplete.ts |
自动补齐的 debounce、请求、幽灵文字管理 | ReplyBox 局部 |
useAiTextTools.ts |
语气/润色/改写的统一调用和结果管理 | ReplyBox 局部 |
usePanelMode.ts |
右栏放大/缩小模式切换 | Workspace 全局 |
useAutoComplete.ts 核心逻辑:
export function useAutoComplete(
textareaRef: Ref<HTMLTextAreaElement>,
conversationId: Ref<string>,
enabled: Ref<boolean>,
) {
const ghostText = ref('') // 幽灵文字内容
const isLoading = ref(false) // 加载状态
const abortController = ref<AbortController | null>(null)
// debounce 800ms,输入停顿后触发
const debouncedFetch = useDebounceFn(async (text: string) => {
if (!enabled.value || text.length < 5) {
ghostText.value = ''
return
}
// 取消上一个请求
abortController.value?.abort()
abortController.value = new AbortController()
isLoading.value = true
try {
const result = await wingmanApi.autocomplete(
conversationId.value,
{ current_text: text, cursor_position: text.length },
{ signal: abortController.value.signal },
)
ghostText.value = result.completion || ''
} catch (e) {
if (!isAbortError(e)) ghostText.value = ''
} finally {
isLoading.value = false
}
}, 800)
// Tab 接受补齐
function acceptCompletion() {
// 将 ghostText 追加到输入框
// 清空 ghostText
}
// Esc 清除补齐
function dismissCompletion() {
ghostText.value = ''
}
return { ghostText, isLoading, debouncedFetch, acceptCompletion, dismissCompletion }
}
usePanelMode.ts 核心逻辑:
export function usePanelMode() {
const mode = ref<'normal' | 'expanded'>('normal')
const panelWidth = computed(() =>
mode.value === 'normal' ? '260px' : '560px'
)
const centerColVisible = computed(() =>
mode.value === 'normal'
)
function toggleMode() {
mode.value = mode.value === 'normal' ? 'expanded' : 'normal'
}
return { mode, panelWidth, centerColVisible, toggleMode }
}
3.3 幽灵文字实现方案
PRD 确认为内联幽灵文字。实现方式为 textarea 上方覆盖透明文字层:
┌──────────────────────────────┐
│ textarea (z-index: 1) │ ← 文字颜色: #303133 (正常)
│ 透明背景,文字不可见的部分 │ 选中文字后的补齐文字不可见
├──────────────────────────────┤
│ ghost overlay (z-index: 2) │ ← 文字颜色: #C0C4CC (灰色)
│ pointer-events: none │ 仅在 textarea 文字末尾之后显示
│ 位置与 textarea 文字完全对齐 │ 通过 mirror div 技术计算光标坐标
└──────────────────────────────┘
技术要点:
- 创建一个与 textarea 样式完全一致的
<div>(mirror div),用于计算光标坐标 - ghost overlay 定位在光标坐标处,显示灰色补齐文字
pointer-events: none确保不拦截鼠标事件- Tab 键监听在 textarea 的
keydown事件中,e.key === 'Tab'时阻止默认行为并接受补齐 - 继续输入时自动清除 ghost text(
input事件触发 debounce 重新请求)
Mirror div 技术(用于精确计算光标位置):
function getCursorCoordinates(textarea: HTMLTextAreaElement, position: number) {
const div = document.createElement('div')
// 复制 textarea 的所有影响文字布局的样式
const styles = window.getComputedStyle(textarea)
Object.assign(div.style, {
position: 'absolute',
visibility: 'hidden',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
// ... 复制 font-family, font-size, padding, border, width 等
})
// 截取光标位置之前的文字
div.textContent = textarea.value.substring(0, position)
document.body.appendChild(div)
// 创建一个 span 标记光标位置
const span = document.createElement('span')
span.textContent = '|'
div.appendChild(span)
// 获取 span 的坐标(相对于 textarea)
const coords = {
x: span.offsetLeft - textarea.scrollLeft,
y: span.offsetTop - textarea.scrollTop,
}
document.body.removeChild(div)
return coords
}
3.4 前端 API 层扩展
在 frontend-agent/src/api/wingman.ts 中新增 4 个函数:
// 自动补齐
export async function autocomplete(
conversationId: string,
data: { current_text: string; cursor_position: number; max_length?: number },
config?: { signal?: AbortSignal },
): Promise<{ completion: string; confidence: number }>
// 语气调整
export async function toneAdjust(
conversationId: string,
data: { selected_text: string; full_text: string; tone: string },
): Promise<{ rewritten_text: string; tone: string; changes_summary: string }>
// 文字润色
export async function polish(
conversationId: string,
data: { text: string; action: string },
): Promise<{ polished_text: string; action: string; changes_summary: string }>
// 智能改写
export async function rewrite(
conversationId: string,
data: { current_text: string; generate_count?: number; include_knowledge?: boolean },
): Promise<{ versions: Array<{ text: string; style: string; source: string }> }>
4. 布局重构架构设计
4.1 CSS 变量体系
:root {
/* === 布局尺寸 === */
--sidebar-width: 260px; /* 左栏: 280px → 260px */
--assistant-panel-width: 260px; /* 右栏正常: 320px → 260px */
--assistant-panel-expanded: 560px; /* 右栏放大: 新增 */
/* === 右栏模式控制 === */
--center-col-flex: 1; /* 正常: 中栏弹性 */
--center-col-width: auto; /* 正常: 中栏自动 */
--center-col-overflow: visible; /* 正常: 可见 */
/* === 回复建议区颜色 === */
--suggest-ai-bg: #FAECE7; /* AI推荐背景 - 珊瑚色 */
--suggest-ai-border: #F0997B;
--suggest-ai-text: #993C1D;
--suggest-quick-bg: #EAF3DE; /* 快速回复背景 - 绿色 */
--suggest-quick-border: #97C459;
--suggest-quick-text: #27500A;
/* === 工具栏分组颜色 === */
--toolbar-conv-bg: #E1F5EE; /* 常规工具 - 绿色系 */
--toolbar-ai-bg: #FAEEDA; /* AI工具 - 琥珀色系 */
/* === 回复建议区高度 === */
--suggest-area-height: auto; /* 动态: 选中后 0 */
--suggest-ai-height: 44px; /* AI推荐区固定高度 */
--suggest-quick-height: 56px; /* 快速回复区固定2层 */
}
4.2 右栏放大/缩小模式实现
<!-- Workspace.vue -->
<template>
<div class="workspace" :class="{ 'panel-expanded': panelMode === 'expanded' }">
<aside class="sidebar"><ConversationList /></aside>
<main class="center-col"><ChatArea /></main>
<aside class="assistant-panel"><AiAssistantPanel /></aside>
</div>
</template>
<style scoped>
.workspace {
display: flex;
height: 100vh;
transition: all 0.3s ease;
}
.sidebar {
width: var(--sidebar-width);
flex-shrink: 0;
}
.center-col {
flex: var(--center-col-flex);
width: var(--center-col-width);
overflow: var(--center-col-overflow);
min-width: 0;
transition: all 0.3s ease;
}
.assistant-panel {
width: var(--assistant-panel-width);
flex-shrink: 0;
transition: width 0.3s ease;
}
/* 放大模式 */
.workspace.panel-expanded {
--assistant-panel-width: var(--assistant-panel-expanded);
--center-col-flex: 0;
--center-col-width: 0;
--center-col-overflow: hidden;
}
</style>
4.3 回复建议区动态高度
<!-- ReplySuggestArea.vue -->
<template>
<div
class="reply-suggest-area"
:class="{ collapsed: !shouldShow }"
>
<div class="ai-recommend-section" v-if="shouldShow">
<AiRecommendBar />
</div>
<div class="quick-reply-section" v-if="shouldShow">
<QuickReplyBar />
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
inputText: string // 来自 ReplyBox 的输入框内容
}>()
// 输入框为空时展开,有内容时缩回
const shouldShow = computed(() => props.inputText.length === 0)
</script>
<style scoped>
.reply-suggest-area {
max-height: calc(var(--suggest-ai-height) + var(--suggest-quick-height));
overflow: hidden;
transition: max-height 0.3s ease;
}
.reply-suggest-area.collapsed {
max-height: 0;
}
</style>
4.4 工具栏单行左右分区
<!-- ReplyBox.vue 工具栏部分 -->
<template>
<div class="toolbar">
<!-- 左侧: 常规工具 -->
<div class="toolbar-left">
<button class="tool-btn conv" @click="screenshot">截图</button>
<button class="tool-btn conv" @click="camera">拍照</button>
<button class="tool-btn conv" @click="emoji">表情</button>
<button class="tool-btn conv" @click="file">文件</button>
<button class="tool-btn conv" @click="invite">邀请</button>
</div>
<!-- 右侧: AI工具 + 发送 -->
<div class="toolbar-right">
<button
class="tool-btn ai"
:class="{ active: autoCompleteEnabled }"
@click="autoCompleteEnabled = !autoCompleteEnabled"
>补齐</button>
<button class="tool-btn ai" @click="showTonePopover" :disabled="!hasSelection">语气</button>
<button class="tool-btn ai" @click="showPolishPanel">润色</button>
<button class="tool-btn ai" @click="showRewritePanel">改写</button>
<button class="tool-btn send" @click="send">发送</button>
</div>
</div>
</template>
5. 数据流设计
5.1 自动补齐数据流
坐席输入文字
→ textarea input 事件
→ useAutoComplete.debouncedFetch(text) [debounce 800ms]
→ 取消上一个 AbortController
→ 调用 wingmanApi.autocomplete(conversationId, { current_text })
→ POST /api/conversations/{id}/wingman/autocomplete
→ FastAPI 路由层
→ _get_recent_messages(conversationId, db, limit=5)
→ WingmanService.generate_completion(conversationId, text, messages)
→ _build_context_messages(messages, _COMPLETION_SYSTEM_PROMPT)
→ 追加 current_text 到上下文末尾
→ _call_wingman_api(context, temperature=0.2)
→ Dify API POST /chat/completions
→ 解析 choices[0].message.content
→ 返回 { completion, confidence }
→ 前端设置 ghostText.value = result.completion
→ ghost overlay 渲染灰色文字
坐席按 Tab
→ acceptCompletion()
→ inputText.value += ghostText.value
→ ghostText.value = ''
5.2 语气调整数据流
坐席选中文字 → 点击"语气"按钮 → 弹出 ToneAdjustPopover
→ 选择"专业/友好/简洁"
→ 调用 wingmanApi.toneAdjust(conversationId, { selected_text, full_text, tone })
→ POST /api/conversations/{id}/wingman/tone-adjust
→ WingmanService.adjust_tone(...)
→ _build_context_messages(messages, _TONE_SYSTEM_PROMPT)
→ 注入 selected_text + full_text + tone 指令
→ _call_wingman_api(context, temperature=0.3)
→ 返回 { rewritten_text, tone, changes_summary }
→ 浮层显示"原文 → 改写文"对比
→ 坐席点击"替换" → 用 rewritten_text 替换选中区域
→ 坐席点击"取消" → 关闭浮层,不修改
5.3 右栏模式切换数据流
坐席点击 PanelModeToggle "放大"按钮
→ usePanelMode.toggleMode()
→ mode.value = 'expanded'
→ CSS 变量更新:
--assistant-panel-width → 560px
--center-col-flex → 0
--center-col-width → 0
--center-col-overflow → hidden
→ CSS transition 0.3s 平滑动画
→ 中栏压缩隐藏,右栏展开为 560px
坐席点击"正常"按钮
→ toggleMode() → mode.value = 'normal'
→ CSS 变量恢复
→ 中栏恢复显示,右栏缩回 260px
6. 关键设计决策
决策 1:自动补齐用 Mirror Div 而非 contenteditable
选择:保持 <textarea> + mirror div 计算 + ghost overlay 覆盖
理由:
contenteditablediv 虽然原生支持幽灵文字,但会破坏现有的 Ctrl+Enter 发送、Enter 换行等快捷键逻辑- textarea 的所有现有功能(粘贴图片、拖拽文件等)无需改动
- mirror div 是业界成熟方案(VS Code、GitHub Copilot 均用此方案)
- 实现复杂度可控,仅需一个坐标计算函数
决策 2:补齐 API 用 AbortController 而非 WebSocket
选择:HTTP POST + AbortController 取消
理由:
- 补齐请求频率高(每次停顿 800ms 触发),但每次请求轻量(< 500 字符)
- WebSocket 需要新增消息类型和连接管理,增加复杂度
- AbortController 可在浏览器层取消请求,避免无效响应
- 现有 WingmanService 全部使用 HTTP 同步调用,保持一致性
决策 3:语气/润色/改写用浮层面板而非全屏弹窗
选择:el-popover / el-drawer 浮层
理由:
- 坐席需要在操作时仍能看到对话上下文
- 全屏
el-dialog会遮挡消息列表,打断工作流 - 浮层可定位在工具栏按钮附近,视线不跳转
- 润色和改写面板使用
el-drawer从右侧滑出,宽度 400px,不遮挡消息列表
决策 4:右栏模式切换用 CSS 变量而非 v-if
选择:CSS 变量 + class 切换,不用 v-if 销毁/重建组件
理由:
v-if会导致中栏组件销毁/重建,丢失消息列表滚动位置和输入框内容- CSS 变量方案仅改变视觉布局,组件实例保持存活
overflow: hidden+width: 0确保中栏不可见但不销毁- 切回正常模式时立即可见,无需重新渲染
决策 5:回复建议区用 max-height 动画而非 v-show
选择:max-height transition + collapsed class
理由:
v-show(display: none)会导致回复建议区下方布局跳动max-height从auto到0的过渡更平滑- 组件实例保持存活,AI推荐数据不丢失
- 选中条目后缩回的动画体验更流畅
决策 6:_call_wingman_api 改造为可选参数而非新增方法
选择:在现有方法签名上新增 temperature 参数,默认值 0.3
理由:
- 现有 4 个方法的调用无需修改(默认值兼容)
- 避免代码重复(不需要新增
_call_wingman_api_with_temp()) - 未来其他方法也可按需传递 temperature
- 改动面最小,降低回归风险
Part B: 接口与数据结构
7. API 规范
7.1 自动补齐
POST /api/conversations/{conversation_id}/wingman/autocomplete
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| current_text | string | 是 | 坐席当前输入的文字 |
| cursor_position | int | 是 | 光标位置(字符偏移量) |
| max_length | int | 否 | 补齐最大长度,默认 80 |
响应:
{
"code": 200,
"data": {
"completion": "正在查看相关工单记录,请稍候。",
"confidence": 0.85
}
}
7.2 语气调整
POST /api/conversations/{conversation_id}/wingman/tone-adjust
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| selected_text | string | 是 | 坐席选中的文字 |
| full_text | string | 是 | 输入框全部内容 |
| tone | string | 是 | professional / friendly / concise |
响应:
{
"code": 200,
"data": {
"rewritten_text": "经排查,您的VPN连接异常由本地网络DNS解析超时引起。",
"tone": "professional",
"changes_summary": "去除口语化表达,增加技术术语"
}
}
7.3 文字润色
POST /api/conversations/{conversation_id}/wingman/polish
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 | 输入框全部内容 |
| action | string | 是 | expand / compress / correct |
响应:
{
"code": 200,
"data": {
"polished_text": "建议您按以下步骤操作:\n1. 完全退出VPN客户端\n2. 重新启动\n3. 重新连接",
"action": "expand",
"changes_summary": "增加了操作步骤细化和兜底方案"
}
}
7.4 智能改写
POST /api/conversations/{conversation_id}/wingman/rewrite
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| current_text | string | 否 | 当前输入框内容(可为空) |
| generate_count | int | 否 | 生成数量,默认 3 |
| include_knowledge | bool | 否 | 是否检索知识库,默认 true |
响应:
{
"code": 200,
"data": {
"versions": [
{ "text": "VPN连接异常,建议重启客户端后重试。", "style": "简洁直接", "source": "对话上下文" },
{ "text": "VPN连接问题处理步骤:\n1. 退出VPN客户端\n2. 重启\n3. 重新连接", "style": "详细带步骤", "source": "对话上下文" },
{ "text": "根据知识库文档《VPN故障排查指南》...", "style": "带知识库引用", "source": "RAGFlow知识库" }
]
}
}
8. Pydantic 模型
现有 Wingman 端点未使用 Pydantic 请求模型。本设计新增请求体验证模型,提升 API 健壮性:
# backend/app/schemas/wingman.py
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from enum import Enum
class ToneType(str, Enum):
PROFESSIONAL = "professional"
FRIENDLY = "friendly"
CONCISE = "concise"
class PolishAction(str, Enum):
EXPAND = "expand"
COMPRESS = "compress"
CORRECT = "correct"
class AutocompleteRequest(BaseModel):
current_text: str = Field(..., min_length=1, max_length=2000, description="坐席当前输入文字")
cursor_position: int = Field(..., ge=0, description="光标位置")
max_length: int = Field(default=80, ge=10, le=200, description="补齐最大长度")
class ToneAdjustRequest(BaseModel):
selected_text: str = Field(..., min_length=5, max_length=2000, description="选中的文字")
full_text: str = Field(..., min_length=1, max_length=5000, description="输入框全部内容")
tone: ToneType = Field(..., description="目标语气")
class PolishRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=5000, description="待润色文字")
action: PolishAction = Field(..., description="润色操作")
class RewriteRequest(BaseModel):
current_text: str = Field(default="", max_length=5000, description="当前输入内容")
generate_count: int = Field(default=3, ge=1, le=5, description="生成数量")
include_knowledge: bool = Field(default=True, description="是否检索知识库")
# 响应模型
class AutocompleteResponse(BaseModel):
completion: str = Field(default="", description="补齐文字")
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="置信度")
class ToneAdjustResponse(BaseModel):
rewritten_text: str
tone: str
changes_summary: str = ""
class PolishResponse(BaseModel):
polished_text: str
action: str
changes_summary: str = ""
class RewriteVersion(BaseModel):
text: str
style: str
source: str
class RewriteResponse(BaseModel):
versions: List[RewriteVersion] = Field(default_factory=list)
9. Dify Prompt 模板
在 WingmanService 中新增 4 个类属性常量:
class WingmanService:
# === 现有 Prompts(保持不变) ===
# _DRAFT_SYSTEM_PROMPT
# _SUMMARY_SYSTEM_PROMPT
# _TAGS_SYSTEM_PROMPT
# _KNOWLEDGE_SUGGESTION_PROMPT
# === 新增 Prompts ===
_COMPLETION_SYSTEM_PROMPT = """你是一个IT服务坐席输入助手。根据坐席当前正在输入的内容和对话上下文,补齐下一句话。
要求:
1. 补齐内容自然衔接当前文字,不要重复已有内容
2. 长度控制在1-2个短句,不超过80字
3. 语气专业、简洁,符合IT服务规范
4. 只返回补齐的文字,不要加引号或其他标记"""
_TONE_SYSTEM_PROMPT = """你是一个IT服务话术改写助手。将坐席选中的文字改写为{tone}风格。
语气定义:
- professional(专业):使用准确的技术术语,结构化表达,去除口语化内容
- friendly(友好):适当增加问候和关心语,语气更亲和
- concise(简洁):去除冗余,直奔主题,控制字数
要求:
1. 保持原意不变,只调整语气和表达方式
2. 改写后的文字长度与原文相近(±30%)
3. 符合IT服务坐席的专业规范
4. 只返回改写后的文字,不要加引号或解释"""
_POLISH_SYSTEM_PROMPT = """你是一个IT服务文字润色助手。对坐席输入的文字进行{action}。
操作定义:
- expand(扩写):在原文基础上增加操作步骤、注意事项、解释说明,使回复更完整
- compress(压缩):精简表达,去除重复和冗余,保留核心信息,控制字数
- correct(纠错):检查并修正错别字、语法错误、标点符号、格式问题
要求:
1. 保持原文核心意思不变
2. 符合IT服务坐席的专业规范
3. 只返回润色后的文字,不要加引号或解释"""
_REWRITE_SYSTEM_PROMPT = """你是一个IT服务回复生成助手。基于对话上下文{with_knowledge},生成{count}个不同风格的回复版本。
版本要求:
- 版本1:简洁直接,一句话说明解决方案
- 版本2:详细带步骤,包含操作步骤和注意事项
- 版本3:带知识库引用,引用相关知识库文档{knowledge_note}
要求:
1. 每个版本独立完整,可直接发送
2. 语气专业、符合IT服务规范
3. 以JSON格式输出:[{{"text": "回复内容", "style": "风格描述", "source": "来源"}}]"""
10. TypeScript 类型定义
// frontend-agent/src/types/wingman-assist.ts
/** 语气类型 */
export type Tone = 'professional' | 'friendly' | 'concise'
/** 润色操作 */
export type PolishAction = 'expand' | 'compress' | 'correct'
/** 自动补齐请求 */
export interface AutocompleteRequest {
current_text: string
cursor_position: number
max_length?: number
}
/** 自动补齐响应 */
export interface AutocompleteResponse {
completion: string
confidence: number
}
/** 语气调整请求 */
export interface ToneAdjustRequest {
selected_text: string
full_text: string
tone: Tone
}
/** 语气调整响应 */
export interface ToneAdjustResponse {
rewritten_text: string
tone: string
changes_summary: string
}
/** 润色请求 */
export interface PolishRequest {
text: string
action: PolishAction
}
/** 润色响应 */
export interface PolishResponse {
polished_text: string
action: string
changes_summary: string
}
/** 改写版本 */
export interface RewriteVersion {
text: string
style: string
source: string
}
/** 改写请求 */
export interface RewriteRequest {
current_text?: string
generate_count?: number
include_knowledge?: boolean
}
/** 改写响应 */
export interface RewriteResponse {
versions: RewriteVersion[]
}
/** 右栏面板模式 */
export type PanelMode = 'normal' | 'expanded'
/** 语气选项配置 */
export const TONE_OPTIONS: Array<{ value: Tone; label: string; desc: string }> = [
{ value: 'professional', label: '专业', desc: '准确术语、结构化表达' },
{ value: 'friendly', label: '友好', desc: '增加关心语、语气亲和' },
{ value: 'concise', label: '简洁', desc: '去除冗余、直奔主题' },
]
/** 润色操作配置 */
export const POLISH_ACTIONS: Array<{ value: PolishAction; label: string; desc: string }> = [
{ value: 'expand', label: '扩写', desc: '增加步骤和细节' },
{ value: 'compress', label: '压缩', desc: '精简保留核心' },
{ value: 'correct', label: '纠错', desc: '修正错别字和语法' },
]
Part C: 架构图
11. 类图
classDiagram
%% ===== 后端 =====
class WingmanService {
-api_url: str
-api_key: str
-timeout: int
-_client: httpx.AsyncClient
+generate_draft() Dict
+generate_summary() Dict
+suggest_tags() Dict
+generate_knowledge_suggestion() Dict
+generate_completion() Dict %% 新增
+adjust_tone() Dict %% 新增
+polish_text() Dict %% 新增
+rewrite_versions() Dict %% 新增
-_get_client() AsyncClient
-_build_context_messages() List
-_call_wingman_api(text, temp) str %% 改造: 新增temp参数
-_parse_json_response() Dict
-_estimate_confidence() float
+close() void
}
class AutocompleteRequest {
+current_text: str
+cursor_position: int
+max_length: int
}
class ToneAdjustRequest {
+selected_text: str
+full_text: str
+tone: ToneType
}
class PolishRequest {
+text: str
+action: PolishAction
}
class RewriteRequest {
+current_text: str
+generate_count: int
+include_knowledge: bool
}
class ToneType {
<<enumeration>>
PROFESSIONAL
FRIENDLY
CONCISE
}
class PolishAction {
<<enumeration>>
EXPAND
COMPRESS
CORRECT
}
%% ===== 前端 =====
class ReplyBox {
+inputText: string
+autoCompleteEnabled: boolean
-useAutoComplete()
-useAiTextTools()
+acceptCompletion()
+showTonePopover()
+showPolishPanel()
+showRewritePanel()
+send()
}
class ReplySuggestArea {
+inputText: string
+shouldShow: boolean
-AiRecommendBar
-QuickReplyBar
}
class AiAssistantPanel {
+mode: PanelMode
-PanelModeToggle
-AiTrainingPanel
+toggleMode()
}
class AiTrainingPanel {
-SmartTagEditor
-QualityFeedback
-KnowledgeContribute
-UsageStats
}
class useAutoComplete {
+ghostText: Ref~string~
+isLoading: Ref~boolean~
-debouncedFetch(text)
+acceptCompletion()
+dismissCompletion()
}
class usePanelMode {
+mode: Ref~PanelMode~
+panelWidth: ComputedRef
+centerColVisible: ComputedRef
+toggleMode()
}
WingmanService --> AutocompleteRequest : 接收
WingmanService --> ToneAdjustRequest : 接收
WingmanService --> PolishRequest : 接收
WingmanService --> RewriteRequest : 接收
ToneAdjustRequest --> ToneType
PolishRequest --> PolishAction
ReplyBox --> useAutoComplete : 使用
ReplyBox --> ReplySuggestArea : 包含
AiAssistantPanel --> usePanelMode : 使用
AiAssistantPanel --> AiTrainingPanel : 包含
12. 时序图
12.1 自动补齐时序图
sequenceDiagram
participant A as 坐席
participant R as ReplyBox
participant U as useAutoComplete
participant API as wingman.ts
participant F as FastAPI
participant W as WingmanService
participant D as Dify
A->>R: 输入文字
R->>U: input event (text)
U->>U: debounce 800ms
U->>U: abort previous request
U->>API: autocomplete(convId, {current_text})
API->>F: POST /wingman/autocomplete
F->>F: _get_recent_messages(limit=5)
F->>W: generate_completion(convId, text, msgs)
W->>W: _build_context_messages(msgs, prompt)
W->>W: append current_text to context
W->>D: POST /chat/completions (temp=0.2)
D-->>W: {choices[0].message.content}
W-->>F: {completion, confidence}
F-->>API: {code:200, data:{completion,confidence}}
API-->>U: result
U->>U: ghostText.value = result.completion
U-->>R: ghost overlay 渲染灰色文字
R-->>A: 显示幽灵文字
A->>R: 按 Tab 键
R->>U: keydown Tab
U->>U: inputText += ghostText
U->>U: ghostText = ''
R-->>A: 补齐文字变为正常颜色
12.2 语气调整时序图
sequenceDiagram
participant A as 坐席
participant R as ReplyBox
participant T as ToneAdjustPopover
participant API as wingman.ts
participant F as FastAPI
participant W as WingmanService
participant D as Dify
A->>R: 选中文字 (≥5字符)
A->>R: 点击"语气"按钮
R->>T: 弹出浮层 (3个语气选项)
A->>T: 选择"专业"
T->>API: toneAdjust(convId, {selected_text, full_text, tone})
API->>F: POST /wingman/tone-adjust
F->>F: _get_recent_messages(limit=5)
F->>W: adjust_tone(convId, selected, full, tone, msgs)
W->>W: _build_context_messages(msgs, _TONE_SYSTEM_PROMPT)
W->>W: 注入 selected_text + full_text + tone
W->>D: POST /chat/completions (temp=0.3)
D-->>W: 改写后的文字
W-->>F: {rewritten_text, tone, changes_summary}
F-->>API: response
API-->>T: result
T->>T: 显示"原文 → 改写文"对比
T-->>A: 展示对比结果
alt 坐席点击"替换"
A->>T: 点击"替换"
T->>R: 替换选中区域文字
T->>T: 关闭浮层
else 坐席点击"取消"
A->>T: 点击外部/Esc
T->>T: 关闭浮层,不修改
end
12.3 右栏模式切换时序图
sequenceDiagram
participant A as 坐席
participant P as PanelModeToggle
participant U as usePanelMode
participant W as Workspace
participant C as ChatArea (中栏)
participant R as AiAssistantPanel (右栏)
A->>P: 点击"放大"按钮
P->>U: toggleMode()
U->>U: mode.value = 'expanded'
U->>W: class="panel-expanded"
W->>W: CSS变量更新 (transition 0.3s)
Note over W: --assistant-panel-width: 560px<br/>--center-col-flex: 0<br/>--center-col-width: 0<br/>--center-col-overflow: hidden
W->>C: flex=0, width=0, overflow=hidden
W->>R: width=560px
Note over C,R: 0.3s 平滑动画<br/>中栏压缩隐藏<br/>右栏展开为完整训练面板
A->>P: 点击"正常"按钮
P->>U: toggleMode()
U->>U: mode.value = 'normal'
U->>W: 移除 class="panel-expanded"
W->>W: CSS变量恢复
W->>C: flex=1, width=auto, overflow=visible
W->>R: width=260px
Note over C,R: 0.3s 平滑动画<br/>中栏恢复显示<br/>右栏缩回正常宽度
Part D: 任务分解
13. 文件清单
13.1 后端新建文件
| 文件路径 | 说明 |
|---|---|
backend/app/schemas/wingman_assist.py |
新增 Pydantic 请求/响应模型 |
13.2 后端修改文件
| 文件路径 | 修改范围 | 说明 |
|---|---|---|
backend/app/services/wingman_service.py |
扩展 | 新增 4 个方法 + 4 个 system prompt 常量;改造 _call_wingman_api() 新增 temperature 参数 |
backend/app/api/wingman.py |
扩展 | 新增 4 个端点;引入 Pydantic 模型做请求验证 |
13.3 前端新建文件
| 文件路径 | 说明 |
|---|---|
frontend-agent/src/types/wingman-assist.ts |
TypeScript 类型定义 + 常量配置 |
frontend-agent/src/composables/useAutoComplete.ts |
自动补齐 composable |
frontend-agent/src/composables/useAiTextTools.ts |
语气/润色/改写统一 composable |
frontend-agent/src/composables/usePanelMode.ts |
右栏模式切换 composable |
frontend-agent/src/components/chat/ReplySuggestArea.vue |
回复建议区容器 |
frontend-agent/src/components/chat/AiRecommendBar.vue |
AI推荐条(合并自 AiRecommendInline + AiSuggestReply) |
frontend-agent/src/components/chat/QuickReplyBar.vue |
快速回复条(改造自 QuickReplyPanel) |
frontend-agent/src/components/chat/GhostTextOverlay.vue |
幽灵文字覆盖层 |
frontend-agent/src/components/chat/ToneAdjustPopover.vue |
语气调整浮层 |
frontend-agent/src/components/chat/PolishPanel.vue |
润色精修面板 |
frontend-agent/src/components/chat/RewritePanel.vue |
改写选择面板 |
frontend-agent/src/components/assistant/PanelModeToggle.vue |
右栏模式切换按钮组 |
frontend-agent/src/components/assistant/AiTrainingPanel.vue |
AI训练区主体 |
frontend-agent/src/components/assistant/SmartTagEditor.vue |
智能标注编辑面板 |
frontend-agent/src/components/assistant/QualityFeedback.vue |
质量反馈面板 |
frontend-agent/src/components/assistant/KnowledgeContribute.vue |
知识贡献面板 |
frontend-agent/src/components/assistant/UsageStats.vue |
使用统计面板 |
13.4 前端修改文件
| 文件路径 | 修改范围 | 说明 |
|---|---|---|
frontend-agent/src/api/wingman.ts |
扩展 | 新增 4 个 API 调用函数 |
frontend-agent/src/views/Workspace.vue |
改造 | 三栏宽度调整;右栏模式切换 class 绑定;引入 usePanelMode |
frontend-agent/src/styles/global.css |
改造 | CSS 变量体系更新 |
frontend-agent/src/components/chat/ChatArea.vue |
改造 | 新增 ReplySuggestArea 层;TroubleshootBar 默认折叠 |
frontend-agent/src/components/chat/UserInfoBar.vue |
小幅修改 | 详情默认折叠 |
frontend-agent/src/components/chat/TroubleshootBar.vue |
小幅修改 | 默认折叠为图标条 |
frontend-agent/src/components/chat/ReplyBox.vue |
大幅改造 | 工具栏重构为左右分区;新增 AI 工具按钮;集成 useAutoComplete;新增浮层组件引用 |
frontend-agent/src/components/assistant/AiAssistantPanel.vue |
全面重构 | 移除回复前功能;改为 PanelModeToggle + AiTrainingPanel |
frontend-agent/src/components/conversation/TodoPanel.vue |
小幅修改 | max-height 260px → 220px |
13.5 前端删除文件
| 文件路径 | 原因 |
|---|---|
frontend-agent/src/components/chat/AiRecommendInline.vue |
功能被 AiRecommendBar.vue 替代 |
frontend-agent/src/components/assistant/AiSuggestReply.vue |
功能合并到 AiRecommendBar.vue |
frontend-agent/src/components/assistant/QuickReplyPanel.vue |
改造为 QuickReplyBar.vue(改名+横向布局) |
14. 开发计划
Phase 1: 后端 WingmanService 扩展(预计 1.5 天)
- 新建
schemas/wingman_assist.py— Pydantic 模型 - 改造
wingman_service.py—_call_wingman_api()新增 temperature 参数 - 新增 4 个方法 + 4 个 system prompt 常量
- 扩展
wingman.py— 新增 4 个端点,引入 Pydantic 模型 - 后端单元测试:每个端点正常 + 降级场景
Phase 2: 前端 API 层 + Composable(预计 1 天)
- 新建
types/wingman-assist.ts— 类型定义 - 扩展
api/wingman.ts— 4 个 API 函数 - 新建
useAutoComplete.ts— debounce + AbortController + ghost text 管理 - 新建
useAiTextTools.ts— 语气/润色/改写统一调用 - 新建
usePanelMode.ts— 模式切换
Phase 3: ReplyBox 工具栏 + AI 辅助组件(预计 2 天)
ReplyBox.vue工具栏重构为左右分区GhostTextOverlay.vue— mirror div + 幽灵文字渲染ToneAdjustPopover.vue— 语气选择 + 原文/改写文对比PolishPanel.vue— 精修面板RewritePanel.vue— 改写选择面板- 键盘快捷键:Tab 接受补齐、Esc 清除
Phase 4: 布局重构 + 回复建议区(预计 2 天)
global.cssCSS 变量更新Workspace.vue三栏宽度调整 + 模式切换 classChatArea.vue新增 ReplySuggestArea 层ReplySuggestArea.vue— 容器组件AiRecommendBar.vue— 合并 AiRecommendInline + AiSuggestReplyQuickReplyBar.vue— 横向 2 层布局 + 数字快捷键UserInfoBar.vue详情默认折叠TroubleshootBar.vue默认折叠为图标条
Phase 5: 右栏训练区 + 模式切换(预计 1.5 天)
AiAssistantPanel.vue全面重构PanelModeToggle.vue— 正常/放大切换AiTrainingPanel.vue— 训练区主体SmartTagEditor.vue— 智能标注(合并原"推荐排查流程"+ 标注)QualityFeedback.vue— 质量反馈面板KnowledgeContribute.vue— 知识贡献面板UsageStats.vue— 使用统计面板
Phase 6: 功能清理 + 联调测试(预计 1 天)
- 删除
AiRecommendInline.vue、AiSuggestReply.vue - 重命名
QuickReplyPanel.vue→QuickReplyBar.vue - 端到端测试:补齐/语气/润色/改写全流程
- 右栏放大/缩小切换测试
- 回复建议区展开/缩回动画测试
- 响应式断点验证(1280px / 1024px)
- Dify 不可用时的降级测试
总预估: 9 天
15. 风险与缓解
| 风险 | 概率 | 影响 | 缓解措施 |
|---|---|---|---|
| 幽灵文字定位不准(mirror div 坐标计算误差) | 中 | 中 | 参照 VS Code textarea-caret-position 方案;增加 padding/font 精确复制逻辑;添加回退:定位不准时降级为底部建议条 |
| 补齐 API 延迟 > 1.5s 影响体验 | 中 | 中 | 前端 1.5s 超时取消请求;Dify prompt 精简(max_length 限制 80 字);考虑后续迭代引入流式 SSE |
| Dify Wingman Agent 不支持多 temperature | 低 | 高 | 改造前先验证 Dify API 是否传递 temperature 参数;若不支持,在后端不传 temperature(使用 Dify 默认值),通过 system prompt 控制创造性 |
| 右栏放大模式下中栏组件状态丢失 | 低 | 中 | 使用 CSS 隐藏而非 v-if 销毁;组件实例保持存活;切回时立即可见 |
| 快速回复从竖向改为横向,三层导航体验下降 | 中 | 中 | 数字快捷键固定 2 层(L1: Alt+1 |
| 坐席习惯右栏快速回复,移位后找不到 | 中 | 低 | 工具栏保留"快回"入口;输入框为空时回复建议区自动展开;首次使用引导提示 |
| AiAssistantPanel 全面重构影响现有草稿/摘要/标注功能 | 中 | 高 | 分步重构:先新增训练区组件,再移除旧功能;保持 WingmanService 后端不变,仅前端组件重构;保留 git 分支可回滚 |
| RAGFlow 知识库检索延迟影响改写响应 | 中 | 低 | 知识库检索设 3s 超时;超时降级为仅基于对话上下文生成 2 个版本;前端显示"知识库检索中"提示 |
附录:与主架构文档的映射关系
| 本文档章节 | 主文档对应位置 |
|---|---|
| §1 架构概述 | 主文档 §8 AI Wingman 设计(更新) |
| §2 后端架构 | 主文档 §15.8 坐席端 AI 辅助消息框(新增) |
| §3 前端架构 | 主文档 §7.2 坐席工作台模块(更新) |
| §4 布局重构 | 主文档 §7.2 坐席工作台模块(更新) |
| §7 API 规范 | 主文档 §14 API设计规范(引用) |
| §11 类图 | 主文档 §15.8(引用) |
| §12 时序图 | 主文档 §15.8(引用) |
文档结束 | 版本 v1.0 | 2026-07-11 | 宋献 (Simon)