Files
wecom_it_smart_desk/docs/02-技术文档/实现配置/AI对话链路全栈改造实施计划-v1.0.md
T
Simon 44e77dcb0e chore(docs): docs/ 目录全面重新编号 + 重组
**重构前**(旧编号 02-11):
- docs/02-产品需求/      → 00 产品规划/PRD
- docs/03-技术架构/      → 01-05 子目录散落
- docs/04-原型设计/      → 01-02 产品设计(HTML 原型)
- docs/05-原型设计/      → screens/
- docs/06-测试素材/      → 02-E2E / 03-功能 / 04-版本测试
- docs/07-项目管理/      → 任务说明书/日报/计划
- docs/08-安全审计/      → 审计报告
- docs/09-堡垒运维/      → toolbox / deploy
- docs/10-项目管理/      → 任务说明书(重复)
- docs/11-历史归档/      → deploy-nas-archived

**重构后**(新编号 00-07,语义化):
- docs/00-产品开发流程与文档管理规范.md
- docs/00-版本迭代总览.md
- docs/01-产品文档/      (PRD/原型/认证/会话/AI 服务/坐席/集成)
- docs/02-技术文档/      (技术方案/架构图/重构记录/前端改造/实现配置)
- docs/03-测试文档/      (E2E/功能用例/版本报告/缺陷单)
- docs/04-运维文档/      (部署运维/运维指南)
- docs/05-运营文档/      (品牌推广/用户手册)
- docs/06-安全审计/      (审计报告)
- docs/07-项目管理/      (任务说明书/日报/计划/看板)

**净收益**:
- 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号)
- 消除 02-产品需求 与 10-项目管理 的编号重叠
- 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录)
- 把运维/安全/项目管理从 0X 散落改为 04/06/07

合计 494 文件 + 78495 行 / - 14076 行
2026-08-03 18:46:55 +08:00

37 KiB
Raw Blame History

AI 对话链路全栈改造实施计划

版本: v1.2 日期: 2026-07-24 作者: 宋献 (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 透传修复

部署方式:通过 v2_ops.pyjumpserver-V2 技能)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-263get_reply_stream()choices[0].delta.content 作为纯文本逐 chunk 透传
  • ai_handler.py:278-282handle_message()ai_result["content"] 直接放入 AIReplyResult.content
  • conversation.ts:1029-1054handleAiReplyChunk() 直接将 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 路径。

// conversation.ts:458-460
checkApprovalIntent(content).catch(...)  // 异步触发,不等待

Dify 意图识别返回后(3-15 秒),卡片突然插入消息流中,与正在流式输出的 AI 回复交错出现。无任何确认步骤。

问题 2:「无关性」

根因APPROVAL_PREFILTER_KEYWORDSapproval.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 词。

改造:收窄到仅强意图词——

# 改造后:只保留明确表达"申请/提交"意图的词
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:审批意图(文字 + 卡片推荐)

{
  "text": "您想申请 VPN 账号?点击右侧卡片快速提交,一般 1-2 个工作日审批完成。",
  "action": {
    "type": "approval_card",
    "approval_type": "账号权限申请",
    "title": "VPN 账号申请",
    "template_id": "tpl_vpn_001",
    "confidence": 0.92
  },
  "options": null
}

场景 2:交互式排查(文字 + 选项)

{
  "text": "电脑蓝屏了?我来帮您排查。蓝屏时有错误代码吗?",
  "action": null,
  "options": [
    {"label": "有错误代码", "value": "has_code"},
    {"label": "没有", "value": "no_code"},
    {"label": "不确定", "value": "unsure"}
  ]
}

场景 3:纯文字回复

{
  "text": "好的,VPN 账号一般 1-2 个工作日审批完成,届时会通过企微通知您。",
  "action": null,
  "options": null
}

5.3 WS 消息格式

聊天气泡消息

{
  "type": "ai_reply",
  "data": {
    "message_id": "msg_xxx",
    "content": "您想申请 VPN 账号?点击右侧卡片快速提交...",
    "msg_type": "ai_structured",
    "extra_data": {
      "options": [
        {"label": "有错误代码", "value": "has_code"}
      ]
    }
  }
}

侧边栏推荐消息

{
  "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 生产部署验证通过。 v1.3 补充交付2026-07-25,AI 回复打字机逐字显示效果(前端 useTypewriter composable),H5 + Agent 双端已编译验证通过。

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 增加 actionoptions 字段;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_recommendoption_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_structureddynamic_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 完成后需验证:

  • Dify API 返回格式正确(JSON 可解析)
  • 后端 WS 消息格式正确(ai_reply + dynamic_recommend
  • 前端聊天气泡渲染正确(文字 + 选项按钮)
  • 前端侧边栏推荐渲染正确(卡片 + 操作按钮)
  • 选项点击后回传正常(WS option_select → 新 AI 回复)
  • 图片消息处理正常(VisionService 分析 → 描述注入)
  • 错误降级正常(Dify 超时/非 JSON → 降级回复)
  • 坐席端可见 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→纯文本→转人工)

十、边缘情况与隐式设计说明

版本: v1.2 日期: 2026-07-24 说明: 补充文档中未明确说明的隐式设计决策及边缘情况

10.1 选项消息的前端本地添加(已废弃)

原设计(有问题)

用户点击选项 → 前端本地立即添加消息(message_id = option_select_${timestamp}
              → 后端接收 WS option_select → 存储到数据库(message_id = UUID
              → 后端广播 new_message → 前端收到后再添加一次
              → 结果:同一条消息显示两次

问题:前端和后端使用不同的 message_id,前端的去重机制(基于 message_id)失效。

修复方案(v1.2

用户点击选项 → 前端只发送 WS,不本地添加消息
            → 后端接收 → 存储到数据库(message_id = UUID
            → 后端广播 new_message → 前端收到后添加
            → 结果:消息来源唯一,无重复

隐式设计意图(原本未文档化)

  • 前端本地添加消息的目的是提升用户体验:用户点击选项后,立即在界面上显示消息,让用户知道"我已经发送了",避免等待网络响应的 3 秒延迟
  • 但这个设计在文档中没有明确说明,属于「实现时随手加的」

10.2 消息轮询机制

设计

  • 前端使用 3 秒间隔的轮询(poll)作为 WebSocket 的补充
  • 当 WebSocket 消息丢失或延迟时,轮询作为兜底机制确保消息不丢失

考量

  • 轮询间隔 3 秒是体验和服务器压力的平衡点
  • 过短会增加服务器压力,过长会影响体验

10.3 Dify 工作流变更

重要Dify 的工作流(Workflow)修改后,必须手动点击「发布」按钮,否则更改不生效。

验证方法

  • 在 Dify 后台修改工作流后,保存 ≠ 发布
  • 必须点击「发布」按钮,新版本才会生效
  • 未发布时调用 API 会返回旧版本的结果

故障表现:修改了 Dify Prompt 或节点逻辑,但测试接口时发现行为未变化——通常是忘记点击「发布」。

10.4 消息 ID 去重机制

设计

  • 前端使用 processedMessageIds Set 记录已处理的消息 ID
  • handleNewMessage() 中检查消息是否已处理,避免重复渲染

局限

  • 只基于 message_id 去重,不支持内容级别的去重
  • 如果后端生成的 UUID 与前端生成的临时 ID 不同,去重会失效(参见 10.1)

10.5 WebSocket 与 HTTP 降级

设计

  • 发送消息时优先使用 WebSocket
  • 如果 WebSocket 未连接(sent = false),降级为 HTTP API 调用

代码位置conversation.ts:1453-1460


十一、变更日志

版本 日期 变更内容
v1.0 2026-07-13 初始版本
v1.1 2026-07-13 补充部署记录
v1.2 2026-07-24 新增第十章:边缘情况与隐式设计说明
v1.3 2026-07-25 AI回复打字机逐字显示效果交付(前端 H5 + Agent 双端 useTypewriter composable