bea288e414
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
715 lines
28 KiB
Markdown
715 lines
28 KiB
Markdown
# 业务路由推荐 — 系统架构设计
|
||
|
||
> **版本**: v1.0
|
||
> **日期**: 2026-07-15
|
||
> **作者**: Bob(架构师)
|
||
> **状态**: 待评审
|
||
> **关联PRD**: `docs/02-产品需求/业务路由推荐-PRD.md`
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [Part A: 系统设计](#part-a-系统设计)
|
||
- [1. 实现方案](#1-实现方案)
|
||
- [2. 文件列表](#2-文件列表)
|
||
- [3. 数据结构与接口](#3-数据结构与接口)
|
||
- [4. 程序调用流程](#4-程序调用流程)
|
||
- [5. 待明确事项](#5-待明确事项)
|
||
- [Part B: 任务分解](#part-b-任务分解)
|
||
- [6. 依赖包列表](#6-依赖包列表)
|
||
- [7. 任务列表](#7-任务列表)
|
||
- [8. 共享知识](#8-共享知识)
|
||
- [9. 任务依赖图](#9-任务依赖图)
|
||
|
||
---
|
||
|
||
# Part A: 系统设计
|
||
|
||
## 1. 实现方案
|
||
|
||
### 1.1 技术栈选型
|
||
|
||
**沿用现有技术栈,不引入新框架**:
|
||
|
||
| 层 | 技术栈 | 说明 |
|
||
|---|--------|------|
|
||
| 后端 | FastAPI + SQLAlchemy + PostgreSQL + Redis | 与现有审批/BYOD 系统完全一致 |
|
||
| H5前端 | Vue3 + Vant4 + TypeScript | 名片卡片用 Vant 组件实现 |
|
||
| 坐席前端 | Vue3 + Element Plus | 名片卡片用 Element Plus 风格适配 |
|
||
| AI意图识别 | Dify 原生 API(扩展现有 Prompt) | 同一个 Dify 应用,API Key 不变 |
|
||
|
||
### 1.2 核心技术挑战与对策
|
||
|
||
| 挑战 | 对策 |
|
||
|------|------|
|
||
| **向后兼容**:扩展 Dify Prompt 后不能破坏现有审批识别 | 原审批判断规则原文保留不动,仅新增非IT判断段落;原3字段(`is_approval_request`/`confidence`/`approval_type`)语义和取值不变,新增3字段;上线前用审批测试用例集回归 |
|
||
| **路由检测时机**:在消息处理流程中何时调用 Dify | 在 `process_h5_ai_reply` 后台任务中新增路由检测步骤(与 BYOD 拦截模式一致),位于 BYOD 检测之后、打招呼/呼叫人工检测之前;通过关键词预过滤减少不必要的 Dify 调用 |
|
||
| **误路由防护**:IT问题被误识别为非IT | `routing_confidence < 0.7` 不触发名片推荐,走正常 AI 回复流程;路由文本消息中附「仍需IT帮助」兜底引导 |
|
||
| **企微跳转可靠性**:`openEnterpriseChat` 调用失败 | 前端容错:userid 为空或 SDK 未就绪时 Toast 提示;上线前核对联系人 userid 数据 |
|
||
| **双通道下发**:名片消息需同时到达 H5 和坐席端 | 复用现有 `ws_manager.broadcast_to_employees()`(推H5)+ `ws_manager.broadcast()`(推坐席)双通道模式 |
|
||
|
||
### 1.3 架构模式
|
||
|
||
采用**现有系统的「后台任务 + WS推送」异步架构**,与 BYOD 卡片完全一致的消息处理模式:
|
||
|
||
```
|
||
员工发消息 → HTTP即时返回 → 后台AI任务(process_h5_ai_reply)
|
||
↓
|
||
┌─ BYOD关键词拦截(现有)
|
||
├─ 路由关键词预过滤 → Dify统一意图识别(新增)
|
||
│ └─ non_it_routing && confidence≥0.7 → 发送名片卡片
|
||
├─ 打招呼/呼叫人工检测(现有)
|
||
└─ Dify流式回复(现有)
|
||
```
|
||
|
||
### 1.4 关键设计决策
|
||
|
||
1. **路由检测放在后台任务而非前端调用**:PRD 要求「后端自动发送」名片消息,且需经 WS 双通道下发。与 BYOD 卡片的处理模式一致,在 `process_h5_ai_reply` 中集成,保证服务端控制消息发送。
|
||
|
||
2. **统一 Dify Prompt 但独立调用函数**:Dify 后台 Prompt 扩展为统一意图识别引擎(一次调用输出审批+路由判断),但后端 `routing_service.py` 和 `approval.py` 各自维护调用函数,避免耦合。两者使用相同的 Dify 应用(同一 API Key),只是解析各自关心的字段。
|
||
|
||
3. **关键词预过滤双层设计**:路由预过滤关键词(打印机/工牌/报销等)与审批预过滤关键词可能重叠(如"办公用品")。Dify Prompt 内部的判断优先级(先审批→再IT咨询→再非IT路由)确保正确分流,预过滤只负责「是否值得调 Dify」。
|
||
|
||
4. **名片消息三段式发送**:路由文本说明 → 名片卡片 → 系统提示,三条消息分别落库 + WS推送,与 PRD 4.3 交互流程一致。
|
||
|
||
---
|
||
|
||
## 2. 文件列表
|
||
|
||
### 2.1 新建文件
|
||
|
||
| # | 文件路径 | 说明 |
|
||
|---|---------|------|
|
||
| 1 | `backend/app/models/business_contact.py` | 业务联系人数据模型 |
|
||
| 2 | `backend/app/models/routing_event.py` | 路由命中统计模型(P1) |
|
||
| 3 | `backend/app/services/routing_service.py` | 路由服务:Dify调用、联系人查询、名片发送 |
|
||
| 4 | `backend/app/api/routing.py` | 路由API:联系人查询、坐席手动发名片(P1) |
|
||
| 5 | `backend/alembic/versions/047_business_contacts.py` | 数据库迁移:建表 + 初始数据 |
|
||
| 6 | `docs/02-产品需求/dify_unified_intent_prompt_v3.md` | 扩展后的统一意图识别 Dify Prompt |
|
||
| 7 | `frontend-h5/src/components/chat/ContactCard.vue` | H5员工名片卡片组件 |
|
||
| 8 | `frontend-h5/src/api/routing.ts` | H5路由API(联系人查询,供组件使用) |
|
||
| 9 | `frontend-agent/src/components/chat/ContactCardInline.vue` | 坐席端名片卡片组件 |
|
||
| 10 | `frontend-agent/src/api/routing.ts` | 坐席端路由API(手动发名片) |
|
||
|
||
### 2.2 修改文件
|
||
|
||
| # | 文件路径 | 修改内容 |
|
||
|---|---------|---------|
|
||
| 11 | `backend/app/config.py` | 新增 `routing_confidence_threshold` 配置项 |
|
||
| 12 | `backend/app/models/__init__.py` | 注册 `BusinessContact`、`RoutingEvent` 模型 |
|
||
| 13 | `backend/app/api/router.py` | 注册 `routing` 路由器 |
|
||
| 14 | `backend/app/api/approval.py` | `_call_dify_approval_intent()` 解析扩展字段;`ApprovalDetectIntentResponse` 新增 `intent_type`/`business_category`/`routing_confidence` 字段 |
|
||
| 15 | `backend/app/tasks/h5_ai_task.py` | 新增路由检测步骤:`_handle_routing()` |
|
||
| 16 | `frontend-h5/src/api/conversation.ts` | `MsgContentType` 新增 `'contact_card'` |
|
||
| 17 | `frontend-h5/src/components/chat/MessageBubble.vue` | 新增 `msg.msg_type === 'contact_card'` 渲染分支 |
|
||
| 18 | `frontend-agent/src/components/chat/MessageBubble.vue` | 新增 `contact_card` 渲染分支 |
|
||
| 19 | `frontend-agent/src/components/chat/InputBox.vue` | 新增「发名片」入口按钮(P1) |
|
||
|
||
---
|
||
|
||
## 3. 数据结构与接口
|
||
|
||
### 3.1 类图
|
||
|
||
> 完整类图见 `docs/03-技术架构/业务路由推荐-类图.mermaid`
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class BusinessContact {
|
||
+int id
|
||
+str name
|
||
+str gender
|
||
+str department
|
||
+str position
|
||
+str responsibility
|
||
+str extension
|
||
+str service_area
|
||
+str wecom_userid
|
||
+str avatar_url
|
||
+str business_category
|
||
+bool is_active
|
||
+datetime created_at
|
||
+datetime updated_at
|
||
}
|
||
|
||
class RoutingEvent {
|
||
+int id
|
||
+str conversation_id
|
||
+str employee_id
|
||
+str message_content
|
||
+str business_category
|
||
+float routing_confidence
|
||
+int contact_id
|
||
+str contact_name
|
||
+bool is_clicked
|
||
+datetime created_at
|
||
}
|
||
|
||
class RoutingService {
|
||
+ROUTING_PREFILTER_KEYWORDS: list~str~
|
||
+ROUTING_KEYWORD_TO_CATEGORY: dict
|
||
+async detect_routing_intent(text, employee_id) dict
|
||
+async get_contact_by_category(category) BusinessContact
|
||
+async send_contact_card(db, conversation, employee_id, contact, reason) None
|
||
+async record_routing_event(db, conversation_id, employee_id, content, category, confidence, contact) None
|
||
}
|
||
|
||
class RoutingAPI {
|
||
+GET /h5/routing/contact
|
||
+GET /routing/contacts
|
||
+POST /conversations/{id}/send-contact-card
|
||
}
|
||
|
||
class ApprovalDetectIntentResponse {
|
||
+bool is_approval_request
|
||
+float confidence
|
||
+str approval_type
|
||
+str source
|
||
+str intent_type
|
||
+str business_category
|
||
+float routing_confidence
|
||
}
|
||
|
||
BusinessContact "1" --> RoutingEvent : contact_id
|
||
RoutingService --> BusinessContact : 查询
|
||
RoutingService --> RoutingEvent : 记录
|
||
RoutingAPI --> RoutingService : 调用
|
||
```
|
||
|
||
### 3.2 数据库表结构
|
||
|
||
#### 表 `business_contacts`(P0)
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| `id` | Integer | PK, 自增 | 主键 |
|
||
| `name` | String(50) | NOT NULL | 联系人姓名 |
|
||
| `gender` | String(10) | NOT NULL, default 'male' | 性别(male/female) |
|
||
| `department` | String(100) | NOT NULL | 部门名称 |
|
||
| `position` | String(100) | NOT NULL | 岗位 |
|
||
| `responsibility` | String(500) | NOT NULL | 负责业务描述 |
|
||
| `extension` | String(20) | NULL | 分机号 |
|
||
| `service_area` | String(200) | NULL | 服务区域/办公地点 |
|
||
| `wecom_userid` | String(100) | NOT NULL | 企微用户ID |
|
||
| `avatar_url` | String(500) | NULL | 头像URL(为空用姓名首字渲染) |
|
||
| `business_category` | String(50) | NOT NULL | 业务类别(行政/人力资源/财务/法务/行政-物业) |
|
||
| `is_active` | Boolean | NOT NULL, default True | 是否启用 |
|
||
| `created_at` | DateTime | NOT NULL | 创建时间 |
|
||
| `updated_at` | DateTime | NOT NULL | 更新时间 |
|
||
|
||
**索引**: `idx_business_contacts_category` on (`business_category`, `is_active`)
|
||
|
||
#### 表 `routing_events`(P1 — 路由命中统计)
|
||
|
||
| 字段 | 类型 | 约束 | 说明 |
|
||
|------|------|------|------|
|
||
| `id` | Integer | PK, 自增 | 主键 |
|
||
| `conversation_id` | String(36) | NOT NULL, FK | 会话ID |
|
||
| `employee_id` | String(64) | NOT NULL | 员工ID |
|
||
| `message_content` | String(500) | NOT NULL | 触发路由的员工消息(截断) |
|
||
| `business_category` | String(50) | NOT NULL | 识别的业务类别 |
|
||
| `routing_confidence` | Float | NOT NULL | 路由置信度 |
|
||
| `contact_id` | Integer | FK | 推荐的联系人ID |
|
||
| `contact_name` | String(50) | NOT NULL | 联系人姓名(冗余) |
|
||
| `is_clicked` | Boolean | default False | 员工是否点击了「联系TA」 |
|
||
| `created_at` | DateTime | NOT NULL | 创建时间 |
|
||
|
||
**索引**: `idx_routing_events_category` on (`business_category`), `idx_routing_events_conv` on (`conversation_id`)
|
||
|
||
### 3.3 消息类型定义
|
||
|
||
#### 新增 `msg_type = 'contact_card'`
|
||
|
||
与现有 `approval_card` / `byod_card` 采用相同的渲染架构:后端通过 `msg_type` 区分,卡片数据通过 `extra_data` 透传。
|
||
|
||
**消息记录结构**(Message 表):
|
||
|
||
| 字段 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| `sender_type` | `'ai'` | 发送者为 AI |
|
||
| `sender_id` | `'ai_bot'` | AI 标识 |
|
||
| `sender_name` | `'Duckula(达寇拉)'` | 与现有 AI 消息一致 |
|
||
| `msg_type` | `'contact_card'` | 新增消息类型 |
|
||
| `content` | 路由说明文本 | 如"为您推荐行政服务联系人" |
|
||
| `extra_data` | JSON(见下) | 联系人完整信息 |
|
||
|
||
**`extra_data` 结构规范**:
|
||
|
||
```json
|
||
{
|
||
"contact": {
|
||
"id": 1,
|
||
"name": "王芳",
|
||
"gender": "female",
|
||
"department": "行政部",
|
||
"position": "设备管理岗",
|
||
"responsibility": "打印机/复印机/扫描仪",
|
||
"extension": "8002",
|
||
"service_area": "滨江园区 3-5楼",
|
||
"wecom_userid": "WangFang",
|
||
"avatar_url": "",
|
||
"business_category": "行政"
|
||
},
|
||
"routing_reason": "打印机问题属于行政设备范畴,不在IT服务台服务范围内,为您推荐行政服务联系人",
|
||
"business_category": "行政",
|
||
"routing_confidence": 0.85
|
||
}
|
||
```
|
||
|
||
### 3.4 API 接口定义
|
||
|
||
#### H5 端 — 查询联系人(P0)
|
||
|
||
```
|
||
GET /api/h5/routing/contact?business_category=行政
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"id": 1,
|
||
"name": "王芳",
|
||
"gender": "female",
|
||
"department": "行政部",
|
||
"position": "设备管理岗",
|
||
"responsibility": "打印机/复印机/扫描仪",
|
||
"extension": "8002",
|
||
"service_area": "滨江园区 3-5楼",
|
||
"wecom_userid": "WangFang",
|
||
"avatar_url": "",
|
||
"business_category": "行政"
|
||
}
|
||
}
|
||
```
|
||
|
||
**逻辑**:按 `business_category` + `is_active=true` 查询,取第一条有效联系人。
|
||
|
||
#### 坐席端 — 获取联系人列表(P1)
|
||
|
||
```
|
||
GET /api/routing/contacts?business_category=行政&keyword=王
|
||
```
|
||
|
||
**响应**:`{ "code": 0, "data": { "items": [BusinessContact, ...] } }`
|
||
|
||
#### 坐席端 — 手动发送名片(P1)
|
||
|
||
```
|
||
POST /api/conversations/{conversation_id}/send-contact-card
|
||
```
|
||
|
||
**请求体**:
|
||
```json
|
||
{ "contact_id": 1, "reason": "员工咨询报销问题,为您推荐财务联系人" }
|
||
```
|
||
|
||
**逻辑**:坐席手动选择联系人 → 后端创建 `contact_card` 消息 → WS 推送双通道。
|
||
|
||
#### 审批意图检测扩展(P0 — 向后兼容)
|
||
|
||
```
|
||
POST /api/approval/detect-intent
|
||
```
|
||
|
||
**响应(扩展后)**:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"is_approval_request": false,
|
||
"confidence": 0.15,
|
||
"approval_type": null,
|
||
"source": "dify",
|
||
"intent_type": "non_it_routing",
|
||
"business_category": "行政",
|
||
"routing_confidence": 0.85
|
||
}
|
||
}
|
||
```
|
||
|
||
> 原 4 个字段(`is_approval_request`/`confidence`/`approval_type`/`source`)语义不变,新增 3 个字段。前端现有审批逻辑只读取原字段,不受影响。
|
||
|
||
### 3.5 Dify 统一意图识别 Prompt 扩展
|
||
|
||
扩展后输出格式(原3字段不变,新增3字段):
|
||
|
||
```json
|
||
{
|
||
"is_approval_request": true,
|
||
"confidence": 0.95,
|
||
"approval_type": "设备申请",
|
||
"intent_type": "approval",
|
||
"business_category": null,
|
||
"routing_confidence": 0.0
|
||
}
|
||
```
|
||
|
||
**判断优先级链**(Prompt 内部逻辑):
|
||
|
||
```
|
||
1. IT审批意图 → intent_type: "approval", is_approval_request: true
|
||
2. IT范围内咨询/报修 → intent_type: "it_consult", is_approval_request: false
|
||
3. 非IT业务路由 → intent_type: "non_it_routing", business_category: "行政"/"人力资源"/...
|
||
4. 闲聊/无关 → intent_type: "chitchat"
|
||
```
|
||
|
||
> 完整 Prompt 见 `docs/02-产品需求/dify_unified_intent_prompt_v3.md`
|
||
|
||
---
|
||
|
||
## 4. 程序调用流程
|
||
|
||
### 4.1 核心时序图
|
||
|
||
> 完整时序图见 `docs/03-技术架构/业务路由推荐-时序图.mermaid`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant E as 员工(H5)
|
||
participant H5 as H5前端
|
||
participant BE as 后端API
|
||
participant TASK as 后台AI任务
|
||
participant DIFY as Dify API
|
||
participant DB as PostgreSQL
|
||
participant WS as WebSocket
|
||
participant AG as 坐席端
|
||
participant WECOM as 企微
|
||
|
||
E->>H5: 发送非IT消息 "打印机坏了"
|
||
H5->>BE: POST /h5/conversations/current/messages
|
||
BE->>DB: 存储员工消息
|
||
BE->>WS: broadcast(新消息→坐席)
|
||
BE-->>H5: 即时返回(user_message)
|
||
BE->>TASK: asyncio.create_task(process_h5_ai_reply)
|
||
|
||
Note over TASK: BYOD关键词检查(未命中)
|
||
|
||
TASK->>TASK: 路由关键词预过滤("打印机"命中)
|
||
TASK->>DIFY: 调用统一意图识别
|
||
DIFY-->>TASK: intent_type=non_it_routing, business_category=行政, routing_confidence=0.85
|
||
|
||
TASK->>DB: 查询business_contacts(行政)
|
||
DB-->>TASK: 返回联系人(王芳)
|
||
|
||
TASK->>DB: 存路由说明文本消息(ai, text)
|
||
TASK->>WS: broadcast_to_employees(ai_reply→H5)
|
||
TASK->>WS: broadcast(new_message→坐席)
|
||
|
||
TASK->>DB: 存contact_card消息(ai, contact_card)
|
||
TASK->>WS: broadcast_to_employees(ai_reply→H5, 含extra_data)
|
||
TASK->>WS: broadcast(new_message→坐席, 含extra_data)
|
||
|
||
TASK->>DB: 存系统提示消息(system)
|
||
TASK->>WS: broadcast_to_employees(ai_reply→H5)
|
||
TASK->>WS: broadcast(new_message→坐席)
|
||
|
||
TASK->>DB: 记录routing_event(P1)
|
||
|
||
WS-->>H5: ai_reply(路由文本)
|
||
H5->>E: 渲染AI文本气泡
|
||
WS-->>H5: ai_reply(contact_card)
|
||
H5->>E: 渲染ContactCard名片卡片
|
||
WS-->>H5: ai_reply(系统提示)
|
||
H5->>E: 渲染系统消息
|
||
|
||
WS-->>AG: new_message x3(路由文本+名片+系统提示)
|
||
AG->>AG: 渲染名片卡片(坐席端可见)
|
||
|
||
E->>H5: 点击「联系TA」
|
||
H5->>WECOM: wx.invoke('openEnterpriseChat', {userids: 'WangFang'})
|
||
WECOM-->>E: 打开企微单聊
|
||
|
||
Note over E,BE: 当前IT服务台会话不自动结束
|
||
```
|
||
|
||
### 4.2 路由检测在后台任务中的位置
|
||
|
||
```
|
||
process_h5_ai_reply(conversation_id, employee_id, content, dify_conversation_id)
|
||
│
|
||
├─ 1. BYOD 关键词拦截(现有,不变)
|
||
│ └─ 命中 → _handle_byod_query() → return
|
||
│
|
||
├─ 2. 【新增】路由关键词预过滤
|
||
│ └─ 命中非IT关键词 → _handle_routing()
|
||
│ ├─ 调用 Dify 统一意图识别
|
||
│ ├─ intent_type == "non_it_routing" && routing_confidence >= 0.7
|
||
│ │ └─ YES → 发送名片三段式消息 → return
|
||
│ │ └─ NO → 继续往下(走正常AI流程)
|
||
│ └─ Dify调用失败 → 关键词降级兜底(按关键词映射类别)
|
||
│
|
||
├─ 3. 打招呼/呼叫人工检测(现有,不变)
|
||
│ └─ 命中 → 同步回复 → return
|
||
│
|
||
└─ 4. Dify 流式回复(现有,不变)
|
||
└─ 流式推送 → _persist_and_push()
|
||
```
|
||
|
||
### 4.3 坐席手动发名片流程(P1)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant AG as 坐席端
|
||
participant BE as 后端API
|
||
participant DB as PostgreSQL
|
||
participant WS as WebSocket
|
||
participant H5 as H5前端
|
||
|
||
AG->>BE: POST /conversations/{id}/send-contact-card {contact_id, reason}
|
||
BE->>DB: 查询 BusinessContact by id
|
||
DB-->>BE: 返回联系人信息
|
||
BE->>DB: 创建 contact_card 消息(ai, extra_data=contact)
|
||
BE->>WS: broadcast_to_employees(ai_reply→H5)
|
||
BE->>WS: broadcast(new_message→坐席)
|
||
BE-->>AG: 返回成功
|
||
WS-->>H5: ai_reply(contact_card)
|
||
H5->>H5: 渲染名片卡片
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 待明确事项
|
||
|
||
| # | 问题 | 影响 | 假设/建议 |
|
||
|---|------|------|----------|
|
||
| 1 | 企微 `openEnterpriseChat` 的 `userids` 参数格式(企微账号ID vs 工号) | 数据模型 `wecom_userid` 字段取值 | **假设**:使用企微通讯录同步的 userid(与现有员工 employee_id 格式一致),需上线前核对 |
|
||
| 2 | 非IT业务类别是否还有其他部门需覆盖 | Prompt 设计 + 数据初始化 | 当前覆盖 5 类(行政/人力资源/财务/法务/行政-物业),建议上线后根据路由统计补充 |
|
||
| 3 | 一个业务类别多个联系人时的选择策略 | 后端查询逻辑 | P0 取第一个 `is_active=true` 的联系人;P2 支持按服务区域匹配 |
|
||
| 4 | Dify Prompt 修改后是否需要 A/B 测试 | 上线策略 | 建议灰度上线,监控审批识别准确率无下降后全量;先用 PRD 5.5 回归测试用例集验证 |
|
||
| 5 | 坐席手动发名片的触发入口位置 | 坐席端 UI | **假设**:在 InputBox 工具栏新增「发名片」按钮,弹出联系人选择面板 |
|
||
| 6 | routing_confidence 在 0.5~0.7 之间是否推荐 | 路由触发策略 | 已确认:**不推荐**,走正常 AI 回复流程 |
|
||
|
||
---
|
||
|
||
# Part B: 任务分解
|
||
|
||
## 6. 依赖包列表
|
||
|
||
**无需新增第三方依赖包**。所有功能基于现有技术栈实现:
|
||
|
||
| 已有依赖 | 用途 |
|
||
|---------|------|
|
||
| `fastapi` | API 路由 |
|
||
| `sqlalchemy` | ORM 模型 |
|
||
| `alembic` | 数据库迁移 |
|
||
| `httpx` | Dify API 调用(已在 approval.py 中使用) |
|
||
| `pydantic` | Schema 定义 |
|
||
| `vant@^4` | H5 名片卡片 UI 组件 |
|
||
| `element-plus` | 坐席端名片卡片 UI 组件 |
|
||
|
||
---
|
||
|
||
## 7. 任务列表
|
||
|
||
### T01: 后端数据层 + 配置 + Prompt 基础设施
|
||
|
||
| 项 | 内容 |
|
||
|----|------|
|
||
| **Task ID** | T01 |
|
||
| **Task Name** | 后端数据层 + 配置基础设施 |
|
||
| **Priority** | P0 |
|
||
| **Dependencies** | 无 |
|
||
|
||
**源文件**:
|
||
- `backend/app/models/business_contact.py`(新建)— BusinessContact 模型
|
||
- `backend/app/models/routing_event.py`(新建)— RoutingEvent 模型(P1)
|
||
- `backend/app/models/__init__.py`(修改)— 注册新模型
|
||
- `backend/alembic/versions/047_business_contacts.py`(新建)— 建表 + 初始数据迁移
|
||
- `backend/app/config.py`(修改)— 新增 `routing_confidence_threshold` 配置
|
||
- `backend/app/api/router.py`(修改)— 注册 routing 路由器(占位,路由实现在 T02)
|
||
- `docs/02-产品需求/dify_unified_intent_prompt_v3.md`(新建)— 扩展后的 Dify Prompt
|
||
|
||
**验收标准**:
|
||
- BusinessContact / RoutingEvent 模型可被 Alembic 检测到
|
||
- `alembic upgrade head` 成功建表并写入初始联系人数据
|
||
- `routing_confidence_threshold` 配置项默认值 0.7
|
||
- Dify Prompt 文档包含完整的扩展后 System Prompt(原审批规则 + 新增非IT路由判断段落)
|
||
|
||
---
|
||
|
||
### T02: 后端路由服务 + API + 后台任务集成
|
||
|
||
| 项 | 内容 |
|
||
|----|------|
|
||
| **Task ID** | T02 |
|
||
| **Task Name** | 后端路由服务与消息发送 |
|
||
| **Priority** | P0 |
|
||
| **Dependencies** | T01 |
|
||
|
||
**源文件**:
|
||
- `backend/app/services/routing_service.py`(新建)— 核心路由逻辑:关键词预过滤、Dify调用、联系人查询、名片三段式发送、路由事件记录
|
||
- `backend/app/api/routing.py`(新建)— API 端点:`GET /h5/routing/contact`、`GET /routing/contacts`、`POST /conversations/{id}/send-contact-card`
|
||
- `backend/app/api/approval.py`(修改)— `_call_dify_approval_intent()` 解析扩展字段;`ApprovalDetectIntentResponse` 新增 `intent_type`/`business_category`/`routing_confidence`
|
||
- `backend/app/tasks/h5_ai_task.py`(修改)— 在 BYOD 检测后新增 `_handle_routing()` 调用步骤
|
||
|
||
**验收标准**:
|
||
- 员工发送"打印机坏了" → 后台任务检测到 non_it_routing → 发送路由文本 + contact_card + 系统提示三条消息
|
||
- 员工发送"我要申请笔记本电脑" → 审批意图正常识别(回归无影响)
|
||
- `routing_confidence < 0.7` 时不触发名片推荐,走正常 AI 流程
|
||
- Dify 调用失败时降级为关键词匹配(按 `ROUTING_KEYWORD_TO_CATEGORY` 映射)
|
||
- 坐席端通过 `POST /conversations/{id}/send-contact-card` 可手动发送名片
|
||
- WS 双通道推送正常(H5 收到 `ai_reply`,坐席收到 `new_message`)
|
||
|
||
---
|
||
|
||
### T03: H5 前端名片卡片组件 + 消息映射
|
||
|
||
| 项 | 内容 |
|
||
|----|------|
|
||
| **Task ID** | T03 |
|
||
| **Task Name** | H5 员工名片卡片与渲染 |
|
||
| **Priority** | P0 |
|
||
| **Dependencies** | T02 |
|
||
|
||
**源文件**:
|
||
- `frontend-h5/src/components/chat/ContactCard.vue`(新建)— 名片卡片组件:头像/姓名/部门岗位/负责业务(绿色高亮)/分机号/服务区域/「联系TA」按钮
|
||
- `frontend-h5/src/components/chat/MessageBubble.vue`(修改)— 新增 `v-else-if="msg.msg_type === 'contact_card'"` 渲染分支
|
||
- `frontend-h5/src/api/conversation.ts`(修改)— `MsgContentType` 新增 `'contact_card'`
|
||
- `frontend-h5/src/api/routing.ts`(新建)— `getContactByCategory()` API 封装
|
||
|
||
**验收标准**:
|
||
- 名片卡片按 PRD 4.1 布局渲染:头像44px、姓名加粗、负责业务绿色高亮 #07C160、「联系TA」绿色按钮
|
||
- 点击「联系TA」→ 调用 `wx.invoke('openEnterpriseChat', {userids: contact.wecom_userid})` 打开企微单聊
|
||
- userid 为空或企微 SDK 未就绪 → Toast 提示「暂时无法发起聊天,请联系管理员」
|
||
- `mapMessage()` 无需改动(`msg_type` 和 `extra_data` 已透传),仅类型定义扩展
|
||
- 历史消息中 contact_card 消息可正常渲染(从 `extra_data.contact` 读取数据)
|
||
|
||
---
|
||
|
||
### T04: 坐席端名片展示 + 手动发名片功能
|
||
|
||
| 项 | 内容 |
|
||
|----|------|
|
||
| **Task ID** | T04 |
|
||
| **Task Name** | 坐席端名片可见性与手动发送 |
|
||
| **Priority** | P1 |
|
||
| **Dependencies** | T02 |
|
||
|
||
**源文件**:
|
||
- `frontend-agent/src/components/chat/ContactCardInline.vue`(新建)— 坐席端名片卡片(Element Plus 风格,展示联系人信息 + 推荐原因,无「联系TA」按钮)
|
||
- `frontend-agent/src/components/chat/MessageBubble.vue`(修改)— 新增 `contact_card` 渲染分支
|
||
- `frontend-agent/src/api/routing.ts`(新建)— `getContacts()`、`sendContactCard()` API 封装
|
||
- `frontend-agent/src/components/chat/InputBox.vue`(修改)— 工具栏新增「发名片」按钮,弹出联系人选择面板
|
||
|
||
**验收标准**:
|
||
- AI 发送的路由名片在坐席端消息流中可见,展示联系人信息 + 推荐原因
|
||
- 坐席点击「发名片」→ 弹出联系人选择面板(按业务类别分组)→ 选择联系人 → 发送到当前会话
|
||
- 坐席端名片卡片不显示「联系TA」按钮(坐席无需跳转企微单聊)
|
||
- 名片消息在坐席端和 H5 端同时可见(双通道验证)
|
||
|
||
---
|
||
|
||
## 8. 共享知识
|
||
|
||
### 8.1 消息类型命名规范
|
||
|
||
```
|
||
msg_type 取值统一使用 snake_case:
|
||
text / image / file / voice / video / location
|
||
system / approval_card / byod_card / contact_card(新增)
|
||
```
|
||
|
||
### 8.2 extra_data 结构规范
|
||
|
||
所有卡片类消息的 `extra_data` 采用 **命名空间嵌套** 结构:
|
||
|
||
| msg_type | extra_data 结构 |
|
||
|----------|----------------|
|
||
| `approval_card` | `{ "approval_type": "设备申请" }` |
|
||
| `byod_card` | `{ "byod_result": { eligible, has_subsidy, ... } }` |
|
||
| `contact_card`(新增) | `{ "contact": { name, department, ... }, "routing_reason": "...", "business_category": "...", "routing_confidence": 0.85 }` |
|
||
|
||
### 8.3 WS 推送事件规范
|
||
|
||
路由名片消息复用现有 WS 事件类型:
|
||
|
||
| 推送目标 | 事件类型 | data 关键字段 |
|
||
|---------|---------|--------------|
|
||
| H5 员工 | `ai_reply` | `message_id`, `msg_type: "contact_card"`, `extra_data`, `sender_type: "ai"` |
|
||
| 坐席端 | `new_message` | `message_id`, `msg_type: "contact_card"`, `extra_data`, `sender_type: "ai"` |
|
||
|
||
> H5 前端收到 `ai_reply` 事件时,根据 `msg_type` 决定渲染方式(文本气泡 / 名片卡片),与现有 BYOD 处理逻辑一致。
|
||
|
||
### 8.4 Dify 统一意图识别响应字段
|
||
|
||
| 字段 | 类型 | 取值范围 | 说明 |
|
||
|------|------|---------|------|
|
||
| `is_approval_request` | bool | true/false | **原字段**,是否为审批请求(语义不变) |
|
||
| `confidence` | float | 0.0~1.0 | **原字段**,审批置信度(语义不变) |
|
||
| `approval_type` | string\|null | 12种类型\|null | **原字段**,审批类型(语义不变) |
|
||
| `intent_type` | string | approval/it_consult/non_it_routing/chitchat | **新增**,意图大类 |
|
||
| `business_category` | string\|null | 行政/人力资源/财务/法务/行政-物业\|null | **新增**,非IT业务类别 |
|
||
| `routing_confidence` | float | 0.0~1.0 | **新增**,路由置信度,≥0.7 触发名片推荐 |
|
||
|
||
### 8.5 路由关键词预过滤列表
|
||
|
||
```python
|
||
ROUTING_PREFILTER_KEYWORDS = [
|
||
# 行政
|
||
"打印机", "复印机", "扫描仪", "保洁", "名片印刷",
|
||
# 人力资源
|
||
"工牌", "考勤", "入职", "离职", "社保", "公积金",
|
||
# 财务
|
||
"报销", "发票", "借款", "工资条",
|
||
# 法务
|
||
"合同", "法务", "知识产权",
|
||
# 行政-物业
|
||
"空调", "电梯", "门禁", "停车",
|
||
]
|
||
|
||
ROUTING_KEYWORD_TO_CATEGORY = {
|
||
"打印机": "行政", "复印机": "行政", "扫描仪": "行政", "保洁": "行政", "名片印刷": "行政",
|
||
"工牌": "人力资源", "考勤": "人力资源", "入职": "人力资源", "离职": "人力资源",
|
||
"社保": "人力资源", "公积金": "人力资源",
|
||
"报销": "财务", "发票": "财务", "借款": "财务", "工资条": "财务",
|
||
"合同": "法务", "法务": "法务", "知识产权": "法务",
|
||
"空调": "行政-物业", "电梯": "行政-物业", "门禁": "行政-物业", "停车": "行政-物业",
|
||
}
|
||
```
|
||
|
||
### 8.6 跳转后会话不自动结束
|
||
|
||
员工点击「联系TA」跳转企微单聊后,当前 IT 服务台会话状态保持不变(`ai_handling`/`serving`),员工可返回继续咨询。不在后端触发任何会话状态变更。
|
||
|
||
### 8.7 向后兼容性保证
|
||
|
||
1. **Dify Prompt 扩展**:原审批判断规则原文保留,仅新增非IT判断段落
|
||
2. **API 响应扩展**:`/approval/detect-intent` 响应新增字段,原字段语义不变;前端现有审批逻辑只读取原字段
|
||
3. **消息类型扩展**:`contact_card` 是新增 `msg_type`,不影响现有 `text`/`image`/`approval_card`/`byod_card` 的渲染
|
||
4. **数据库扩展**:新建表,不修改现有表结构
|
||
|
||
---
|
||
|
||
## 9. 任务依赖图
|
||
|
||
```mermaid
|
||
graph LR
|
||
T01[T01: 后端数据层+配置+Prompt]
|
||
T02[T02: 后端路由服务+API+任务集成]
|
||
T03[T03: H5名片卡片+消息映射]
|
||
T04[T04: 坐席端名片+手动发名片]
|
||
|
||
T01 --> T02
|
||
T02 --> T03
|
||
T02 --> T04
|
||
|
||
style T01 fill:#07C160,color:#fff
|
||
style T02 fill:#07C160,color:#fff
|
||
style T03 fill:#1989fa,color:#fff
|
||
style T04 fill:#ff976a,color:#fff
|
||
```
|
||
|
||
**依赖说明**:
|
||
- T01 → T02:路由服务依赖数据模型和配置
|
||
- T02 → T03:H5 前端依赖后端 API 契约和 WS 推送格式
|
||
- T02 → T04:坐席端依赖后端 API 契约
|
||
- T03 和 T04 互不依赖,可并行开发
|
||
|
||
---
|
||
|
||
> **附**:时序图 `docs/03-技术架构/业务路由推荐-时序图.mermaid`
|
||
> **附**:类图 `docs/03-技术架构/业务路由推荐-类图.mermaid`
|