%% 坐席端 AI 辅助消息框 — 时序图 %% 文档版本: v1.0 %% 创建日期: 2026-07-11 %% 包含 4 个功能的完整调用流程 %% ========================================================================== %% 4.1 实时自动补齐 %% ========================================================================== sequenceDiagram participant User as 坐席 participant RB as ReplyBox.vue participant GT as GhostText.vue participant Cmp as useAiAssist.ts participant API as wingman.ts participant BE as wingman.py
(autocomplete) participant Svc as WingmanService
.generate_completion() participant Redis as Redis participant Dify as Dify AI User->>RB: 输入文字 RB->>Cmp: handleInput() → triggerAutocomplete() Note over Cmp: debounce 800ms 等待 Note over Cmp: 输入停顿 > 800ms 触发 Cmp->>Cmp: abort 上一个 AbortController Cmp->>Cmp: 创建新 AbortController Note over Cmp: 检查:输入 > 5 字符 && autocompleteEnabled Cmp->>API: autocomplete(convId, text, cursorPos, signal) API->>BE: POST /conversations/{id}/wingman/autocomplete BE->>BE: _validate_conversation() BE->>BE: _get_recent_messages(limit=5) BE->>Svc: generate_completion(conv_id, text, messages) Svc->>Svc: _make_cache_key(text, conv_id) Svc->>Redis: GET wingman:autocomplete:{hash} alt Redis 命中缓存 Redis-->>Svc: {completion, confidence} Svc-->>BE: 缓存结果 else Redis 未命中 Redis-->>Svc: nil Svc->>Svc: _build_context_messages(messages, _COMPLETION_SYSTEM_PROMPT) Svc->>Svc: 追加 user 消息: "坐席正在输入:{text}\n请补齐" Svc->>Dify: POST /chat/completions
{temperature: 0.2} Dify-->>Svc: "正在查看相关工单记录..." Svc->>Redis: SETEX wingman:autocomplete:{hash} 30s Svc-->>BE: {completion, confidence} end BE-->>API: {code: 0, data: {completion, confidence}} API-->>Cmp: AutocompleteResult Note over Cmp: 检查:未被 abort && completion 非空 Cmp->>Cmp: ghostText.value = completion Cmp->>GT: 渲染幽灵文字(灰色斜体) GT-->>User: 光标位置显示灰色补齐文字 alt Tab 键接受 User->>RB: 按 Tab RB->>Cmp: acceptGhostText() Cmp->>RB: inputText += ghostText Cmp->>Cmp: ghostText = '' GT-->>User: 幽灵文字消失,文字变为正常颜色 else 继续输入 User->>RB: 继续输入 RB->>Cmp: clearGhostText() Cmp->>Cmp: ghostText = '' Note over Cmp: 重新触发 debounce else Esc 键 User->>RB: 按 Esc RB->>Cmp: clearGhostText() Cmp->>Cmp: ghostText = '' end %% ========================================================================== %% 4.2 语气调整 %% ========================================================================== sequenceDiagram participant User as 坐席 participant RB as ReplyBox.vue participant TAP as ToneAdjustPopover.vue participant Cmp as useAiAssist.ts participant API as wingman.ts participant BE as wingman.py
(tone-adjust) participant Svc as WingmanService
.adjust_tone() participant Dify as Dify AI User->>RB: 选中输入框文字(≥ 5 字符) User->>RB: 点击工具栏"语气"按钮 RB->>TAP: visible = true(弹出浮层) TAP-->>User: 显示 3 种语气选项 User->>TAP: 点击"专业" TAP->>Cmp: adjustTone(selectedText, fullText, 'professional') Cmp->>Cmp: toneLoading = true Cmp->>API: adjustTone(convId, selectedText, fullText, 'professional', signal) API->>BE: POST /conversations/{id}/wingman/tone-adjust BE->>BE: _validate_conversation() BE->>BE: _get_recent_messages(limit=5) BE->>Svc: adjust_tone(conv_id, selectedText, fullText, 'professional', messages) Svc->>Svc: _build_context_messages(messages, _TONE_ADJUST_SYSTEM_PROMPT) Svc->>Svc: 追加 user 消息: "原文:{selectedText}\n完整内容:{fullText}\n改写为{tone}风格" Svc->>Dify: POST /chat/completions
{temperature: 0.3} Dify-->>Svc: "经排查,您的VPN连接异常..." Svc-->>BE: {rewritten_text, tone, changes_summary} BE-->>API: {code: 0, data: {...}} API-->>Cmp: ToneAdjustResult Cmp->>Cmp: toneResult = result Cmp->>Cmp: toneLoading = false Cmp->>TAP: 显示原文/改写文对比 TAP-->>User: 原文 → 改写文 + 变更说明 alt 点击"替换" User->>TAP: 点击"替换" TAP->>RB: emit('replace', rewritten_text) RB->>RB: 用 rewritten_text 替换选中区域 RB->>TAP: visible = false else 点击"取消"或外部 User->>TAP: 点击取消 TAP->>RB: emit('cancel') RB->>TAP: visible = false end %% ========================================================================== %% 4.3 文字润色 %% ========================================================================== sequenceDiagram participant User as 坐席 participant RB as ReplyBox.vue participant PP as PolishPanel.vue participant Cmp as useAiAssist.ts participant API as wingman.ts participant BE as wingman.py
(polish) participant Svc as WingmanService
.polish_text() participant Dify as Dify AI User->>RB: 点击工具栏"润色"按钮 RB->>PP: visible = true(弹出精修面板) PP-->>User: 显示原文 + 3 个操作按钮 User->>PP: 点击"扩写" PP->>Cmp: polishText('expand') Cmp->>Cmp: polishLoading = true Cmp->>API: polishText(convId, inputText, 'expand', signal) API->>BE: POST /conversations/{id}/wingman/polish BE->>BE: _validate_conversation() BE->>BE: _get_recent_messages(limit=5) BE->>Svc: polish_text(conv_id, text, 'expand', messages) Svc->>Svc: _build_context_messages(messages, _POLISH_SYSTEM_PROMPT) Svc->>Svc: 追加 user 消息: "对以下文字进行扩写:{text}" Svc->>Dify: POST /chat/completions
{temperature: 0.3} Dify-->>Svc: "建议您按以下步骤操作:\n1. 退出VPN..." Svc-->>BE: {polished_text, action, changes_summary} BE-->>API: {code: 0, data: {...}} API-->>Cmp: PolishResult Cmp->>Cmp: polishResult = result Cmp->>Cmp: polishLoading = false Cmp->>PP: 显示左右对比(原文 | 结果) PP-->>User: 左侧原文 | 右侧结果(可编辑)+ 变更说明 Note over User,PP: 坐席可在结果区域手动编辑 alt 点击"替换全部" User->>PP: 编辑后点击"替换全部" PP->>RB: emit('replace', editedText) RB->>RB: inputText = editedText RB->>PP: visible = false else 切换操作 User->>PP: 点击"压缩"或"纠错" PP->>Cmp: polishText('compress' | 'correct') Note over Cmp,Dify: 重新调用流程 else 点击"取消" User->>PP: 点击取消 PP->>RB: emit('cancel') RB->>PP: visible = false end %% ========================================================================== %% 4.4 智能改写 %% ========================================================================== sequenceDiagram participant User as 坐席 participant RB as ReplyBox.vue participant RP as RewritePanel.vue participant Cmp as useAiAssist.ts participant API as wingman.ts participant BE as wingman.py
(rewrite) participant Svc as WingmanService
.rewrite_versions() participant RAG as RAGFlow participant Dify as Dify AI User->>RB: 点击工具栏"改写"按钮 RB->>RP: visible = true(弹出选择面板) RP->>Cmp: rewriteVersions() Cmp->>Cmp: rewriteLoading = true Cmp->>API: rewriteVersions(convId, inputText, signal) API->>BE: POST /conversations/{id}/wingman/rewrite BE->>BE: _validate_conversation() BE->>BE: _get_recent_messages(limit=10) BE->>Svc: rewrite_versions(conv_id, text, messages, generate_count=3, include_knowledge=true) Note over Svc: 构建上下文 Svc->>Svc: _build_context_messages(messages, _REWRITE_SYSTEM_PROMPT) par 知识库检索(并行) Svc->>RAG: retrieval(question=当前问题, dataset_ids=默认知识库) RAG-->>Svc: 检索到 3 个相关文档片段 Svc->>Svc: 将知识片段拼入 system prompt and 准备 Dify 调用 Note over Svc: 等待知识检索完成 end Svc->>Dify: POST /chat/completions
{temperature: 0.6} Dify-->>Svc: "版本1\n---\n版本2\n---\n版本3" Svc->>Svc: 按 "---" 分割为 3 个版本 Svc->>Svc: 标注 style 和 source Svc-->>BE: {versions: [{text, style, source}, ...]} BE-->>API: {code: 0, data: {...}} API-->>Cmp: RewriteResult Cmp->>Cmp: rewriteResult = result Cmp->>Cmp: rewriteLoading = false Cmp->>RP: 显示 3 个版本卡片 RP-->>User: 版本1(简洁直接)/ 版本2(详细带步骤)/ 版本3(带知识库引用) alt 选择版本 → 替换 User->>RP: 点击版本卡片 → "替换" RP->>RB: emit('replace', versionText) RB->>RB: inputText = versionText RB->>RP: visible = false else 选择版本 → 追加 User->>RP: 点击版本卡片 → "追加" RP->>RB: emit('append', versionText) RB->>RB: inputText += '\n' + versionText RB->>RP: visible = false else 点击"取消" User->>RP: 点击取消 RP->>RB: emit('cancel') RB->>RP: visible = false end