Files
wecom_it_smart_desk/docs/03-技术架构/增量设计-AI辅助消息框-20260711.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

1048 lines
48 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.0
> **创建日期**2026-07-11
> **架构师**:高见远
> **关联 PRD**`docs/02-产品需求/坐席端AI辅助消息框-PRD.md`
> **技术栈**:后端 FastAPI + Python / 前端 Vue3 + Element Plus + TypeScript
> **部署方式**Docker Composebind mount `./app:/app/app`
---
## 目录
- [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 核心技术挑战
| 挑战 | 难点 | 解决方案 |
|------|------|----------|
| **幽灵文字内联定位** | `<textarea>` 无法直接在光标处叠加文字,需精确计算光标像素坐标 | 创建隐藏 `<div>` 镜像 textarea 的样式(字体、行高、padding、换行),通过镜像 div 截取光标前文本计算像素偏移,在 textarea 同级覆盖一层透明 `<span>` 渲染幽灵文字 |
| **补齐请求生命周期管理** | 用户连续输入时需取消旧请求、仅保留最新结果 | 前端使用 `AbortController` 管理请求;每次新输入触发时 abort 上一个 controller,创建新的 |
| **多温度 Dify 调用** | 现有 `_call_wingman_api()` 的 temperature 硬编码为 0.3,4 个新功能需要不同温度 | 改造 `_call_wingman_api()` 增加 `temperature` 参数(默认 0.3 保持向后兼容),各方法按需传入 |
| **补齐结果 Redis 缓存** | 同一对话中重复输入相同前缀时避免重复调用 Dify | 在 `generate_completion()` 中先查 Rediskey=`wingman:autocomplete:{hash(text+conv_id)}`,TTL=30s),命中则直接返回;未命中调用 Dify 后写入缓存 |
| **改写功能知识库集成** | 版本 3 需引用知识库文档,需检索 RAGFlow 并注入 prompt | `rewrite_versions()` 调用 `build_ragflow_client()` 检索相关知识片段,拼入 Dify system promptRAGFlow 不可用时降级为仅生成 2 个版本 |
| **AI 不可用降级** | 所有 AI 功能不可用时不能影响坐席正常输入和发送 | 后端方法 catch 所有异常返回空结果/默认值(与现有 `generate_draft` 范式一致);前端 catch 请求错误后静默忽略,不弹错误提示 |
#### 1.2 框架与库选型
| 层 | 选型 | 理由 |
|----|------|------|
| 后端 API | FastAPI + Pydantic | 现有项目已使用,Pydantic 提供请求体验证 |
| 后端 AI 调用 | httpx AsyncClient | 复用 WingmanService 现有 httpx 连接池 |
| 后端缓存 | redis.asyncio (aioredis) | 复用 `settings.create_redis_client()`,与 CacheService 同一 Redis 实例 |
| 后端知识检索 | RAGFlow Client (`app.integrations.ragflow`) | 复用现有 RagflowClient,通过 `build_ragflow_client()` 获取 |
| 前端框架 | Vue3 Composition API + `<script setup lang="ts">` | 与 ReplyBox.vue 现有范式一致 |
| 前端 UI | Element Plus (el-popover, el-button, el-tooltip) | 现有项目已引入,浮层/面板组件直接复用 |
| 前端 HTTP | Axios + AbortController | 复用 `apiClient` 实例,AbortController 管理请求取消 |
| 前端防抖 | 自实现 debounce(或 lodash-es debounce | 补齐 800ms debounce;项目已有 lodash-es 依赖 |
#### 1.3 整体架构
```
┌──────────────────────────────────────────────────────────────────────┐
│ 坐席工作台 (Vue3 + Element Plus) │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ ReplyBox.vue (改造) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ AiAssistToolbar.vue (新建) │ │ │
│ │ │ [补齐ON/OFF] [语气] [润色] [改写] │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ textarea + GhostText.vue (新建, 幽灵文字覆盖层) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ ToneAdjustPopover.vue / PolishPanel.vue / RewritePanel.vue│ │ │
│ │ │ (按需弹出, 浮层/面板形式) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ useAiAssist.ts (composable, 状态管理 + 请求调度) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ HTTP (Axios + AbortController) │
└─────────────────────────┼────────────────────────────────────────────┘
┌─────────────────────────▼────────────────────────────────────────────┐
│ FastAPI 后端 │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ wingman.py (路由层, 新增 4 个端点) │ │
│ │ POST /conversations/{id}/wingman/autocomplete ← 自动补齐 │ │
│ │ POST /conversations/{id}/wingman/tone-adjust ← 语气调整 │ │
│ │ POST /conversations/{id}/wingman/polish ← 文字润色 │ │
│ │ POST /conversations/{id}/wingman/rewrite ← 智能改写 │ │
│ │ + 现有: draft / summary / tags │ │
│ └───────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────▼────────────────────────────────────┐ │
│ │ WingmanService (服务层, 新增 4 个方法 + 改造底层调用) │ │
│ │ generate_completion() temperature=0.2 + Redis 缓存 │ │
│ │ adjust_tone() temperature=0.3 │ │
│ │ polish_text() temperature=0.3 │ │
│ │ rewrite_versions() temperature=0.6 + RAGFlow 检索 │ │
│ │ + 现有: generate_draft / generate_summary / suggest_tags │ │
│ │ │ │
│ │ _call_wingman_api() ← 改造: 新增 temperature 参数 │ │
│ │ _build_context_messages() ← 复用: 角色映射 │ │
│ └──────┬──────────────────────┬───────────────────────────────────┘ │
│ │ httpx │ redis.asyncio │
└─────────┼───────────────────────┼─────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌──────────────────┐
│ Dify AI (外部) │ │ Redis 缓存 │
│ Wingman Agent │ │ key: wingman: │
│ /chat/completions │ │ autocomplete: │
│ (OpenAI 兼容格式) │ │ {hash} TTL=30s │
└─────────────────────┘ └──────────────────┘
▼ (改写功能版本3)
┌─────────────────────┐
│ RAGFlow 知识检索 │
│ /api/v1/retrieval │
│ (build_ragflow_ │
│ client() 获取) │
└─────────────────────┘
```
#### 1.4 与现有系统的关系
| 现有组件 | 关系 | 说明 |
|----------|------|------|
| `WingmanService` | **扩展** | 新增 4 个方法,改造 `_call_wingman_api()` 支持 temperature 参数 |
| `wingman.py` 路由 | **扩展** | 新增 4 个端点 + 引入 Pydantic 请求模型 |
| `schemas/` 目录 | **新增** | 新增 `wingman_assist.py` 定义 4 个请求 Schema |
| `dependencies/__init__.py` | **修改** | `dep_wingman_service` 注入 Redis 客户端 |
| `wingman.ts` API 层 | **扩展** | 新增 4 个前端 API 函数 + AbortController 支持 |
| `ReplyBox.vue` | **改造** | 集成 AiAssistToolbar + GhostText + 三个面板组件 |
| 现有 `generate_draft` 等 | **不受影响** | `_call_wingman_api` 改造向后兼容(默认 temperature=0.3 |
---
### 2. 文件列表
| # | 文件路径 | 操作 | 说明 |
|---|---------|------|------|
| 1 | `backend/app/services/wingman_service.py` | **修改** | 新增 4 个方法 + 4 个 system prompt + 改造 `_call_wingman_api` |
| 2 | `backend/app/api/wingman.py` | **修改** | 新增 4 个 API 端点 + 引入 Schema 依赖 |
| 3 | `backend/app/schemas/wingman_assist.py` | **新建** | 4 个请求体 Pydantic 模型 |
| 4 | `backend/app/dependencies/__init__.py` | **修改** | `dep_wingman_service` 注入 Redis 客户端 |
| 5 | `frontend-agent/src/api/wingman.ts` | **修改** | 新增 4 个 API 函数 + 4 个响应类型 + AbortController |
| 6 | `frontend-agent/src/composables/useAiAssist.ts` | **新建** | AI 辅助状态管理 + 请求调度 composable |
| 7 | `frontend-agent/src/components/chat/ai-assist/GhostText.vue` | **新建** | 幽灵文字渲染层(光标定位 + Tab 接受) |
| 8 | `frontend-agent/src/components/chat/ai-assist/AiAssistToolbar.vue` | **新建** | AI 辅助工具栏(补齐开关 + 语气/润色/改写按钮) |
| 9 | `frontend-agent/src/components/chat/ai-assist/ToneAdjustPopover.vue` | **新建** | 语气选择浮层(专业/友好/简洁 + 原文/改写对比) |
| 10 | `frontend-agent/src/components/chat/ai-assist/PolishPanel.vue` | **新建** | 润色精修面板(扩写/压缩/纠错 + 左右对比) |
| 11 | `frontend-agent/src/components/chat/ai-assist/RewritePanel.vue` | **新建** | 改写版本选择面板(3 个版本 + 替换/追加) |
| 12 | `frontend-agent/src/components/chat/ReplyBox.vue` | **修改** | 集成 AI 工具栏 + GhostText + 三个面板组件 |
---
### 3. 数据结构和接口
#### 3.1 后端 Pydantic 请求 Schema
```python
# backend/app/schemas/wingman_assist.py
from pydantic import BaseModel, Field
from typing import List, Optional
class AutocompleteRequest(BaseModel):
"""自动补齐请求"""
current_text: str = Field(..., min_length=1, max_length=500, description="当前输入文本")
cursor_position: int = Field(0, ge=0, description="光标位置")
max_length: int = Field(80, ge=10, le=200, description="补齐最大长度")
class ToneAdjustRequest(BaseModel):
"""语气调整请求"""
selected_text: str = Field(..., min_length=1, max_length=2000, description="选中的文字")
full_text: str = Field("", max_length=5000, description="输入框完整内容")
tone: str = Field(..., pattern="^(professional|friendly|concise)$", description="目标语气")
class PolishRequest(BaseModel):
"""文字润色请求"""
text: str = Field(..., min_length=1, max_length=5000, description="待润色文字")
action: str = Field(..., pattern="^(expand|compress|correct)$", description="润色操作")
conversation_context: bool = Field(True, description="是否携带对话上下文")
class RewriteRequest(BaseModel):
"""智能改写请求"""
current_text: str = Field("", max_length=5000, description="当前输入文本(可为空)")
generate_count: int = Field(3, ge=1, le=5, description="生成版本数")
include_knowledge: bool = Field(True, description="是否包含知识库引用版本")
```
#### 3.2 后端 WingmanService 新增方法签名
```python
class WingmanService:
# === 现有方法(保持不变) ===
# generate_draft() / generate_summary() / suggest_tags() / generate_knowledge_suggestion()
# === 新增 System Prompt 常量 ===
_COMPLETION_SYSTEM_PROMPT: str # 自动补齐
_TONE_ADJUST_SYSTEM_PROMPT: str # 语气调整
_POLISH_SYSTEM_PROMPT: str # 文字润色
_REWRITE_SYSTEM_PROMPT: str # 智能改写
# === 新增方法 ===
async def generate_completion(
self,
conversation_id: str,
current_text: str,
messages: List[Dict[str, Any]],
max_length: int = 80,
) -> Dict[str, Any]:
"""自动补齐:temperature=0.2Redis 缓存 TTL=30s"""
async def adjust_tone(
self,
conversation_id: str,
selected_text: str,
full_text: str,
tone: str,
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""语气调整:temperature=0.3"""
async def polish_text(
self,
conversation_id: str,
text: str,
action: str,
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""文字润色:temperature=0.3"""
async def rewrite_versions(
self,
conversation_id: str,
current_text: str,
messages: List[Dict[str, Any]],
generate_count: int = 3,
include_knowledge: bool = True,
) -> Dict[str, Any]:
"""智能改写:temperature=0.6,可选 RAGFlow 知识检索"""
# === 改造方法 ===
async def _call_wingman_api(
self,
context_messages: List[Dict[str, str]],
temperature: float = 0.3, # ← 新增参数,默认值保持兼容
) -> Optional[str]:
"""调用 Dify API(改造:支持 temperature 参数)"""
# === 新增内部方法 ===
async def _get_cache(self, key: str) -> Optional[Dict[str, Any]]:
"""读取 Redis 缓存"""
async def _set_cache(self, key: str, value: Dict[str, Any], ttl: int = 30) -> None:
"""写入 Redis 缓存"""
def _make_cache_key(self, text: str, conversation_id: str) -> str:
"""生成缓存 key: wingman:autocomplete:{md5(text+conv_id)}"""
async def _search_knowledge(self, query: str) -> Optional[str]:
"""调用 RAGFlow 检索知识(降级返回 None)"""
```
#### 3.3 前端 TypeScript 类型定义
```typescript
// frontend-agent/src/api/wingman.ts (新增部分)
/** 自动补齐响应 */
export interface AutocompleteResult {
completion: string
confidence: number
}
/** 语气类型 */
export type ToneType = 'professional' | 'friendly' | 'concise'
/** 语气调整响应 */
export interface ToneAdjustResult {
rewritten_text: string
tone: ToneType
changes_summary: string
}
/** 润色操作类型 */
export type PolishAction = 'expand' | 'compress' | 'correct'
/** 文字润色响应 */
export interface PolishResult {
polished_text: string
action: PolishAction
changes_summary: string
}
/** 改写版本 */
export interface RewriteVersion {
text: string
style: string
source: string
}
/** 智能改写响应 */
export interface RewriteResult {
versions: RewriteVersion[]
}
```
#### 3.4 前端 useAiAssist Composable 接口
```typescript
// frontend-agent/src/composables/useAiAssist.ts
export interface UseAiAssistOptions {
/** 获取当前会话 ID */
getConversationId: () => string | undefined
/** 获取输入框文本 */
getInputText: () => string
/** 设置输入框文本 */
setInputText: (text: string) => void
/** 获取 textarea DOM 引用(用于光标定位) */
getTextareaRef: () => HTMLTextAreaElement | null
}
export function useAiAssist(options: UseAiAssistOptions) {
// === 自动补齐 ===
const ghostText: Ref<string> // 当前幽灵文字
const isCompletLoading: Ref<boolean> // 补齐加载中
const autocompleteEnabled: Ref<boolean> // 补齐开关
function triggerAutocomplete(): void // 触发补齐(debounce 800ms
function acceptGhostText(): void // Tab 接受补齐
function clearGhostText(): void // 清除补齐(Esc 或继续输入)
// === 语气调整 ===
const tonePopoverVisible: Ref<boolean>
const toneLoading: Ref<boolean>
const toneResult: Ref<ToneAdjustResult | null>
async function adjustTone(selectedText: string, fullText: string, tone: ToneType): Promise<void>
// === 文字润色 ===
const polishPanelVisible: Ref<boolean>
const polishLoading: Ref<boolean>
const polishResult: Ref<PolishResult | null>
async function polishText(action: PolishAction): Promise<void>
// === 智能改写 ===
const rewritePanelVisible: Ref<boolean>
const rewriteLoading: Ref<boolean>
const rewriteResult: Ref<RewriteResult | null>
async function rewriteVersions(): Promise<void>
// === 生命周期 ===
function cleanup(): void // 组件卸载时调用,abort 所有请求
return {
ghostText, isCompletLoading, autocompleteEnabled,
triggerAutocomplete, acceptGhostText, clearGhostText,
tonePopoverVisible, toneLoading, toneResult, adjustTone,
polishPanelVisible, polishLoading, polishResult, polishText,
rewritePanelVisible, rewriteLoading, rewriteResult, rewriteVersions,
cleanup,
}
}
```
#### 3.5 类图
```mermaid
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 : 弹出面板
```
> **类图文件**`docs/03-技术架构/05-架构图/ai-assist-class-diagram.mermaid`
---
### 4. 程序调用流程
#### 4.1 实时自动补齐时序图
```mermaid
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 语气调整时序图
```mermaid
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 文字润色时序图
```mermaid
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 智能改写时序图
```mermaid
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
```
> **时序图文件**`docs/03-技术架构/05-架构图/ai-assist-sequence-diagram.mermaid`
---
### 5. 待明确事项
| # | 待明确问题 | 当前假设 | 影响范围 |
|---|-----------|---------|---------|
| 1 | **补齐建议条的过渡方案**PRD §2.1.3 提到可先采用"输入框下方建议条"过渡,但当前需求确认为内联幽灵文字。是否坚持内联方案? | 假设坚持内联幽灵文字方案(GhostText.vue 通过隐藏 div 镜像计算光标位置) | GhostText.vue 实现复杂度 |
| 2 | **补齐的置信度阈值**:PRD §9 风险表提到"低置信度不显示",但未定义具体阈值 | 假设 confidence < 0.5 时不显示幽灵文字 | useAiAssist.ts `triggerAutocomplete` 逻辑 |
| 3 | **Shift+Tab 接受一个词**PRD §2.1.2 提到 Shift+Tab 接受一个词,"词"的界定标准? | 假设按空格/标点分词,接受到下一个空格或标点 | GhostText.vue / useAiAssist.ts 键盘处理 |
| 4 | **改写功能 RAGFlow dataset_ids**`rewrite_versions()` 需要指定知识库 ID,当前默认使用哪个? | 假设使用系统默认知识库(通过配置或 RAGFlow 默认 dataset),需确认 | WingmanService._search_knowledge() |
| 5 | **语气/润色/改写是否记录日志**PRD §6 提到"语气/润色/改写可记录用于后续优化",记录到哪个表? | 假设首版不记录,后续迭代增加 | 无额外文件(首版不实现) |
| 6 | **补齐 API 独立超时**PRD §4.1.3 提到补齐 API 超时 3s,但 §2.1.2 提到 1.5s 超时不显示。最终以后端超时还是前端超时为准? | 假设前端 AbortController 超时 1.5s(用户感知优先),后端 httpx 超时 3s(兜底) | wingman.ts timeout / wingman_service.py timeout |
| 7 | **AI 工具栏按钮位置**:PRD §3.1 显示 AI 按钮在现有按钮右侧、发送按钮左侧。是否与布局优化任务的工具栏重构冲突? | 假设 AI 按钮独立分组(用分隔线隔开),不与布局优化任务的工具栏重构冲突 | ReplyBox.vue 模板 |
---
## Part B: 任务分解
### 6. 依赖包列表
#### 后端(Python / pip
| 包名 | 版本 | 用途 | 是否新增 |
|------|------|------|---------|
| `httpx` | 现有 | 异步 HTTP 客户端(调用 Dify API | ❌ 已有 |
| `redis[asyncio]` | 现有 | Redis 异步客户端(补齐结果缓存) | ❌ 已有 |
| `pydantic` | 现有 | 请求体 Schema 验证 | ❌ 已有 |
> **后端无新增依赖包**。所有功能复用现有 httpx + redis + pydantic + RAGFlow Client。
#### 前端(npm
| 包名 | 版本 | 用途 | 是否新增 |
|------|------|------|---------|
| `axios` | 现有 | HTTP 请求(复用 apiClient 实例) | ❌ 已有 |
| `element-plus` | 现有 | UI 组件(el-popover, el-button, el-tooltip | ❌ 已有 |
| `lodash-es` | 现有 | debounce 函数(补齐 800ms 防抖) | ❌ 已有 |
| `@vueuse/core` | 现有 | useDebounceFn 或 watchDebounced(如已引入) | ❌ 已有 |
> **前端无新增依赖包**。所有功能复用现有 Vue3 + Element Plus + lodash-es + axios。
---
### 7. 任务列表
| 任务 ID | 任务名称 | 源文件 | 依赖 | 优先级 | 预估工时 |
|---------|---------|--------|------|--------|---------|
| **T01** | **后端服务层与 API 端点** | `backend/app/services/wingman_service.py`(修改)<br>`backend/app/api/wingman.py`(修改)<br>`backend/app/schemas/wingman_assist.py`(新建)<br>`backend/app/dependencies/__init__.py`(修改) | 无 | P0 | 2 天 |
| **T02** | **前端 API 封装与状态管理** | `frontend-agent/src/api/wingman.ts`(修改)<br>`frontend-agent/src/composables/useAiAssist.ts`(新建)<br>`frontend-agent/src/types/ai-assist.d.ts`(新建) | T01 | P0 | 1 天 |
| **T03** | **AI 辅助 UI 组件(补齐 + 语气 + 工具栏)** | `frontend-agent/src/components/chat/ai-assist/GhostText.vue`(新建)<br>`frontend-agent/src/components/chat/ai-assist/ToneAdjustPopover.vue`(新建)<br>`frontend-agent/src/components/chat/ai-assist/AiAssistToolbar.vue`(新建) | T01, T02 | P1 | 2 天 |
| **T04** | **AI 辅助 UI 组件(润色 + 改写)+ ReplyBox 集成** | `frontend-agent/src/components/chat/ai-assist/PolishPanel.vue`(新建)<br>`frontend-agent/src/components/chat/ai-assist/RewritePanel.vue`(新建)<br>`frontend-agent/src/components/chat/ReplyBox.vue`(修改) | T01, T02, T03 | P1 | 2 天 |
#### 任务详细说明
**T01: 后端服务层与 API 端点**
1.`schemas/wingman_assist.py` 中定义 4 个 Pydantic 请求模型(AutocompleteRequest / ToneAdjustRequest / PolishRequest / RewriteRequest
2.`wingman_service.py` 中:
- 新增 4 个 system prompt 常量
- 改造 `_call_wingman_api()` 增加 `temperature` 参数(默认 0.3
- 新增 `generate_completion()` — 含 Redis 缓存读写
- 新增 `adjust_tone()` — 按 tone 动态构建 prompt
- 新增 `polish_text()` — 按 action 动态构建 prompt
- 新增 `rewrite_versions()` — 含 RAGFlow 知识检索 + 版本分割
- 新增 `_get_cache()` / `_set_cache()` / `_make_cache_key()` / `_search_knowledge()` 内部方法
- `__init__` 增加 `redis_client` 可选参数
3.`wingman.py` 中新增 4 个 API 端点,复用 `_validate_conversation` + `_get_recent_messages`
4.`dependencies/__init__.py` 中修改 `dep_wingman_service` 注入 Redis 客户端
**T02: 前端 API 封装与状态管理**
1.`wingman.ts` 中新增 4 个 API 函数(autocomplete / adjustTone / polishText / rewriteVersions),均接受可选 `signal: AbortSignal` 参数
2.`types/ai-assist.d.ts` 中定义共享类型(ToneType / PolishAction / 各 Result 接口)
3.`useAiAssist.ts` 中实现 composable
- 自动补齐:debounce 800ms + AbortController 生命周期管理 + ghostText 状态
- 语气/润色/改写:loading 状态 + result 状态 + 面板 visible 状态
- `cleanup()` 函数:组件卸载时 abort 所有进行中的请求
**T03: AI 辅助 UI 组件(补齐 + 语气 + 工具栏)**
1. `GhostText.vue` — 幽灵文字渲染层:
- 创建隐藏 div 镜像 textarea 样式
- 计算光标像素坐标(截取光标前文本 → 镜像 div → getBoundingClientRect
- 在光标位置覆盖灰色斜体文字
- 监听 Tab(接受全部)/ Shift+Tab(接受一个词)/ Esc(清除)
2. `ToneAdjustPopover.vue` — 语气选择浮层:
- 3 种语气按钮(专业/友好/简洁)
- 原文/改写文对比展示
- 替换/取消按钮
3. `AiAssistToolbar.vue` — AI 辅助工具栏:
- 补齐开关(toggle 按钮)
- 语气/润色/改写按钮
- 按钮状态管理(禁用条件:无选中文字时禁用语气按钮,无文本时禁用润色/改写按钮)
**T04: AI 辅助 UI 组件(润色 + 改写)+ ReplyBox 集成**
1. `PolishPanel.vue` — 润色精修面板:
- 扩写/压缩/纠错 3 个操作按钮
- 左右对比布局(原文 | 结果,结果可编辑)
- 替换全部/取消按钮
2. `RewritePanel.vue` — 改写版本选择面板:
- 3 个版本卡片纵向排列(可滚动)
- 每个版本支持"替换"/"追加"
3. `ReplyBox.vue` 改造:
- 引入 `useAiAssist` composable
- 在工具栏中插入 `AiAssistToolbar` 组件(分隔线隔开)
- 在 textarea 区域叠加 `GhostText` 组件
- 添加 `ToneAdjustPopover` / `PolishPanel` / `RewritePanel` 组件
- 改造 `handleKeydown`:Tab 键接受补齐、Esc 键清除补齐
- 改造 input 事件:触发 debounce 补齐
- 组件卸载时调用 `aiAssist.cleanup()`
---
### 8. 共享知识
#### 8.1 后端约定
```
- 所有 API 响应使用统一格式:{code: 0, data: {...}, message: "success"}
- code=0 表示成功,非0表示错误
- 使用 success_response(data=result) 返回
- 所有端点需要坐席认证:Depends(get_current_agent)
- 所有端点复用 _validate_conversation() 验证会话存在性
- 消息历史获取复用 _get_recent_messages(),按时间正序排列
- Dify API 调用统一通过 _call_wingman_api()OpenAI 兼容格式
- _call_wingman_api() 改造后 temperature 参数默认 0.3,现有方法不传参则行为不变
- AI 不可用时所有方法返回默认值(空字符串/空列表),不抛异常
- Redis 缓存 key 格式:wingman:autocomplete:{md5(text + conversation_id)}TTL=30s
- Redis 不可用时缓存降级(跳过读写,直接调用 Dify)
- RAGFlow 检索通过 build_ragflow_client() 获取客户端,不可用时返回 None 降级
- Pydantic Schema 放在 schemas/wingman_assist.py,与现有 schemas/ 目录约定一致
```
#### 8.2 前端约定
```
- 所有 API 调用使用 apiClientsrc/api/index.ts 导出的 axios 实例)
- 响应拦截器自动提取 res.dataScheme A),API 函数直接返回内层 data
- 错误时 reject {code, message},调用方 try-catch
- AbortController 管理补齐请求生命周期:
- 每次新请求前 abort 上一个 controller
- 组件卸载时 abort 所有进行中的请求
- 补齐 debounce 800ms,使用 lodash-es 的 debounce 或 @vueuse/core 的 useDebounceFn
- 幽灵文字样式:
- color: #B4B2A9(灰色)
- font-style: italic(斜体)
- pointer-events: none(不可点击)
- user-select: none(不可选中)
- AI 按钮使用统一紫色系 #534AB7,与常规工具栏按钮区分
- 组件命名:PascalCase,文件名与组件名一致
- Composable 命名:use 前缀(useAiAssist),放在 composables/ 目录
- TypeScript 类型定义:API 响应类型放在 wingman.ts,共享类型放在 types/ai-assist.d.ts
- AI 请求失败时静默处理(不弹 ElMessage 错误提示),不影响坐席正常输入
- 面板/浮层使用 Element Plus 的 el-popover 或自定义浮层,z-index 不低于 100
```
#### 8.3 错误处理策略
```
- 后端:WingmanService 所有方法 try-catch 全部异常,返回默认值
- generate_completion 降级 → {completion: "", confidence: 0.0}
- adjust_tone 降级 → {rewritten_text: "", tone: "", changes_summary: "AI 服务暂不可用"}
- polish_text 降级 → {polished_text: "", action: "", changes_summary: "AI 服务暂不可用"}
- rewrite_versions 降级 → {versions: []}
- 前端:useAiAssist 中 try-catch 请求错误
- 补齐失败 → ghostText 保持空,不显示
- 语气/润色/改写失败 → loading=false,面板保持打开,显示重试按钮
- AbortError(请求被取消)→ 静默忽略,不处理
```
#### 8.4 性能约定
```
- 补齐 API:前端 AbortController 超时 1.5s,后端 httpx 超时 3s(兜底)
- 语气/润色 API:前端超时 3s(与 apiClient 默认 20s 不同,需单独设置)
- 改写 API:前端超时 5s(需单独设置)
- 补齐 Redis 缓存 TTL=30s,避免短时间重复输入相同前缀时重复调用 Dify
- 上下文消息条数:补齐/语气/润色取最近 5 条,改写取最近 10 条
- 改写功能的 RAGFlow 检索与 Dify 调用串行(先检索知识 → 注入 prompt → 调用 Dify
```
---
### 9. 任务依赖图
```mermaid
graph LR
T01[T01: 后端服务层与 API 端点<br/>wingman_service.py + wingman.py<br/>+ schemas + dependencies<br/>预估 2 天]
T02[T02: 前端 API 封装与状态管理<br/>wingman.ts + useAiAssist.ts<br/>+ types<br/>预估 1 天]
T03[T03: UI 组件 - 补齐+语气+工具栏<br/>GhostText + ToneAdjustPopover<br/>+ AiAssistToolbar<br/>预估 2 天]
T04[T04: UI 组件 - 润色+改写+集成<br/>PolishPanel + RewritePanel<br/>+ ReplyBox 改造<br/>预估 2 天]
T01 --> T02
T01 --> T03
T02 --> T03
T01 --> T04
T02 --> T04
T03 --> T04
style T01 fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style T02 fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style T03 fill:#fff3e0,stroke:#f57c00,stroke-width:2px
style T04 fill:#fce4ec,stroke:#c62828,stroke-width:2px
```
**关键路径**T01 → T02 → T03 → T04(总计约 7 天)
**并行机会**
- T01 完成后,T02 可独立开始
- T02 完成后,T03 和 T04 的组件开发可部分并行(但 T04 依赖 T03 的 AiAssistToolbar
- T01 的后端 API 可用 Mock 数据先行联调前端组件
---
## 附录:Dify System Prompt 汇总
### A.1 自动补齐(temperature=0.2
```
你是一个IT服务坐席输入助手。根据坐席当前正在输入的内容和对话上下文,补齐下一句话。
要求:
1. 补齐内容自然衔接当前文字,不要重复已有内容
2. 长度控制在1-2个短句,不超过80字
3. 语气专业、简洁,符合IT服务规范
4. 只返回补齐的文字,不要加引号或其他标记
```
### A.2 语气调整(temperature=0.3
```
你是一个IT服务话术改写助手。将坐席选中的文字改写为{tone}风格。
语气定义:
- 专业:使用准确的技术术语,结构化表达,去除口语化内容
- 友好:适当增加问候和关心语,语气更亲和
- 简洁:去除冗余,直奔主题,控制字数
要求:
1. 保持原意不变,只调整语气和表达方式
2. 改写后的文字长度与原文相近(±30%)
3. 符合IT服务坐席的专业规范
4. 只返回改写后的文字,不要加引号或解释
```
### A.3 文字润色(temperature=0.3
```
你是一个IT服务文字精修助手。对坐席输入的文字进行{action}处理。
操作定义:
- 扩写:在原文基础上增加操作步骤、注意事项、解释说明,使回复更完整
- 压缩:精简表达,去除重复和冗余,保留核心信息,控制字数
- 纠错:检查并修正错别字、语法错误、标点符号、格式问题
要求:
1. 保持原意不变
2. 只返回处理后的文字,不要加引号或解释
```
### A.4 智能改写(temperature=0.6
```
你是一个IT服务智能回复助手。基于对话上下文和知识库,为坐席生成3个不同风格的备选回复。
版本要求:
- 版本1:简洁直接,一句话说明问题和解决方案
- 版本2:详细带步骤,包含操作步骤和注意事项
- 版本3:带知识库引用,引用相关文档并给出权威解决方案
要求:
1. 3个版本内容不重复,各有侧重
2. 每个版本独立成段,用---分隔
3. 只返回回复内容,不要加额外解释
```