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分块上传
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
%% 坐席端 AI 辅助消息框 — 类图
|
||||
%% 文档版本: v1.0
|
||||
%% 创建日期: 2026-07-11
|
||||
|
||||
classDiagram
|
||||
direction TB
|
||||
|
||||
class WingmanService {
|
||||
-str api_url
|
||||
-str api_key
|
||||
-float timeout
|
||||
-httpx.AsyncClient _client
|
||||
-aioredis.Redis _redis
|
||||
-str _COMPLETION_SYSTEM_PROMPT
|
||||
-str _TONE_ADJUST_SYSTEM_PROMPT
|
||||
-str _POLISH_SYSTEM_PROMPT
|
||||
-str _REWRITE_SYSTEM_PROMPT
|
||||
+__init__(redis_client: Optional[Redis])
|
||||
+async generate_completion(conversation_id: str, current_text: str, messages: List[Dict], max_length: int) Dict
|
||||
+async adjust_tone(conversation_id: str, selected_text: str, full_text: str, tone: str, messages: List[Dict]) Dict
|
||||
+async polish_text(conversation_id: str, text: str, action: str, messages: List[Dict]) Dict
|
||||
+async rewrite_versions(conversation_id: str, current_text: str, messages: List[Dict], generate_count: int, include_knowledge: bool) Dict
|
||||
-async _call_wingman_api(context_messages: List[Dict], temperature: float) Optional[str]
|
||||
-_build_context_messages(messages: List[Dict], system_prompt: str) List[Dict]
|
||||
-async _get_cache(key: str) Optional[Dict]
|
||||
-async _set_cache(key: str, value: Dict, ttl: int) void
|
||||
-_make_cache_key(text: str, conversation_id: str) str
|
||||
-async _search_knowledge(query: str) Optional[str]
|
||||
-_parse_json_response(content: str, default: Dict) Dict
|
||||
}
|
||||
|
||||
class WingmanAPIRouter {
|
||||
+POST autocomplete(conversation_id, request: AutocompleteRequest, agent, db, wingman_service)
|
||||
+POST tone_adjust(conversation_id, request: ToneAdjustRequest, agent, db, wingman_service)
|
||||
+POST polish(conversation_id, request: PolishRequest, agent, db, wingman_service)
|
||||
+POST rewrite(conversation_id, request: RewriteRequest, agent, db, wingman_service)
|
||||
-async _validate_conversation(conversation_id, agent, db) Conversation
|
||||
-async _get_recent_messages(conversation_id, db, limit) List[Dict]
|
||||
}
|
||||
|
||||
class AutocompleteRequest {
|
||||
+str current_text
|
||||
+int cursor_position
|
||||
+int max_length
|
||||
}
|
||||
|
||||
class ToneAdjustRequest {
|
||||
+str selected_text
|
||||
+str full_text
|
||||
+str tone
|
||||
}
|
||||
|
||||
class PolishRequest {
|
||||
+str text
|
||||
+str action
|
||||
+bool conversation_context
|
||||
}
|
||||
|
||||
class RewriteRequest {
|
||||
+str current_text
|
||||
+int generate_count
|
||||
+bool include_knowledge
|
||||
}
|
||||
|
||||
class WingmanTS {
|
||||
+autocomplete(convId: str, text: str, cursorPos: int, signal: AbortSignal) Promise~AutocompleteResult~
|
||||
+adjustTone(convId: str, selectedText: str, fullText: str, tone: ToneType, signal: AbortSignal) Promise~ToneAdjustResult~
|
||||
+polishText(convId: str, text: str, action: PolishAction, signal: AbortSignal) Promise~PolishResult~
|
||||
+rewriteVersions(convId: str, currentText: str, signal: AbortSignal) Promise~RewriteResult~
|
||||
}
|
||||
|
||||
class UseAiAssist {
|
||||
+Ref~string~ ghostText
|
||||
+Ref~boolean~ isCompletLoading
|
||||
+Ref~boolean~ autocompleteEnabled
|
||||
+Ref~boolean~ tonePopoverVisible
|
||||
+Ref~boolean~ polishPanelVisible
|
||||
+Ref~boolean~ rewritePanelVisible
|
||||
+triggerAutocomplete() void
|
||||
+acceptGhostText() void
|
||||
+clearGhostText() void
|
||||
+adjustTone(selectedText: str, fullText: str, tone: ToneType) Promise~void~
|
||||
+polishText(action: PolishAction) Promise~void~
|
||||
+rewriteVersions() Promise~void~
|
||||
+cleanup() void
|
||||
}
|
||||
|
||||
class GhostText {
|
||||
+Props: ghostText: string, textareaRef: HTMLTextAreaElement
|
||||
+Emits: accept, dismiss
|
||||
-calculateCursorPos(textarea: HTMLTextAreaElement) {x: number, y: number}
|
||||
}
|
||||
|
||||
class AiAssistToolbar {
|
||||
+Props: autocompleteEnabled: boolean, hasSelection: boolean, hasText: boolean
|
||||
+Emits: toggleAutocomplete, toneAdjust, polish, rewrite
|
||||
}
|
||||
|
||||
class ToneAdjustPopover {
|
||||
+Props: visible: boolean, loading: boolean, result: ToneAdjustResult|null, originalText: string
|
||||
+Emits: select(tone: ToneType), replace, cancel
|
||||
}
|
||||
|
||||
class PolishPanel {
|
||||
+Props: visible: boolean, loading: boolean, result: PolishResult|null, originalText: string
|
||||
+Emits: action(action: PolishAction), replace(text: string), cancel
|
||||
}
|
||||
|
||||
class RewritePanel {
|
||||
+Props: visible: boolean, loading: boolean, result: RewriteResult|null
|
||||
+Emits: replace(text: string), append(text: string), cancel
|
||||
}
|
||||
|
||||
class ReplyBox {
|
||||
-UseAiAssist aiAssist
|
||||
-Ref~string~ inputText
|
||||
-HTMLTextAreaElement inputRef
|
||||
+handleKeydown(event: KeyboardEvent) void
|
||||
+handleInput() void
|
||||
}
|
||||
|
||||
%% 后端关系
|
||||
WingmanAPIRouter --> WingmanService : Depends (DI)
|
||||
WingmanAPIRouter --> AutocompleteRequest : 验证请求体
|
||||
WingmanAPIRouter --> ToneAdjustRequest : 验证请求体
|
||||
WingmanAPIRouter --> PolishRequest : 验证请求体
|
||||
WingmanAPIRouter --> RewriteRequest : 验证请求体
|
||||
|
||||
%% 前端关系
|
||||
WingmanTS --> AutocompleteRequest : HTTP 请求
|
||||
UseAiAssist --> WingmanTS : 调用 API
|
||||
ReplyBox --> UseAiAssist : composable
|
||||
ReplyBox --> GhostText : 渲染幽灵文字
|
||||
ReplyBox --> AiAssistToolbar : 渲染工具栏
|
||||
ReplyBox --> ToneAdjustPopover : 弹出浮层
|
||||
ReplyBox --> PolishPanel : 弹出面板
|
||||
ReplyBox --> RewritePanel : 弹出面板
|
||||
@@ -0,0 +1,261 @@
|
||||
%% 坐席端 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<br/>(autocomplete)
|
||||
participant Svc as WingmanService<br/>.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<br/>{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<br/>(tone-adjust)
|
||||
participant Svc as WingmanService<br/>.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<br/>{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<br/>(polish)
|
||||
participant Svc as WingmanService<br/>.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<br/>{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<br/>(rewrite)
|
||||
participant Svc as WingmanService<br/>.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<br/>{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
|
||||
@@ -1,10 +1,12 @@
|
||||
# IT智能服务台 — 系统架构设计文档
|
||||
|
||||
> **文档版本**: v2.1 (综合版)
|
||||
> **文档版本**: v2.2 (综合版)
|
||||
> **创建日期**: 2025-07-11
|
||||
> **最近更新**: 2026-07-10
|
||||
> **架构师**: 高见远 (Bob)
|
||||
> **最近更新**: 2026-07-11
|
||||
> **架构师**: 高见远 (Bob) / 宋献 (Simon)
|
||||
> **状态**: 正式版
|
||||
>
|
||||
> **v2.2 变更**: 新增 §15.8 坐席端AI辅助消息框与布局优化;更新 §7.2 坐席工作台模块布局参数;更新 §8 AI Wingman 设计新增能力
|
||||
|
||||
---
|
||||
|
||||
@@ -43,6 +45,8 @@
|
||||
| 15.4 | 企微审批工单同步 | ✅ 设计完成 |
|
||||
| 15.5 | 复杂场景重构 | ✅ 设计完成 |
|
||||
| 15.6 | ExternalSystemAdapter抽象层 | ✅ 设计完成 |
|
||||
| 15.7 | Wingman设计 | ✅ 已实现 |
|
||||
| 15.8 | 坐席端AI辅助消息框与布局优化 | ✅ 设计完成 |
|
||||
| **16. 技术分析报告** | | |
|
||||
| 16.1 | JP-webcli自动化部署能力分析 | ✅ 已完成 |
|
||||
| **17. 阶段5 自动化闭环** | | |
|
||||
@@ -307,12 +311,24 @@ IT智能服务台是为企业提供 IT support 的智能化服务平台,核心
|
||||
**技术栈**:Vue 3 + TypeScript + Element Plus + Pinia
|
||||
|
||||
**核心功能**:
|
||||
- 会话列表(排队/进行中/已解决)
|
||||
- 会话用户列表(排队/进行中/已解决)
|
||||
- 实时聊天
|
||||
- 快速回复
|
||||
- AI Wingman 右侧栏
|
||||
- 回复建议区(AI推荐 + 快速回复,统一入口)
|
||||
- AI Wingman 右侧栏(训练区:智能标注/质量反馈/知识贡献/使用统计)
|
||||
- AI 辅助消息框(自动补齐/语气调整/文字润色/智能改写)
|
||||
- 右栏放大/缩小模式切换
|
||||
- 消息标记(VIP/招手/情绪)
|
||||
|
||||
**布局参数**(v2.1 更新):
|
||||
|
||||
```
|
||||
左栏: 260px (会话用户列表 + 待办面板)
|
||||
中栏: flex:1 (UserInfoBar + TroubleshootBar + 消息列表 + 回复建议区 + ReplyBox)
|
||||
右栏: 260px(正常) / 560px(放大) (AI训练区 + 模式切换)
|
||||
```
|
||||
|
||||
> 详见 §15.8 坐席端AI辅助消息框与布局优化
|
||||
|
||||
### 7.3 H5 用户端模块
|
||||
|
||||
**技术栈**:Vue 3 + Vant 4 + TypeScript
|
||||
@@ -327,14 +343,26 @@ IT智能服务台是为企业提供 IT support 的智能化服务平台,核心
|
||||
|
||||
## 8. AI Wingman 设计
|
||||
|
||||
详见第15.7节「Wingman设计」
|
||||
详见第15.7节「Wingman设计」和第15.8节「坐席端AI辅助消息框与布局优化」
|
||||
|
||||
Wingman 是坐席工作台的 AI 辅助系统:
|
||||
|
||||
- **草稿回复**:坐席打字 → AI 实时生成 3 条草稿
|
||||
- **自动摘要**:会话结束 → AI 200 字摘要
|
||||
- **知识推荐**:对话中识别关键字 → 推 FAQ
|
||||
- **排查步骤**:员工描述问题 → AI 给 step-by-step
|
||||
**现有能力(已实现)**:
|
||||
- **草稿回复**:坐席打字 → AI 实时生成草稿
|
||||
- **自动摘要**:会话结束 → AI 结构化摘要
|
||||
- **标签建议**:对话内容 → AI 建议分类标签
|
||||
- **知识库优化建议**:对话分析 → 知识库改进建议
|
||||
|
||||
**新增能力(设计完成,见 §15.8)**:
|
||||
- **实时自动补齐**:输入停顿 > 0.8s → 幽灵文字 → Tab 接受
|
||||
- **语气调整**:选中文字 → 专业/友好/简洁 → 一键改写
|
||||
- **文字润色**:扩写/压缩/纠错 → 精修面板 → 确认替换
|
||||
- **智能改写**:对话上下文 + 知识库 → 3 个备选版本
|
||||
|
||||
**布局重构(设计完成,见 §15.8)**:
|
||||
- 回复前功能(草稿/知识/推荐/快回)统一到中栏回复建议区
|
||||
- 回复后功能(标注/反馈/贡献/统计)归右栏 AI 训练区
|
||||
- 右栏支持放大/缩小模式切换(260px ↔ 560px)
|
||||
|
||||
---
|
||||
|
||||
@@ -850,6 +878,142 @@ class SecurityStatus(BaseModel):
|
||||
|
||||
---
|
||||
|
||||
### 15.8 坐席端AI辅助消息框与布局优化
|
||||
|
||||
> **新增日期**: 2026-07-11 (v2.1) | **架构师**: 宋献 (Simon)
|
||||
> **关联文档**: `docs/03-技术架构/坐席端AI辅助消息框与布局优化-架构设计.md`
|
||||
> **关联PRD**: `docs/02-产品需求/坐席端AI辅助消息框-PRD.md` + `docs/02-产品需求/坐席端布局优化建议.md`
|
||||
|
||||
#### 15.8.1 功能概述
|
||||
|
||||
在现有 Wingman 基础上新增 4 项 AI 辅助功能 + 坐席端布局全面重构:
|
||||
|
||||
**AI 辅助消息框(4 项新增功能)**:
|
||||
|
||||
| 功能 | 交互方式 | 后端方法 | temperature |
|
||||
|------|---------|---------|-------------|
|
||||
| 实时自动补齐 | 内联幽灵文字,Tab 接受,debounce 800ms | `generate_completion()` | 0.2 |
|
||||
| 语气调整 | 选中文字 → 专业/友好/简洁 → 原文/改写对比 | `adjust_tone()` | 0.3 |
|
||||
| 文字润色 | 点润色按钮 → 扩写/压缩/纠错 → 精修面板 | `polish_text()` | 0.3 |
|
||||
| 智能改写 | 点改写按钮 → 3 个备选版本 → 替换/追加 | `rewrite_versions()` | 0.6 |
|
||||
|
||||
**布局重构**:
|
||||
|
||||
| 改动区域 | 变化 |
|
||||
|----------|------|
|
||||
| 左栏 | 280px → 260px(会话用户列表) |
|
||||
| 中栏 | +80px 宽度;新增回复建议区;UserInfoBar/TroubleshootBar 默认折叠 |
|
||||
| 右栏 | 320px → 260px(正常)/560px(放大);改为 AI 训练区;支持模式切换 |
|
||||
| 工具栏 | 单行左右分区:常规工具(左) + AI工具(右) |
|
||||
|
||||
#### 15.8.2 后端架构
|
||||
|
||||
**WingmanService 扩展**(`backend/app/services/wingman_service.py`):
|
||||
|
||||
```
|
||||
WingmanService
|
||||
├── 现有方法(保持不变)
|
||||
│ ├── generate_draft() temp=0.3
|
||||
│ ├── generate_summary() temp=0.3
|
||||
│ ├── suggest_tags() temp=0.3
|
||||
│ └── generate_knowledge_suggestion()
|
||||
│
|
||||
├── 新增方法
|
||||
│ ├── generate_completion() temp=0.2 ← 自动补齐
|
||||
│ ├── adjust_tone() temp=0.3 ← 语气调整
|
||||
│ ├── polish_text() temp=0.3 ← 文字润色
|
||||
│ └── rewrite_versions() temp=0.6 ← 智能改写
|
||||
│
|
||||
└── 改造方法
|
||||
└── _call_wingman_api(context, temperature=0.3) ← 新增可选参数
|
||||
```
|
||||
|
||||
**关键改造**:`_call_wingman_api()` 新增 `temperature` 参数(默认 0.3 保持兼容),各方法按需传递不同值。
|
||||
|
||||
**API 端点**(`backend/app/api/wingman.py`):
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/conversations/{id}/wingman/autocomplete` | POST | 自动补齐 |
|
||||
| `/api/conversations/{id}/wingman/tone-adjust` | POST | 语气调整 |
|
||||
| `/api/conversations/{id}/wingman/polish` | POST | 文字润色 |
|
||||
| `/api/conversations/{id}/wingman/rewrite` | POST | 智能改写 |
|
||||
|
||||
新增 Pydantic 请求模型(`backend/app/schemas/wingman_assist.py`):`AutocompleteRequest`、`ToneAdjustRequest`、`PolishRequest`、`RewriteRequest`,含字段验证和枚举类型。
|
||||
|
||||
#### 15.8.3 前端架构
|
||||
|
||||
**新增组件树**:
|
||||
|
||||
```
|
||||
Workspace.vue
|
||||
├── ConversationList.vue (左栏 - 会话用户列表)
|
||||
├── ChatArea.vue (中栏)
|
||||
│ ├── UserInfoBar.vue (详情默认折叠)
|
||||
│ ├── TroubleshootBar.vue (默认折叠为图标条)
|
||||
│ ├── MessageList.vue
|
||||
│ ├── ReplySuggestArea.vue (新增 - 回复建议区)
|
||||
│ │ ├── AiRecommendBar.vue (合并自 AiRecommendInline + AiSuggestReply)
|
||||
│ │ └── QuickReplyBar.vue (改造自 QuickReplyPanel)
|
||||
│ └── ReplyBox.vue (改造)
|
||||
│ ├── GhostTextOverlay.vue (新增 - 幽灵文字)
|
||||
│ ├── ToneAdjustPopover.vue (新增 - 语气浮层)
|
||||
│ ├── PolishPanel.vue (新增 - 润色面板)
|
||||
│ └── RewritePanel.vue (新增 - 改写面板)
|
||||
└── AiAssistantPanel.vue (全面重构)
|
||||
├── PanelModeToggle.vue (新增 - 正常/放大切换)
|
||||
└── AiTrainingPanel.vue (新增 - 训练区)
|
||||
├── SmartTagEditor.vue (智能标注)
|
||||
├── QualityFeedback.vue (质量反馈)
|
||||
├── KnowledgeContribute.vue (知识贡献)
|
||||
└── UsageStats.vue (使用统计)
|
||||
```
|
||||
|
||||
**新增 Composable**:
|
||||
|
||||
| Composable | 职责 |
|
||||
|------------|------|
|
||||
| `useAutoComplete.ts` | debounce 800ms + AbortController + ghost text 管理 |
|
||||
| `useAiTextTools.ts` | 语气/润色/改写统一调用和结果管理 |
|
||||
| `usePanelMode.ts` | 右栏 260px ↔ 560px 模式切换 |
|
||||
|
||||
**功能重复清理**(5 处):
|
||||
|
||||
| 编号 | 重复类型 | 清理方案 |
|
||||
|------|---------|---------|
|
||||
| R1 | AI草稿双展示 | 统一到中栏回复建议区 |
|
||||
| R2 | AI推荐回复三处展示 | 合并为 AiRecommendBar.vue |
|
||||
| R3 | 排查流程命名混淆 | 右栏按钮重命名为"智能标注" |
|
||||
| R4 | 标签建议双入口 | 合并为单一"智能标注"功能 |
|
||||
| R5 | 孤儿组件 | 删除 AiRecommendInline.vue |
|
||||
|
||||
#### 15.8.4 关键设计决策
|
||||
|
||||
| 决策 | 选择 | 理由 |
|
||||
|------|------|------|
|
||||
| 补齐交互 | textarea + mirror div + ghost overlay | 保持现有快捷键和粘贴功能不变 |
|
||||
| 补齐 API | HTTP + AbortController | 轻量请求,与现有 Wingman 调用一致 |
|
||||
| 语气/润色浮层 | el-popover / el-drawer | 不遮挡消息列表,不打断工作流 |
|
||||
| 右栏模式切换 | CSS 变量 + class 切换 | 组件不销毁,状态不丢失 |
|
||||
| 回复建议区动画 | max-height transition | 平滑过渡,组件实例保持存活 |
|
||||
| temperature 改造 | 可选参数默认 0.3 | 现有方法无需修改,向后兼容 |
|
||||
|
||||
#### 15.8.5 开发计划
|
||||
|
||||
| 阶段 | 内容 | 预估 |
|
||||
|------|------|------|
|
||||
| Phase 1 | 后端 WingmanService 扩展 + Pydantic 模型 | 1.5 天 |
|
||||
| Phase 2 | 前端 API 层 + Composable | 1 天 |
|
||||
| Phase 3 | ReplyBox 工具栏 + AI 辅助组件 | 2 天 |
|
||||
| Phase 4 | 布局重构 + 回复建议区 | 2 天 |
|
||||
| Phase 5 | 右栏训练区 + 模式切换 | 1.5 天 |
|
||||
| Phase 6 | 功能清理 + 联调测试 | 1 天 |
|
||||
| **合计** | | **9 天** |
|
||||
|
||||
> 完整架构设计(类图、时序图、API 规范、Dify prompt 模板、TypeScript 类型定义)详见独立文档:`docs/03-技术架构/坐席端AI辅助消息框与布局优化-架构设计.md`
|
||||
|
||||
---
|
||||
|
||||
## 16. 技术分析报告
|
||||
|
||||
### 16.1 JP-webcli自动化部署能力分析
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
%% 代答排除匹配引擎架构图
|
||||
%% 来源:增量设计-知识库迭代-开发任务分解-20260712.md §4.1
|
||||
|
||||
graph TB
|
||||
subgraph "代答排除匹配引擎"
|
||||
Engine[ExclusionService<br/>匹配引擎入口]
|
||||
Chain[ExclusionChain<br/>责任链调度]
|
||||
|
||||
subgraph "策略模式 - 4种匹配器"
|
||||
M1[KeywordMatcher<br/>关键词匹配]
|
||||
M2[RegexMatcher<br/>正则匹配]
|
||||
M3[IntentMatcher<br/>意图匹配]
|
||||
M4[CategoryMatcher<br/>分类排除]
|
||||
end
|
||||
|
||||
Engine --> Chain
|
||||
Chain --> M1
|
||||
Chain --> M2
|
||||
Chain --> M3
|
||||
Chain --> M4
|
||||
end
|
||||
|
||||
subgraph "外部依赖"
|
||||
Dify[Dify 意图识别<br/>复用审批意图链路]
|
||||
TriageDB[triage_sessions<br/>分诊结果]
|
||||
RuleDB[(exclusion_rules)]
|
||||
LogDB[(exclusion_logs)]
|
||||
end
|
||||
|
||||
M3 -->|调用| Dify
|
||||
M4 -->|查询| TriageDB
|
||||
Engine -->|读取规则| RuleDB
|
||||
Engine -->|记录命中| LogDB
|
||||
|
||||
style M1 fill:#dbeafe,stroke:#3b82f6
|
||||
style M2 fill:#fce7f3,stroke:#ec4899
|
||||
style M3 fill:#ede9fe,stroke:#8b5cf6
|
||||
style M4 fill:#dcfce7,stroke:#07C160
|
||||
@@ -0,0 +1,67 @@
|
||||
%% 模块依赖关系图
|
||||
%% 来源:增量设计-知识库迭代-开发任务分解-20260712.md §9
|
||||
|
||||
graph TB
|
||||
subgraph "T01: 项目基础设施"
|
||||
DB[数据库迁移<br/>3张新表]
|
||||
CFG[config.py<br/>新增配置项]
|
||||
MODEL[ORM 模型<br/>3个]
|
||||
SCHEMA[Pydantic Schema<br/>2个文件]
|
||||
ROUTER[router.py<br/>路由注册]
|
||||
end
|
||||
|
||||
subgraph "T02: 分诊交互模块"
|
||||
TRI_API[分诊 API<br/>12个端点]
|
||||
TRI_SVC[TriageService<br/>+ DifyTriageService]
|
||||
H5_FE[H5 前端<br/>TriageCard + useTriage]
|
||||
AG_FE[坐席端前端<br/>TriageDashboard + 3组件]
|
||||
DIFY_APP[Dify 分诊应用<br/>Prompt + 配置]
|
||||
end
|
||||
|
||||
subgraph "T03: 拓扑预览模块"
|
||||
TOPO_FE[管理后台前端<br/>TopologyPreview + 3组件]
|
||||
TOPO_API[复用已有<br/>GET /graph]
|
||||
end
|
||||
|
||||
subgraph "T04: 代答排除模块"
|
||||
EXC_API[排除管理 API<br/>8个端点]
|
||||
EXC_SVC[ExclusionService<br/>+ 4个Matcher]
|
||||
EXC_FE[管理后台前端<br/>ExclusionRules + 2组件]
|
||||
MSG_INT[消息流集成<br/>ai_handler.py]
|
||||
end
|
||||
|
||||
%% 依赖关系
|
||||
DB --> TRI_API
|
||||
DB --> EXC_API
|
||||
CFG --> TRI_SVC
|
||||
MODEL --> TRI_API
|
||||
MODEL --> EXC_API
|
||||
SCHEMA --> TRI_API
|
||||
SCHEMA --> EXC_API
|
||||
ROUTER --> TRI_API
|
||||
ROUTER --> EXC_API
|
||||
|
||||
TRI_API --> TRI_SVC
|
||||
TRI_SVC --> H5_FE
|
||||
TRI_SVC --> AG_FE
|
||||
TRI_SVC --> DIFY_APP
|
||||
|
||||
EXC_API --> EXC_SVC
|
||||
EXC_SVC --> EXC_FE
|
||||
EXC_SVC --> MSG_INT
|
||||
|
||||
%% 软依赖
|
||||
TRI_SVC -.->|"CategoryMatcher<br/>软依赖分诊结果"| EXC_SVC
|
||||
|
||||
%% 拓扑预览独立
|
||||
TOPO_FE --> TOPO_API
|
||||
|
||||
%% 并行标注
|
||||
style TOPO_FE fill:#e8f5e9,stroke:#4caf50,stroke-dasharray: 5 5
|
||||
style TOPO_API fill:#e8f5e9,stroke:#4caf50,stroke-dasharray: 5 5
|
||||
|
||||
style DB fill:#e3f2fd,stroke:#2196f3
|
||||
style CFG fill:#e3f2fd,stroke:#2196f3
|
||||
style MODEL fill:#e3f2fd,stroke:#2196f3
|
||||
style SCHEMA fill:#e3f2fd,stroke:#2196f3
|
||||
style ROUTER fill:#e3f2fd,stroke:#2196f3
|
||||
@@ -0,0 +1,74 @@
|
||||
%% 分诊主流程时序图
|
||||
%% 来源:增量设计-知识库迭代-开发任务分解-20260712.md §2.5
|
||||
|
||||
sequenceDiagram
|
||||
participant H5 as H5 员工端
|
||||
participant API as FastAPI 后端
|
||||
participant TriSvc as TriageService
|
||||
participant DifyTri as Dify 分诊应用
|
||||
participant DB as PostgreSQL
|
||||
participant AG as 坐席端看板
|
||||
participant WS as WebSocket
|
||||
|
||||
rect rgb(255, 243, 224)
|
||||
Note over H5,DB: 阶段1:发起分诊
|
||||
H5->>API: POST /api/h5/triage/start {conversation_id, question}
|
||||
API->>TriSvc: start_triage(conversation_id, question)
|
||||
TriSvc->>DB: INSERT triage_sessions (status=triaging)
|
||||
TriSvc->>DifyTri: 调用分诊 prompt(拆分问题为分步选择题)
|
||||
alt Dify 5秒内响应
|
||||
DifyTri-->>TriSvc: {steps[], confidence, urgency, suggested_route, problem_type}
|
||||
TriSvc->>DB: UPDATE triage_sessions SET triage_steps, confidence, urgency, suggested_route
|
||||
TriSvc-->>API: {triage_id, steps, total, confidence, urgency, suggested_route}
|
||||
API-->>H5: {triage_id, steps, total, ...}
|
||||
else Dify 超时(>5s)
|
||||
TriSvc->>DB: UPDATE triage_sessions SET status=timeout
|
||||
TriSvc-->>API: 超时,自动转人工
|
||||
API-->>H5: 分诊超时,已转人工
|
||||
end
|
||||
end
|
||||
|
||||
rect rgb(227, 242, 253)
|
||||
Note over H5,DB: 阶段2:分步选择 + 坐席协同
|
||||
loop 每一步
|
||||
H5->>API: POST /api/h5/triage/step {triage_id, step_index, selected_label}
|
||||
API->>TriSvc: submit_step(triage_id, step_index, selected_label)
|
||||
TriSvc->>DB: 记录 collected_context
|
||||
TriSvc->>DifyTri: 根据选择动态调整后续步骤
|
||||
DifyTri-->>TriSvc: next_step
|
||||
TriSvc-->>API: {next_step, collected_context}
|
||||
API-->>H5: {next_step, collected_context}
|
||||
end
|
||||
|
||||
Note over AG: 坐席看板实时查看分诊进度
|
||||
AG->>API: GET /api/agent/triage/pending
|
||||
API-->>AG: 待分诊列表
|
||||
AG->>API: GET /api/agent/triage/{triage_id}
|
||||
API-->>AG: 分诊详情
|
||||
|
||||
opt 坐席排除选项
|
||||
AG->>API: POST /api/agent/triage/{triage_id}/exclude-options {excluded_labels}
|
||||
API->>WS: WS 推送 excluded_labels 到 H5
|
||||
WS-->>H5: {type: "triage_exclude", excluded_labels}
|
||||
H5->>H5: TriageCard.setExcludedOptions(labels)
|
||||
end
|
||||
end
|
||||
|
||||
rect rgb(232, 245, 233)
|
||||
Note over H5,DB: 阶段3:分诊完成 / 转人工
|
||||
alt 所有步骤完成
|
||||
H5->>API: POST /api/h5/triage/complete {triage_id, context}
|
||||
API->>TriSvc: complete_triage(triage_id, context)
|
||||
TriSvc->>DifyTri: 根据收集的上下文生成最终回复
|
||||
DifyTri-->>TriSvc: {reply, confidence}
|
||||
TriSvc->>DB: UPDATE triage_sessions SET status=routed, route_action=ai_self
|
||||
TriSvc-->>API: {reply, confidence}
|
||||
API-->>H5: AI 回复
|
||||
else 转人工
|
||||
H5->>API: POST /api/h5/triage/transfer {triage_id, context}
|
||||
API->>TriSvc: transfer_to_human(triage_id, context)
|
||||
TriSvc->>DB: UPDATE triage_sessions SET status=routed, route_action=human
|
||||
TriSvc-->>API: 转人工成功
|
||||
API-->>H5: 已转人工
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
%% =============================================================================
|
||||
%% 增量设计:坐席端布局优化 v2.0 — 时序图
|
||||
%% =============================================================================
|
||||
%% 创建日期:2026-07-11
|
||||
%% 包含 3 个关键流程的时序图
|
||||
%% =============================================================================
|
||||
|
||||
%% ====== 时序图 1:回复建议区交互流程 ======
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant User as 坐席
|
||||
participant ChatArea as ChatArea
|
||||
participant RSA as ReplySuggestArea
|
||||
participant ARB as AiRecommendBar
|
||||
participant QRB as QuickReplyBar
|
||||
participant CS as ConversationStore
|
||||
participant RB as ReplyBox
|
||||
participant API as Wingman API
|
||||
|
||||
Note over User, API: 场景:坐席选中会话 → 查看 AI 推荐 → 采纳填入输入框 → 发送
|
||||
|
||||
User->>ChatArea: 选中会话
|
||||
ChatArea->>CS: selectConversation(id)
|
||||
CS->>API: generateDraft(convId)
|
||||
API-->>CS: DraftResult {content, confidence, reasoning}
|
||||
CS->>CS: aiDrafts.set(convId, draft)
|
||||
CS-->>ChatArea: currentConversationId 更新
|
||||
|
||||
ChatArea->>RSA: render (conversationId)
|
||||
RSA->>ARB: :recommendations = loadFromStore()
|
||||
ARB->>CS: 读取 aiDrafts.get(convId)
|
||||
CS-->>ARB: Set~DraftResult~
|
||||
ARB-->>User: 横向展示 1-3 条 AI 推荐卡片
|
||||
|
||||
Note over User, ARB: 坐席点击某条推荐
|
||||
|
||||
User->>ARB: 点击推荐卡片 #1
|
||||
ARB->>CS: pendingReplyText = recommendation.content
|
||||
ARB->>RSA: emit('select', content)
|
||||
RSA->>ChatArea: emit('select', content)
|
||||
|
||||
Note over CS, RB: pendingReplyText 变化触发 ReplyBox watch
|
||||
|
||||
CS-->>RB: watch(pendingReplyText) 触发
|
||||
RB->>RB: inputText = pendingReplyText
|
||||
RB->>RB: pendingReplyText = '' (清空)
|
||||
RB-->>User: 输入框显示推荐内容,光标聚焦
|
||||
|
||||
Note over User, RB: 坐席可编辑后发送
|
||||
|
||||
User->>RB: 按 Enter 发送
|
||||
RB->>RB: handleSend()
|
||||
RB->>ChatArea: emit('send', content)
|
||||
ChatArea->>CS: sendReply(content, replyToId?)
|
||||
CS->>API: sendMessage(convId, content)
|
||||
API-->>CS: Message 对象
|
||||
CS-->>ChatArea: messages.push(newMsg)
|
||||
ChatArea-->>User: 消息列表刷新,显示新消息
|
||||
|
||||
Note over User, QRB: 场景:坐席使用快速回复
|
||||
|
||||
User->>QRB: 点击 L1 分类 chip(如"账号")
|
||||
QRB->>QRB: navState.l1Index = index
|
||||
QRB-->>User: 横向展示 L2 条目预览
|
||||
User->>QRB: 点击条目"密码重置指引"
|
||||
QRB->>CS: pendingReplyText = template.content
|
||||
CS-->>RB: watch 触发,填入输入框
|
||||
RB-->>User: 输入框显示快速回复内容
|
||||
|
||||
|
||||
%% ====== 时序图 2:右栏模式切换流程 ======
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant User as 坐席
|
||||
participant PMT as PanelModeToggle
|
||||
participant AAP as AiAssistantPanel
|
||||
participant WS as Workspace
|
||||
participant CS as ConversationStore
|
||||
participant DOM as DOM/CSS
|
||||
|
||||
Note over User, DOM: 场景:坐席点击右栏放大/缩小开关
|
||||
|
||||
User->>PMT: 点击开关按钮
|
||||
PMT->>AAP: emit('toggle')
|
||||
AAP->>WS: emit('toggle')
|
||||
|
||||
alt 当前为 normal 模式 (260px)
|
||||
WS->>CS: setPanelMode('expanded')
|
||||
CS-->>WS: panelMode = 'expanded'
|
||||
WS->>WS: rightSidebarRef.style.width = 'var(--assistant-panel-expanded)'
|
||||
WS->>DOM: CSS transition: width 260px → 560px (0.3s)
|
||||
DOM-->>User: 右栏平滑放大到 560px
|
||||
Note over WS, DOM: 中栏 flex:1 自动缩小,min-width: 400px 保底
|
||||
else 当前为 expanded 模式 (560px)
|
||||
WS->>CS: setPanelMode('normal')
|
||||
CS-->>WS: panelMode = 'normal'
|
||||
WS->>WS: rightSidebarRef.style.width = 'var(--assistant-panel-width)'
|
||||
WS->>DOM: CSS transition: width 560px → 260px (0.3s)
|
||||
DOM-->>User: 右栏平滑缩小到 260px
|
||||
Note over WS, DOM: 中栏 flex:1 自动扩大
|
||||
end
|
||||
|
||||
Note over User, DOM: 拖拽手柄在放大模式下仍然可用
|
||||
Note over WS, DOM: 拖拽范围:200px ~ 560px(上限已调整)
|
||||
|
||||
|
||||
%% ====== 时序图 3:功能迁移流程(开发过程) ======
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Dev as 开发过程
|
||||
participant Old as 旧组件
|
||||
participant New as 新组件
|
||||
participant ChatArea as ChatArea
|
||||
participant AAP as AiAssistantPanel
|
||||
participant Store as ConversationStore
|
||||
|
||||
Note over Dev, Store: Phase 1 — 清理重复(T02)
|
||||
|
||||
Dev->>Old: 删除 AiRecommendInline.vue
|
||||
Dev->>ChatArea: 移除 import AiRecommendInline
|
||||
Dev->>Old: 删除 AiSuggestReply.vue
|
||||
Dev->>AAP: 移除 import AiSuggestReply
|
||||
|
||||
Note over Dev: 移除右栏已迁移功能
|
||||
|
||||
Dev->>AAP: 移除 Wingman 触发按钮组
|
||||
Dev->>AAP: 移除 AI 推荐区(AiSuggestReply 引用)
|
||||
Dev->>AAP: 移除快速回复区(QuickReplyPanel 引用)
|
||||
Note over AAP: AiAssistantPanel 仅保留容器骨架 + 标注区
|
||||
|
||||
Note over Dev, Store: Phase 3 — 中栏建设(T03)
|
||||
|
||||
Dev->>New: 创建 ReplySuggestArea.vue
|
||||
Dev->>New: 创建 AiRecommendBar.vue
|
||||
Note over New, Store: AiRecommendBar 从 ConversationStore.aiDrafts 加载数据
|
||||
Dev->>New: 创建 QuickReplyBar.vue
|
||||
Note over New, Store: QuickReplyBar 从 QuickReplyStore 加载模板
|
||||
Dev->>ChatArea: 在消息列表与 ReplyBox 之间插入 ReplySuggestArea
|
||||
Dev->>New: ReplyBox 工具栏重构为左右分区
|
||||
|
||||
Note over Dev, Store: Phase 4 — 右栏建设(T04)
|
||||
|
||||
Dev->>New: 创建 AiTrainingPanel.vue (Tab 容器)
|
||||
Dev->>New: 创建 SmartTagEditor.vue
|
||||
Note over New, Store: SmartTagEditor 从 AAP 迁出标签逻辑
|
||||
Dev->>New: 创建 QualityFeedback.vue
|
||||
Dev->>New: 创建 KnowledgeContribute.vue
|
||||
Dev->>New: 创建 PanelModeToggle.vue
|
||||
Dev->>AAP: AiAssistantPanel 承载 PanelModeToggle + AiTrainingPanel
|
||||
Dev->>Store: ConversationStore 新增 panelMode 状态
|
||||
|
||||
Note over Dev, Store: Phase 5 — 联调测试(T05)
|
||||
|
||||
Dev->>ChatArea: 验证 ReplySuggestArea ↔ ReplyBox 联动
|
||||
Dev->>ChatArea: 验证消息区高度 ≥ 400px (1080p)
|
||||
Dev->>AAP: 验证模式切换 + Tab 切换
|
||||
Dev->>Store: 验证 pendingReplyText 全链路通信
|
||||
@@ -0,0 +1,222 @@
|
||||
%% =============================================================================
|
||||
%% 增量设计:坐席端布局优化 v2.0 — 类图
|
||||
%% =============================================================================
|
||||
%% 创建日期:2026-07-11
|
||||
%% 技术栈:Vue 3 + Element Plus + TypeScript + Pinia + CSS Variables
|
||||
%% =============================================================================
|
||||
|
||||
classDiagram
|
||||
%% ====== 容器/页面级组件 ======
|
||||
class Workspace {
|
||||
+Ref~boolean~ assistantVisible
|
||||
+Ref~PanelMode~ panelMode
|
||||
+Ref~HTMLElement~ leftSidebarRef
|
||||
+Ref~HTMLElement~ rightSidebarRef
|
||||
+startLeftResize(e: MouseEvent) void
|
||||
+startRightResize(e: MouseEvent) void
|
||||
+togglePanelMode() void
|
||||
+onMounted() void
|
||||
}
|
||||
|
||||
class ChatArea {
|
||||
+Ref~Message~ replyToMessage
|
||||
+Ref~HTMLElement~ messageListRef
|
||||
+handleSend(content: string) void
|
||||
+handleReplyTo(message: Message) void
|
||||
+scrollToBottom() void
|
||||
}
|
||||
|
||||
%% ====== 中栏新增组件 ======
|
||||
class ReplySuggestArea {
|
||||
+Props: conversationId: string
|
||||
+Emits: select(content: string)
|
||||
+Ref~string~ activeTab
|
||||
+loadRecommendations() void
|
||||
}
|
||||
|
||||
class AiRecommendBar {
|
||||
+Props: recommendations: AiRecommendation[]
|
||||
+Props: loading: boolean
|
||||
+Emits: select(content: string)
|
||||
+Emits: refresh()
|
||||
+handleSelect(index: number) void
|
||||
+confidenceStyle: ComputedRef
|
||||
}
|
||||
|
||||
class QuickReplyBar {
|
||||
+Props: templates: QuickReply[]
|
||||
+Emits: select(content: string)
|
||||
+Ref~QuickReplyNav~ navState
|
||||
+Ref~string~ searchQuery
|
||||
+selectTemplate(tpl: QuickReply) void
|
||||
+goBack() void
|
||||
}
|
||||
|
||||
class ReplyBox {
|
||||
+Props: replyToMessage: Message
|
||||
+Emits: send(content: string)
|
||||
+Emits: cancelReply()
|
||||
+Ref~string~ inputText
|
||||
+Ref~number~ textareaHeight
|
||||
+handleSend() void
|
||||
+handleKeydown(event: KeyboardEvent) void
|
||||
+handleAiTool(action: AiToolAction) void
|
||||
+handleScreenshot() void
|
||||
+handlePaste(event: ClipboardEvent) void
|
||||
}
|
||||
|
||||
%% ====== 右栏组件 ======
|
||||
class AiAssistantPanel {
|
||||
+Props: mode: PanelMode
|
||||
+Emits: toggle()
|
||||
+Ref~string~ conversationId
|
||||
}
|
||||
|
||||
class AiTrainingPanel {
|
||||
+Props: conversationId: string
|
||||
+Ref~TrainingTab~ activeTab
|
||||
+resetForNewConversation() void
|
||||
}
|
||||
|
||||
class PanelModeToggle {
|
||||
+Props: mode: PanelMode
|
||||
+Emits: toggle()
|
||||
}
|
||||
|
||||
class SmartTagEditor {
|
||||
+Props: conversationId: string
|
||||
+Ref~ConversationTags~ currentTags
|
||||
+Ref~string[]~ suggestedTagList
|
||||
+Ref~boolean~ loadingTagSuggestions
|
||||
+handleSuggestTags() void
|
||||
+handleAddSuggestedTag(tag: string) void
|
||||
+handleRemoveTag(key: string) void
|
||||
+handleAddManualTag() void
|
||||
+loadCurrentTags() void
|
||||
}
|
||||
|
||||
class QualityFeedback {
|
||||
+Props: conversationId: string
|
||||
+Ref~Annotation[]~ feedbackList
|
||||
+Ref~boolean~ loading
|
||||
+handleSubmit(messageId: string, feedback: AnnotationFeedback) void
|
||||
+loadAnnotations() void
|
||||
}
|
||||
|
||||
class KnowledgeContribute {
|
||||
+Props: conversationId: string
|
||||
+Ref~ContributeForm~ contributeForm
|
||||
+Ref~boolean~ submitting
|
||||
+handleSubmit() void
|
||||
+resetForm() void
|
||||
}
|
||||
|
||||
%% ====== 修改的现有组件 ======
|
||||
class UserInfoBar {
|
||||
+Props: conversation: Conversation
|
||||
+Props: availableAgents: AgentInfo[]
|
||||
+Props: canInviteCollaborator: boolean
|
||||
+Emits: assign, resolve, toggle-pin, toggle-todo, transfer, invite
|
||||
+Ref~boolean~ isExpanded
|
||||
+Ref~boolean~ showItLevelSelector
|
||||
+toggleExpand() void
|
||||
+resetForNewConversation() void
|
||||
}
|
||||
|
||||
class TroubleshootBar {
|
||||
+Ref~boolean~ isFlowchartExpanded
|
||||
+Ref~boolean~ isIconBarCollapsed
|
||||
+Ref~TroubleshootingTemplate[]~ templates
|
||||
+toggleFlowchart() void
|
||||
+toggleCollapse() void
|
||||
+handleTemplateChange(templateId: string) void
|
||||
+handleStepClick(index: number) void
|
||||
+loadTemplates() void
|
||||
}
|
||||
|
||||
%% ====== Store ======
|
||||
class ConversationStore {
|
||||
+Ref~string~ pendingReplyText
|
||||
+Ref~Map~ aiDrafts
|
||||
+Ref~PanelMode~ panelMode
|
||||
+Ref~string~ currentConversationId
|
||||
+Ref~Conversation~ currentConversation
|
||||
+Ref~Message[]~ messages
|
||||
+setPanelMode(mode: PanelMode) void
|
||||
+selectConversation(id: string) void
|
||||
+sendReply(content: string, replyToId?: string) void
|
||||
}
|
||||
|
||||
class QuickReplyStore {
|
||||
+Ref~QuickReply[]~ templates
|
||||
+ComputedRef~templatesByCategory
|
||||
+ComputedRef~categories
|
||||
+fetchTemplates() void
|
||||
}
|
||||
|
||||
%% ====== 类型定义 ======
|
||||
class PanelMode {
|
||||
<<enumeration>>
|
||||
normal
|
||||
expanded
|
||||
}
|
||||
|
||||
class TrainingTab {
|
||||
<<enumeration>>
|
||||
smart-tag
|
||||
quality-feedback
|
||||
knowledge-contribute
|
||||
}
|
||||
|
||||
class AiRecommendation {
|
||||
+string title
|
||||
+string content
|
||||
+number confidence
|
||||
}
|
||||
|
||||
class ContributeForm {
|
||||
+string title
|
||||
+string problem
|
||||
+string solution
|
||||
+string category
|
||||
}
|
||||
|
||||
class QuickReplyNav {
|
||||
+number l1Index
|
||||
+number l2Index
|
||||
}
|
||||
|
||||
%% ====== 组合关系(contains/renders) ======
|
||||
Workspace *-- ChatArea : renders
|
||||
Workspace *-- AiAssistantPanel : renders
|
||||
ChatArea *-- UserInfoBar : contains
|
||||
ChatArea *-- TroubleshootBar : contains
|
||||
ChatArea *-- ReplySuggestArea : contains (新增)
|
||||
ChatArea *-- ReplyBox : contains
|
||||
|
||||
ReplySuggestArea *-- AiRecommendBar : contains
|
||||
ReplySuggestArea *-- QuickReplyBar : contains
|
||||
|
||||
AiAssistantPanel *-- PanelModeToggle : contains
|
||||
AiAssistantPanel *-- AiTrainingPanel : contains
|
||||
|
||||
AiTrainingPanel *-- SmartTagEditor : tab 1
|
||||
AiTrainingPanel *-- QualityFeedback : tab 2
|
||||
AiTrainingPanel *-- KnowledgeContribute : tab 3
|
||||
|
||||
%% ====== 依赖关系(uses) ======
|
||||
AiRecommendBar ..> ConversationStore : reads aiDrafts, writes pendingReplyText
|
||||
QuickReplyBar ..> ConversationStore : writes pendingReplyText
|
||||
QuickReplyBar ..> QuickReplyStore : reads templates
|
||||
ReplyBox ..> ConversationStore : reads/writes pendingReplyText
|
||||
SmartTagEditor ..> ConversationStore : reads currentConversation
|
||||
QualityFeedback ..> ConversationStore : reads messages
|
||||
Workspace ..> ConversationStore : reads/writes panelMode
|
||||
AiAssistantPanel ..> ConversationStore : reads currentConversationId
|
||||
|
||||
%% ====== 类型引用 ======
|
||||
AiRecommendBar ..> AiRecommendation : uses
|
||||
KnowledgeContribute ..> ContributeForm : uses
|
||||
QuickReplyBar ..> QuickReplyNav : uses
|
||||
PanelModeToggle ..> PanelMode : uses
|
||||
AiTrainingPanel ..> TrainingTab : uses
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user