Files
wecom_it_smart_desk/docs/03-技术架构/坐席端AI辅助消息框与布局优化-架构设计.md
T
Simon 449c6d4875 feat: 2026-07-12~13 全量更新 - AI对话链路改造+H5 v4/v5+坐席端v5+上下文感知诊断+知识库迭代3
## 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分块上传
2026-07-13 02:17:03 +08:00

54 KiB
Raw Blame History

坐席端 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. 调用 Difytemperature=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 技术计算光标坐标
└──────────────────────────────┘

技术要点

  1. 创建一个与 textarea 样式完全一致的 <div>(mirror div),用于计算光标坐标
  2. ghost overlay 定位在光标坐标处,显示灰色补齐文字
  3. pointer-events: none 确保不拦截鼠标事件
  4. Tab 键监听在 textarea 的 keydown 事件中,e.key === 'Tab' 时阻止默认行为并接受补齐
  5. 继续输入时自动清除 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 覆盖

理由

  • contenteditable div 虽然原生支持幽灵文字,但会破坏现有的 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-showdisplay: none)会导致回复建议区下方布局跳动
  • max-heightauto0 的过渡更平滑
  • 组件实例保持存活,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.css CSS 变量更新
  • Workspace.vue 三栏宽度调整 + 模式切换 class
  • ChatArea.vue 新增 ReplySuggestArea 层
  • ReplySuggestArea.vue — 容器组件
  • AiRecommendBar.vue — 合并 AiRecommendInline + AiSuggestReply
  • QuickReplyBar.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.vueAiSuggestReply.vue
  • 重命名 QuickReplyPanel.vueQuickReplyBar.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+17, L2: 19),键盘操作不依赖鼠标;L3 条目列表在选中 L2 后弹出
坐席习惯右栏快速回复,移位后找不到 工具栏保留"快回"入口;输入框为空时回复建议区自动展开;首次使用引导提示
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)