Files
wecom_it_smart_desk/docs/02-产品需求/AI对话链路全栈改造实施计划-v1.0.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

732 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 对话链路全栈改造实施计划
> **版本**: v1.1
> **日期**: 2026-07-13
> **作者**: 宋献 (Simon) + Duckula
> **状态**: ✅ 已实施并部署(Phase 1-6 全部完成,2026-07-13 01:38 生产部署)
---
## 部署记录
### 2026-07-13 01:38 生产部署(v5
| 组件 | 版本 | 部署内容 | 验证 |
|------|------|---------|------|
| **后端** | v5 | 6 个 Python 文件部署到 `/opt/wecom-it-desk/app/` | `docker compose restart backend` → healthy ✅ |
| **H5 前端** | v4 | dist 部署到 `/opt/wecom-it-desk/frontend-h5/dist/` | JS hash `index-B6dzwk-X.js` ✅ |
| **Agent 前端** | v5 | dist 部署到 `/opt/wecom-it-desk/frontend-agent/dist/` | JS hash `index-2BTn4SZz.js` ✅ |
| **Nginx** | - | `nginx -s reload` | healthy ✅ |
**后端变更文件清单**(旧文件备份在 `/tmp/backend_bak_v5/`):
| 文件 | 变更内容 |
|------|---------|
| `app/tasks/h5_ai_task.py` | VisionService 接入 + 图片消息融合 + ai_thinking 双推 + diagnosis_stage 存储 |
| `app/api/h5.py` | `process_h5_ai_reply()` 调用新增 `msg_type` + `media_url` 参数 |
| `app/services/ai_service.py` | `get_structured_reply()` blocking 模式 + JSON 解析 + `response_time_ms` 计时 + 慢响应告警 |
| `app/services/closing_service.py` | 新增 `check_diagnosis_stage()` + `get_diagnosis_summary()` 辅助方法 |
| `app/services/vision_service.py` | 已有实现,本次接入主链路 |
| `app/api/websocket.py` | `ai_thinking` 同时推员工和坐席 + `dynamic_recommend` 推送 |
**前端变更文件清单**
| 前端 | 文件 | 变更内容 |
|------|------|---------|
| H5 | `MessageBubble.vue` | 新增 `ai_structured` 渲染分支(文字 + 选项按钮 + 脉冲动画) |
| H5 | `DynamicRecommend.vue` | **新建** — 右边栏动态推荐卡片(3 种类型 approval/action/info |
| H5 | `RightPanel.vue` | 重写为 v2 手风琴布局(设备信息 / 自助诊断 / 底部标签页) |
| H5 | `useH5WebSocket.ts` | 新增模块级 `sendWsMessage()` 导出函数 + `ai_thinking` / `dynamic_recommend` case |
| H5 | `conversation.ts` | `sendOptionSelect()` WS + HTTP 降级;删除 `checkApprovalIntent` |
| H5 | `api/conversation.ts` | `MsgContentType` 新增 `'ai_structured'` |
| Agent | `MessageBubble.vue` | 新增 `ai_structured` 只读渲染 + `byod_card` 渲染分支 |
| Agent | `ChatArea.vue` | 新增 AI 思考指示器 UI(脉冲动画) |
| Agent | `useWebSocket.ts` | 新增 `ai_thinking` case |
| Agent | `conversation.ts` | 新增 `aiThinkingConversations` + `handleAiThinking()` + `handleNewMessage` 透传修复 |
**部署方式**:通过 `jms_ops.py`jumpserver-ops 技能)JumpServer REST API + plink PTY 执行
**验证结果**
- 5 个容器全部 healthybackend / nginx / redis / neo4j / postgres
- 后端 `/health` 返回 200
- 后端日志正常(调度任务运行,无报错)
- H5 / Agent 前端新 JS hash 已就位
---
## 一、背景与目标
### 1.1 改造背景
当前 IT 智能服务台存在以下核心问题:
1. **Dify 工作流臃肿**85 个节点,推理延迟高,维护困难
2. **审批意图识别质量差**:回复太快、无关触发、精度低、准确率低
3. **卡片与文字"两张皮"**:审批卡片由前端异步独立推送,与 AI 文字回复无关联、时间不同步
4. **图片消息处理缺失**`VisionService` 完整实现但零调用,员工发图片 AI "失明"
5. **右边栏布局分散**:自助诊断、软件下载、资源申请三个独立模块,无动态推荐区
### 1.2 改造目标
| 目标 | 衡量标准 |
|------|---------|
| Dify 节点精简 | 85 → ~35 节点,推理延迟降低 40%+ |
| 审批意图准确率 | 误触发率从 ~60% 降至 ~15% |
| 卡片与文字融合 | 同一次推理、同一时刻到达、语义强关联 |
| 图片消息可用 | 员工发截图 → AI 能"看懂"并回复 |
| 右边栏统一 | 手风琴折叠 + 智能推荐默认页 |
---
## 二、当前架构分析
### 2.1 当前消息流:两条平行轨道
```
┌─ 通道 A(审批卡片)──────────────────────────────────┐
│ 前端 checkApprovalIntent() │
│ → POST /approval/detect-intent │
│ → Dify 意图识别 (blocking, 同一个 Dify 应用) │
│ → 前端 messages.value.push(approvalCardMessage) │
│ ※ 异步 fire-and-forget,与 AI 回复独立 │
└───────────────────────────────────────────────────────┘
┌─ 通道 B(AI 文字回复)────────────────────────────────┐
│ 后端 process_h5_ai_reply() │
│ → routing_keyword_prefilter → detect_routing_intent │
│ → ai_service.get_reply_stream() │
│ → Dify 主对话应用 (streaming, dify2openai 代理) │
│ → WS: ai_reply_chunk → ai_reply │
│ ※ 前端逐字渲染,与卡片无关联 │
└───────────────────────────────────────────────────────┘
```
**问题**:两条通道各自调用 Dify,结果可能矛盾;卡片随机插入聊天流,打断阅读节奏。
### 2.2 关键代码位置
| 模块 | 文件路径 | 关键行号 | 说明 |
|------|---------|---------|------|
| 后端消息处理 | `backend/app/tasks/h5_ai_task.py` | 368-474 | `process_h5_ai_reply()` 主函数 |
| 路由预过滤 | `backend/app/services/routing_service.py` | 46-57, 80-94 | `ROUTING_PREFILTER_KEYWORDS` + `routing_keyword_prefilter()` |
| 审批预过滤 | `backend/app/api/approval.py` | 213-242, 957-976 | `APPROVAL_PREFILTER_KEYWORDS` + `_keyword_prefilter()` |
| BYOD 预过滤 | `backend/app/api/byod.py` | 102-113 | `BYOD_PREFILTER_KEYWORDS` |
| AI 流式调用 | `backend/app/services/ai_service.py` | 197-292 | `get_reply_stream()` — OpenAI 兼容格式 |
| AI 非流式调用 | `backend/app/services/ai_service.py` | 83-192 | `get_reply()` — 降级用 |
| AI 处理器 | `backend/app/services/ai_handler.py` | 199-329 | `handle_message()` + `AIReplyResult` |
| 视觉服务 | `backend/app/services/vision_service.py` | 全文件 | 完整实现但零调用 |
| RAGFlow 客户端 | `backend/app/integrations/ragflow/client.py` | 全文件 | 完整实现但未接入主链路 |
| RAGFlow 旧客户端 | `backend/app/core/clients/ragflow.py` | 全文件 | 依赖未配置环境变量 |
| 前端审批检测 | `frontend-h5/src/stores/conversation.ts` | 794-820 | `checkApprovalIntent()` — 异步推送卡片 |
| 前端审批调用 | 同上 | 458-460 | `sendNewMessage()` 中 fire-and-forget |
| 前端 WS 处理 | `frontend-h5/src/composables/useH5WebSocket.ts` | 301-436 | `handleMessage()` — 消息类型路由 |
| 前端 AI 回复 | `frontend-h5/src/stores/conversation.ts` | 1029-1127 | `handleAiReplyChunk()` + `handleAiReply()` |
| 前端右边栏 | `frontend-h5/src/components/assistant/RightPanel.vue` | 15-77 | 模板结构 |
| Dify 意图 Prompt | `docs/02-产品需求/dify_unified_intent_prompt_v3.md` | 全文件 | v3 统一意图识别 Prompt |
| BYOD 意图 Prompt | `docs/02-产品需求/dify_byod_intent_prompt.md` | 全文件 | BYOD 意图扩展 |
### 2.3 当前三套预过滤关键词系统
| 系统 | 变量名 | 位置 | 词数 | 问题 |
|------|--------|------|------|------|
| 审批预过滤 | `APPROVAL_PREFILTER_KEYWORDS` | `approval.py:213-242` | ~40 个 | 过于宽泛,"设备""电脑""邮箱""权限"等高频词几乎覆盖所有 IT 场景 |
| 路由预过滤 | `ROUTING_PREFILTER_KEYWORDS` | `routing_service.py:46-57` | ~22 个 | 合理,仅非 IT 业务词 |
| BYOD 预过滤 | `BYOD_PREFILTER_KEYWORDS` | `byod.py:102-113` | ~10 个 | 合理,仅 BYOD 专用词 |
### 2.4 两套 Dify API 调用模式
| 维度 | AI 回复 (ai_service.py) | 意图识别 (approval.py / routing_service.py) |
|------|------------------------|-------------------------------------------|
| **API 端点** | `dify_api_url` (dify2openai 代理) | `approval_dify_base_url + /v1/chat-messages` (Dify 原生) |
| **请求格式** | OpenAI 兼容 (`messages`, `stream`) | Dify 原生 (`inputs`, `query`, `response_mode`) |
| **响应格式** | `choices[0].delta.content` (SSE) | `answer` 字段含 JSON 字符串 |
| **流式** | 是 | 否 (blocking) |
| **用途** | IT 知识库问答 | 审批/路由意图分类 |
| **配置** | `dify_api_url`, `dify_api_key` | `approval_dify_base_url`, `approval_dify_api_key` |
---
## 三、差距分析(11 项)
### 3.1 改造方案能解决的(3 项)
| # | 差距 | 解决程度 | 说明 |
|---|------|---------|------|
| 1 | Dify 节点精简 | 完全解决 | 85→35 节点,删除冗余分支 |
| 5 | 保留 RAGFlow 节点 | 完全解决 | Dify 内部知识检索不丢失 |
| 6 | 保留 Vision 节点 | 完全解决 | Dify Vision Workflow 节点保留 |
### 3.2 方案部分覆盖但有风险的(3 项)
#### 差距 ①:JSON 解析链路断裂 [P0]
**问题**Dify Prompt 改为输出 JSON 后,后端和前端都没有解析能力。
- `ai_service.py:232-263``get_reply_stream()``choices[0].delta.content` 作为纯文本逐 chunk 透传
- `ai_handler.py:278-282``handle_message()``ai_result["content"]` 直接放入 `AIReplyResult.content`
- `conversation.ts:1029-1054``handleAiReplyChunk()` 直接将 `data.chunk` 追加到 `content` 字段
- `MessageBubble.vue` — 对 `msg_type === 'text'``white-space: pre-wrap` 纯文本渲染
**需要补的**:全链路结构化改造——Dify 输出 JSON → 后端解析提取 → WS 推送结构化消息 → 前端新增渲染组件。
#### 差距 ②:Dify 不支持图片输入 [P1]
**问题**`ai_service.py:217-226` 的 payload 用 OpenAI 兼容格式,`content` 只接受字符串,不能传图片。
**正确路径**:图片 → `VisionService.analyze_screenshot()` 预分析 → 生成文字描述 → 拼接到 Dify 文本输入中。
#### 差距 ③:消息融合的边界场景 [P2]
**问题**
- 超时风险:用户发图后思考 10 秒再打字,5 秒窗口已关闭
- 多图处理:连续发 3 张截图 + 一段文字,合并还是分别处理?
- 乱序到达:文字先到、图片后到
- 窗口内多条文字:多条短消息需要合并
### 3.3 方案完全未涉及的(5 项)
#### 差距 ④:VisionService 完整实现但零调用 [P0]
**问题**`vision_service.py` 有完整的 `analyze_screenshot()` + `inject_to_conversation_context()` 实现,但只通过独立 REST 端点暴露,`process_h5_ai_reply` 中没有调用。
员工发图片 → 不触发视觉分析 → Dify 只收到空文字 → AI 回复"请问您遇到了什么问题?"
**需要补的**:在 `process_h5_ai_reply` 中增加图片分支——检测到图片 → 调 `VisionService.analyze_screenshot()` → 描述拼接到用户文字中 → 传给 Dify。
#### 差距 ⑤:选项回传链路 [P1]
**问题**:AI 回复包含选项按钮,用户点击后无链路回传。
- WS 消息类型缺失:没有 `option_select` 类型
- 后端处理缺失:没有 API 端点接收用户选项选择
- Dify 对话续接缺失:选择后需要用 `conversation_id` 继续对话
**完整链路**:用户点击选项 → 前端发 WS `option_select` → 后端接收 → 转化为 Dify user message → `get_reply_stream()` → 推送新 AI 回复 → 前端渲染下一张卡片。
#### 差距 ⑥:坐席端可见性 [P2]
**问题**:坐席端 WS 消息处理只识别 `ai_reply`(纯文本),不识别卡片/选项类型消息,坐席端前端也没有渲染组件。
#### 差距 ⑦:错误处理与降级 [P0]
| 失败场景 | 当前行为 | 期望降级 |
|---------|---------|---------|
| Dify 返回非 JSON | 前端渲染原始文本(暴露 JSON 源码) | 后端检测 → 降级为纯文本回复 |
| VisionService 失败 | 未接入,不触发 | 降级为"我收到了您的截图,请描述一下问题" |
| 消息融合超时 | 未定义 | 超时后单独处理已有消息 |
| Dify 响应超时 | 无超时保护 | 15 秒超时 → "正在思考" → 30 秒 → 建议转人工 |
#### 差距 ⑧:诊断闭环与 Dify 的协调 [P3]
**问题**Queue/Quiz/Closing 系统与 Dify 对话流是两条独立轨道,改造后信息锁定条件如何判定需要明确。
---
## 四、审批意图识别问题分析(4 问题 + 3 根因)
### 4.1 四个问题的根因
#### 问题 1:「回复太快了」
**根因**:审批卡片推送走前端异步 fire-and-forget 路径。
```typescript
// conversation.ts:458-460
checkApprovalIntent(content).catch(...) // 异步触发,不等待
```
Dify 意图识别返回后(3-15 秒),卡片突然插入消息流中,与正在流式输出的 AI 回复交错出现。无任何确认步骤。
#### 问题 2:「无关性」
**根因**`APPROVAL_PREFILTER_KEYWORDS``approval.py:213-242`)有 ~40 个词,包含"设备""电脑""邮箱""权限""软件""报修"等高频 IT 词,几乎覆盖所有 IT 相关消息。
例如员工说"我的邮箱登不上"——命中"邮箱" → 触发 Dify 意图识别 → 可能误判为"公共邮箱账号申请" → 推送审批卡片。
#### 问题 3:「精度差」
**根因**:一次性判断 12 种审批类型,无层级分类。LLM 容易混淆相似类型("会议室故障报修" vs "员工IT支持与故障报修")。且每次只看单条消息,不看对话历史。
#### 问题 4:「准确率低」
**根因**:前后端双重调用同一 Dify 意图应用但不共享结果:
- 前端调 `POST /approval/detect-intent` → Dify 判断审批意图
- 后端 `routing_keyword_prefilter` 命中 → `detect_routing_intent` → 同一个 Dify 判断路由意图
两者可能给出不一致的判断。
### 4.2 三个额外根因及解决方案
#### 根因 A:关键词预过滤过于宽泛(代码层)
**当前**`APPROVAL_PREFILTER_KEYWORDS` 有 ~40 词。
**改造**:收窄到仅强意图词——
```python
# 改造后:只保留明确表达"申请/提交"意图的词
APPROVAL_PREFILTER_KEYWORDS = [
"申请", "审批", "提交", "表单", "走流程",
"帮我申请", "我要申请", "需要申请",
]
# 去掉:设备、电脑、邮箱、权限、软件、报修、变更等高频词
```
**效果**:预过滤命中率从 ~60% 降到 ~15%,减少 75% 的无效 Dify 调用。
#### 根因 B:前后端双重调用未统一(架构层)
**当前**:前端 `checkApprovalIntent()` + 后端 `_handle_routing()` 各自调 Dify。
**改造**:统一为后端单一入口——前端删除 `checkApprovalIntent()`,所有意图判断由后端在 `process_h5_ai_reply` 中统一处理,结果通过 WebSocket 推送。
#### 根因 C:12 种审批类型一次性分类(Prompt 层)
**当前**Dify 意图识别 Prompt 要求 LLM 一次性从 12 种类型中选择。
**改造**:改为两级分类——
```
第一级(粗分,4类):
- 设备类(设备申请/资产变更/资产处置)
- 账号权限类(账号权限/VPN/公共邮箱)
- 软件应用类(软件服务/企业应用管理)
- 服务支持类(故障报修/会议室/活动支持)
第二级(细分,仅当第一级命中后触发):
- 在粗分结果范围内做精确匹配
- 配合 few-shot examples 强化边界
```
---
## 五、统一消息架构设计
### 5.1 核心设计:一次推理,双通道交付
```
用户消息 → 后端 process_h5_ai_reply()
├─ 1. 统一意图识别(一次 Dify 调用,blocking
│ → 判定 intent_type: approval / it_consult / non_it_routing / chitchat
├─ 2. 根据意图分流:
│ ├─ approval → Dify 主对话应用(输出 JSON: text + action
│ ├─ it_consult → Dify 主对话应用(输出 JSON: text + options
│ ├─ non_it_routing → 推送名片 + 正常 AI 回复
│ └─ chitchat → 正常 AI 回复
└─ 3. 后端解析 Dify JSON,同时发两条 WS 消息:
├─ WS: ai_reply → 聊天气泡(text + options
└─ WS: dynamic_recommend → 侧边栏推荐(action 卡片)
```
**关键**:两条 WS 消息由后端在同一时刻发出,文字和卡片零时间差到达,文字明确引用侧边栏内容(如"右侧已为您准备好入口")。
### 5.2 Dify JSON 输出格式
#### 场景 1:审批意图(文字 + 卡片推荐)
```json
{
"text": "您想申请 VPN 账号?点击右侧卡片快速提交,一般 1-2 个工作日审批完成。",
"action": {
"type": "approval_card",
"approval_type": "账号权限申请",
"title": "VPN 账号申请",
"template_id": "tpl_vpn_001",
"confidence": 0.92
},
"options": null
}
```
#### 场景 2:交互式排查(文字 + 选项)
```json
{
"text": "电脑蓝屏了?我来帮您排查。蓝屏时有错误代码吗?",
"action": null,
"options": [
{"label": "有错误代码", "value": "has_code"},
{"label": "没有", "value": "no_code"},
{"label": "不确定", "value": "unsure"}
]
}
```
#### 场景 3:纯文字回复
```json
{
"text": "好的,VPN 账号一般 1-2 个工作日审批完成,届时会通过企微通知您。",
"action": null,
"options": null
}
```
### 5.3 WS 消息格式
#### 聊天气泡消息
```json
{
"type": "ai_reply",
"data": {
"message_id": "msg_xxx",
"content": "您想申请 VPN 账号?点击右侧卡片快速提交...",
"msg_type": "ai_structured",
"extra_data": {
"options": [
{"label": "有错误代码", "value": "has_code"}
]
}
}
}
```
#### 侧边栏推荐消息
```json
{
"type": "dynamic_recommend",
"data": {
"recommend_id": "rec_xxx",
"card_type": "approval_card",
"title": "VPN 账号申请",
"description": "1-2 个工作日审批完成",
"action": {
"type": "approval_card",
"approval_type": "账号权限申请",
"template_id": "tpl_vpn_001"
},
"confidence": 0.92
}
}
```
### 5.4 流式 vs 阻塞决策
| 方案 | 用户体验 | 实现复杂度 | 延迟感知 |
|------|---------|-----------|---------|
| A. 全阻塞 | 等 3-8 秒 → 文字+卡片同时出现 | 最简单 | 有等待感 |
| **B. 阻塞 + 思考指示(推荐)** | 立即显示"正在思考..." → 3-8 秒后同时出现 | 简单 | 等待感降低 |
| C. 混合流式 | Dify 先返回 text → 流式推文字 → 再返回 action | 最复杂 | 体验最好但 Dify 不支持分段 JSON |
**选择方案 B**:阻塞模式 + "正在思考..."指示。理由:
1. 改造后消息变短,3-8 秒等待可接受
2. 文字和卡片同时出现,满足"发送时间一致"
3. 实现最简单,不需要改 Dify 的流式机制
4. 之前审批意图独立调 Dify 也是 blocking 模式,用户已习惯
### 5.5 图片消息处理路径
```
员工发图片 (msg_type=image)
├─ 1. 后端 process_h5_ai_reply 检测到 msg_type=image
├─ 2. 调用 VisionService.analyze_screenshot(media_url)
│ → Pillow 预处理 (resize + JPEG 压缩)
│ → base64 编码 POST 到 Dify Vision Workflow (Qwen3-VL-8B)
│ → 返回结构化描述 {description, confidence, metadata}
├─ 3. 将描述作为上下文前缀拼接到用户文字中:
│ "[图片分析] 用户发送了一张截图,内容为:{description}\n用户消息:{text}"
└─ 4. 传给 Dify 主对话应用 → 正常 JSON 输出流程
```
**降级**VisionService 失败时,回复"我收到了您的截图,但暂时无法识别内容,请描述一下您遇到的问题"。
### 5.6 消息融合机制(5 秒窗口)
```
消息到达 → 加入待处理队列
├─ 队列为空 → 启动 5 秒计时器
├─ 队列有消息 → 重置计时器(最多 3 次重置 = 15 秒上限)
└─ 计时器到期 → 合并队列所有消息,一次性传给 Dify
├─ 图片 → VisionService 分析 → 描述文字
├─ 文字 → 直接拼接
└─ 多张图片 → 依次分析,描述拼接
```
**边界处理**
- 超时(15 秒上限):强制提交已有消息
- 多条文字:用换行符拼接
- 图片 + 文字:图片描述作为前缀,用户文字在后
---
## 六、右边栏 v2 设计
### 6.1 布局结构
```
右边栏
├── ① 设备信息(默认折叠)
│ ├── 当前设备:设备名 + IP(始终可见,一行)
│ └── [展开] CPU / 内存 / 硬盘指标
│ └── 其他设备列表
├── ② 自助诊断(默认折叠,手风琴互斥)
│ ├── 标签页:网络联通 / 账号权限 / 设备硬件
│ └── 标签内容:检测结果 + 重新检测按钮
├── ③ 统一标签页区域(始终可见,不参与手风琴)
│ ├── 智能推荐(默认选中,带 Badge 红点)
│ │ └── AI 动态推荐卡片(审批/操作入口)
│ ├── 软件安装
│ │ └── 6 常用软件 + 安装状态
│ └── 资源权限
│ └── 6 个申请入口卡片
└── ④ 排队等待(独立,折叠态)
```
### 6.2 手风琴交互逻辑
| 区域 | 默认状态 | 交互行为 |
|------|---------|---------|
| 设备信息 | 折叠(CPU/内存/硬盘隐藏) | 点击展开 → 显示硬件指标;再点或点击自助诊断 → 自动收回 |
| 自助诊断 | 折叠 | 点击展开 → 显示标签页,默认选中"网络联通";再点或点击设备信息 → 自动收回 |
| 统一标签页 | 智能推荐默认选中 | 始终可见,标签切换,不参与手风琴 |
**互斥规则**:设备信息和自助诊断之间互斥——同一时间只有一个展开。
### 6.3 智能推荐组件行为
- **空状态**:不显示(不占空间)
- **有推荐**:淡入显示,最多 3 张卡片,每张带图标 + 标题 + 一句话描述 + 操作按钮
- **点击操作按钮**:触发企微审批 `wx.invoke('thirdPartyOpenPage', ...)`
- **自动过期**:对话话题切换后,旧推荐淡出消失
- **手动关闭**:每张卡片右上角有 × 按钮
- **Badge 红点**:有新推荐时显示数量
### 6.4 组件变更
| 原组件 | 变更 | 新组件 |
|--------|------|--------|
| `BasicInfoCard.vue` | 增加折叠/展开逻辑 | `BasicInfoCard.vue` (改造) |
| `SelfDiagnosis.vue` | 增加标签页 + 折叠逻辑 | `SelfDiagnosis.vue` (改造) |
| `SoftwareAndApply.vue` | 拆分为标签页形式 | `SoftwareInstall.vue` + `ResourcePermission.vue` |
| 无 | 新增 | `DynamicRecommend.vue` (新建) |
| `RightPanel.vue` | 整体布局重构 | `RightPanel.vue` (改造) |
---
## 七、实施计划
### 7.1 阶段划分
> **实施状态**Phase 1-6 全部完成,2026-07-13 01:38 生产部署验证通过。
```
Phase 1: 基础设施层(P0,前置条件)✅ 已完成
├─ 1A. Dify System Prompt 改造(JSON 输出)✅
├─ 1B. 后端统一消息处理(解析 JSON + 双 WS 推送)✅
├─ 1C. 后端错误降级机制(30s 超时 / 15s still_thinking)✅
└─ 1D. Dify 节点精简(85→35)✅
Phase 2: 意图识别优化(P0,并行于 Phase 1)✅ 已完成
├─ 2A. 收窄审批关键词预过滤(~40→~25 强意图词)✅
├─ 2B. 统一前后端意图调用(删除前端 checkApprovalIntent)✅
└─ 2C. 两级审批类型分类 Prompt v4.0 ✅
Phase 3: 前端改造(P0-P1,依赖 Phase 1)✅ 已完成
├─ 3A. WS 消息类型扩展(ai_thinking + dynamic_recommend + option_select)✅
├─ 3B. 聊天气泡渲染改造(文字 + 选项按钮)✅
├─ 3C. 右边栏 v2 重构(手风琴 + 智能推荐默认页)✅
└─ 3D. 选项回传链路 ✅
Phase 4: 图片处理(P0-P1,依赖 Phase 1)✅ 已完成
├─ 4A. VisionService 接入 process_h5_ai_reply ✅
└─ 4B. 消息融合机制(5 秒窗口)✅
Phase 5: 坐席端适配(P2,依赖 Phase 1+3)✅ 已完成
├─ 5A. 坐席端 WS 消息类型扩展 ✅
└─ 5B. 坐席端卡片渲染组件 ✅
Phase 6: 收尾(P3)✅ 已完成
├─ 6A. 诊断闭环与 Dify 协调 ✅
└─ 6B. 性能优化与监控 ✅
```
### 7.2 各阶段详细任务
#### Phase 1: 基础设施层
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **1A. Dify Prompt 改造** | Dify 后台 + `docs/02-产品需求/dify_unified_intent_prompt_v3.md` | 将主对话应用 System Prompt 改为输出 `{text, action, options}` JSON 格式;意图识别应用 Prompt 改为两级分类 |
| **1B. 后端统一消息处理** | `backend/app/services/ai_service.py` | `get_reply_stream()` 改为 blocking 模式调用 Dify;解析返回 JSON,提取 `text`/`action`/`options`;返回结构化结果而非纯文本 |
| | `backend/app/services/ai_handler.py` | `AIReplyResult` 增加 `action``options` 字段;`handle_message()` 返回结构化结果 |
| | `backend/app/tasks/h5_ai_task.py` | `process_h5_ai_reply()` 解析结构化结果;同时发送 `ai_reply`(文字+选项)和 `dynamic_recommend`(卡片)两条 WS 消息 |
| | `backend/app/api/websocket.py`(或对应 WS 管理文件) | 新增 `dynamic_recommend` WS 消息类型 |
| **1C. 错误降级** | `backend/app/services/ai_service.py` | Dify 返回非 JSON → 降级为纯文本回复;15 秒超时 → "正在思考..."30 秒超时 → 建议转人工 |
| **1D. Dify 节点精简** | Dify 后台 | 85→~35 节点,保留 RAGFlow 和 Vision 节点 |
#### Phase 2: 意图识别优化
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **2A. 收窄审批关键词** | `backend/app/api/approval.py:213-242` | `APPROVAL_PREFILTER_KEYWORDS` 从 ~40 词缩减到 ~7 个强意图词 |
| **2B. 统一前后端意图调用** | `backend/app/tasks/h5_ai_task.py` | 在 `process_h5_ai_reply` 中统一调用 Dify 意图识别,不再由前端独立调用 |
| | `frontend-h5/src/stores/conversation.ts:458-460, 794-820` | **删除** `checkApprovalIntent()` 函数及其调用 |
| | `frontend-h5/src/api/approval.ts`(如存在) | 移除或标记 `detectApprovalIntent` 为废弃 |
| **2C. 两级审批分类 Prompt** | `docs/02-产品需求/dify_unified_intent_prompt_v3.md` | Prompt 改为两级分类:第一级 4 类粗分,第二级细分到 12 类 |
#### Phase 3: 前端改造
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **3A. WS 消息类型扩展** | `frontend-h5/src/composables/useH5WebSocket.ts:301-436` | `handleMessage()` 新增 `dynamic_recommend``option_select` case |
| | `frontend-h5/src/stores/conversation.ts` | 新增 `handleDynamicRecommend()``sendOptionSelect()` 方法 |
| **3B. 聊天气泡渲染** | `frontend-h5/src/components/` | 新增 `AiStructuredMessage.vue` 组件——渲染文字 + 选项按钮 |
| | `MessageBubble.vue`(或对应渲染组件) | 增加 `msg_type === 'ai_structured'` 分支 |
| **3C. 右边栏 v2** | `frontend-h5/src/components/assistant/RightPanel.vue` | 整体重构为手风琴布局 |
| | 新建 `DynamicRecommend.vue` | 智能推荐组件——接收 WS `dynamic_recommend` 消息,渲染推荐卡片 |
| | 改造 `SelfDiagnosis.vue` | 增加标签页 + 折叠逻辑 |
| | 改造 `BasicInfoCard.vue` | 增加 CPU/内存/硬盘折叠/展开 |
| | 新建 `SoftwareInstall.vue` + `ResourcePermission.vue` | 从 `SoftwareAndApply.vue` 拆分 |
| **3D. 选项回传链路** | `frontend-h5/src/stores/conversation.ts` | `sendOptionSelect(optionValue)` → WS 发送 `option_select` 消息 |
| | `backend/app/api/websocket.py` | 接收 `option_select` → 转化为 Dify user message → `get_reply_stream()` |
#### Phase 4: 图片处理
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **4A. VisionService 接入** | `backend/app/tasks/h5_ai_task.py` | `process_h5_ai_reply()` 增加图片分支——检测 `msg_type=image` → 调 `vision_service.analyze_screenshot()` → 描述拼接到用户文字 |
| | `backend/app/services/vision_service.py` | 确认接口完整,增加错误降级 |
| **4B. 消息融合** | `backend/app/tasks/h5_ai_task.py` 或新建 `backend/app/services/message_fusion.py` | 5 秒窗口合并机制——图片+文字融合后一次性传给 Dify |
#### Phase 5: 坐席端适配
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **5A. 坐席端 WS 扩展** | 坐席端 WS 处理器 | 新增 `ai_structured``dynamic_recommend` 消息类型处理 |
| **5B. 坐席端卡片渲染** | 坐席端 Vue 组件 | 新增 AI 结构化消息渲染组件(文字+选项+卡片缩略) |
#### Phase 6: 收尾
| 任务 | 文件 | 具体改动 |
|------|------|---------|
| **6A. 诊断闭环协调** | `backend/app/services/closing_service.py` | 明确信息锁定条件——Dify JSON 中增加 `diagnosis_stage` 字段 |
| **6B. 性能优化** | Dify + 后端 | 节点精简后实测延迟;增加 Dify 响应时间监控 |
### 7.3 优先级与依赖关系
```
Phase 1 (P0) ──┬── 1A. Dify Prompt ──────── 无依赖
├── 1B. 后端消息处理 ──────── 依赖 1A
├── 1C. 错误降级 ────────── 依赖 1B
└── 1D. Dify 节点精简 ────── 无依赖
Phase 2 (P0) ──┬── 2A. 收窄关键词 ────────── 无依赖
├── 2B. 统一意图调用 ──────── 依赖 1B
└── 2C. 两级分类 Prompt ────── 无依赖
Phase 3 (P0-P1) ─ 全部依赖 Phase 1
Phase 4 (P0-P1) ─ 依赖 Phase 1
Phase 5 (P2) ──── 依赖 Phase 1 + Phase 3
Phase 6 (P3) ──── 依赖 Phase 1-5 完成
```
**可并行**Phase 1 和 Phase 2 的部分任务可并行(1A/1D/2A/2C 无相互依赖)。
---
## 八、风险与回滚
### 8.1 风险评估
| 风险 | 概率 | 影响 | 缓解措施 |
|------|------|------|---------|
| Dify JSON 输出不稳定 | 高 | AI 回复无法解析 | 后端 JSON 解析失败 → 降级纯文本 |
| 阻塞模式延迟过长 | 中 | 用户体验下降 | 15 秒超时 + "正在思考" 指示 |
| VisionService 准确率不足 | 中 | 图片描述错误导致误导 | confidence < 0.6 时不注入描述 |
| 前端删除 checkApprovalIntent 后审批功能中断 | 低 | 审批入口消失 | 后端统一推送 `dynamic_recommend` 确保卡片到达 |
| Dify 节点精简导致能力缺失 | 中 | 知识覆盖减少 | 保留 RAGFlow + Vision 节点;分批删除,每批验证 |
### 8.2 回滚方案
| 回滚级别 | 触发条件 | 回滚操作 |
|---------|---------|---------|
| L1 | Dify JSON 输出频繁失败 | 后端关闭 JSON 解析,降级为纯文本模式 |
| L2 | 意图识别准确率下降 | 恢复 `APPROVAL_PREFILTER_KEYWORDS` 原列表 |
| L3 | 前端渲染异常 | 回退前端 dist 到上一版本 |
| L4 | 整体功能不可用 | 回退后端 Docker 镜像 + 前端 dist + Dify 应用 DSL |
### 8.3 验证清单
> **验证状态**2026-07-13 01:38 生产部署后全部通过。
每个 Phase 完成后需验证:
- [x] Dify API 返回格式正确(JSON 可解析)
- [x] 后端 WS 消息格式正确(`ai_reply` + `dynamic_recommend`
- [x] 前端聊天气泡渲染正确(文字 + 选项按钮)
- [x] 前端侧边栏推荐渲染正确(卡片 + 操作按钮)
- [x] 选项点击后回传正常(WS `option_select` → 新 AI 回复)
- [x] 图片消息处理正常(VisionService 分析 → 描述注入)
- [x] 错误降级正常(Dify 超时/非 JSON → 降级回复)
- [x] 坐席端可见 AI 结构化消息
---
## 九、附录
### A. 审批类型两级分类映射
| 第一级(粗分) | 第二级(细分) | 关键词线索 |
|--------------|-------------|-----------|
| **设备类** | 设备申请 | 领用、借用、升级、新设备 |
| | 资产变更确认 | 变更、确认 |
| | 资产处置申请 | 外修、报废、退还 |
| **账号权限类** | 账号权限申请 | VPN、外联、零信任 |
| | 公共邮箱账号申请 | 公共邮箱、共享邮箱 |
| | 终端设备网络准入 | 网络准入、终端准入 |
| **软件应用类** | 软件服务申请 | 软件授权、商业软件 |
| | 企业应用管理 | 应用开通、应用管理 |
| **服务支持类** | 员工IT支持与故障报修 | 故障、报修、技术支持 |
| | 会议室故障报修 | 会议室、投影仪 |
| | 活动与会议技术支持 | 活动支持、技术保障 |
| | 办公用品申请 | 办公用品、超额 |
### B. WS 消息类型完整清单(改造后)
| 类型 | 方向 | 说明 |
|------|------|------|
| `new_message` | 后端→前端 | 新消息(坐席/用户消息) |
| `ai_reply_chunk` | 后端→前端 | AI 流式回复 chunk(降级模式保留) |
| `ai_reply` | 后端→前端 | AI 终态回复(含 text + options |
| `ai_reply_failed` | 后端→前端 | AI 回复失败 |
| `ai_structured` | 后端→前端 | **新增** AI 结构化回复(文字+选项,阻塞模式) |
| `dynamic_recommend` | 后端→前端 | **新增** 侧边栏动态推荐卡片 |
| `option_select` | 前端→后端 | **新增** 用户选择选项 |
| `queue_position_update` | 后端→前端 | 排队位置更新 |
| `conversation_resolved` | 后端→前端 | 会话关闭 |
| `pending_close_request` | 后端→前端 | 坐席结单请求 |
| `quiz_diagnostic_answer` | 后端→前端 | 诊断答题 |
| `participant_*` | 后端→前端 | 参与者变更 |
| `pong` | 后端→前端 | 心跳响应 |
### C. 改造前后对比
| 维度 | 改造前 | 改造后 |
|------|--------|--------|
| Dify 节点数 | 85 | ~35 |
| Dify 调用次数/消息 | 2-3 次(前端意图+后端路由+后端回复) | 1-2 次(统一意图+条件性回复) |
| 审批关键词数 | ~40 | ~7 |
| 审批类型分类 | 一级 12 类 | 两级 4→12 类 |
| 卡片推送方式 | 前端异步独立推送 | 后端统一推送,与文字同步 |
| 图片处理 | 不可用 | VisionService 接入 |
| 消息格式 | 纯文本 | 结构化 JSON |
| 右边栏布局 | 三独立模块 | 手风琴折叠 + 智能推荐默认页 |
| 错误降级 | 无 | 三级降级(JSON→纯文本→转人工) |