Files
wecom_it_smart_desk/docs/02-技术文档/技术架构/技术方案-REQ-坐席-002-AI辅助消息框-v1.0.md
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

1532 lines
56 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.
# 技术方案 - REQ-坐席-002 AI辅助消息框
> **版本**: v1.0
> **日期**: 2026-07-11
> **REQ编号**: REQ-坐席-002
> **关联PRD**: `PRD-REQ-坐席-002-AI辅助消息框-v1.0.md`
> **状态**: 已完成
> **架构师**: 宋献 (Simon)
> **技术栈**: Vue 3 + TypeScript + Element Plus + FastAPI + Dify AI
---
## 目录
- [Part A: 系统设计](#part-a-系统设计)
- [1. 架构概述](#1-架构概述)
- [2. 后端架构设计](#2-后端架构设计)
- [3. 前端架构设计](#3-前端架构设计)
- [4. 布局重构架构设计](#4-布局重构架构设计)
- [5. 数据流设计](#5-数据流设计)
- [6. 关键设计决策](#6-关键设计决策)
- [Part B: 接口与数据结构](#part-b-接口与数据结构)
- [7. API 规范](#7-api-规范)
- [8. Pydantic 模型](#8-pydantic-模型)
- [9. Dify Prompt 模板](#9-dify-prompt-模板)
- [10. TypeScript 类型定义](#10-typescript-类型定义)
- [Part C: 架构图](#part-c-架构图)
- [11. 类图](#11-类图)
- [12. 时序图](#12-时序图)
- [Part D: 任务分解](#part-d-任务分解)
- [13. 文件清单](#13-文件清单)
- [14. 开发计划](#14-开发计划)
- [15. 风险与缓解](#15-风险与缓解)
---
## Part A: 系统设计
### 1. 架构概述
#### 1.1 功能范围
本设计覆盖两大模块:
| 模块 | 功能 | PRD 参考 |
|------|------|---------|
| AI 辅助消息框 | 实时自动补齐、语气调整、文字润色、智能改写 | `坐席端AI辅助消息框-PRD.md` §2 |
| 布局优化 | 回复建议区、工具栏重构、右栏训练区、放大/缩小开关 | `坐席端布局优化建议.md` §3 |
#### 1.2 系统架构图
```
┌──────────────────────────────────────────────────────────────────┐
│ 坐席工作台 (Vue3 + Element Plus) │
│ │
│ ┌──────────┐ ┌───────────────────┐ ┌───────────────────────┐ │
│ │ 左栏 │ │ 中栏 │ │ 右栏 │ │
│ │ 会话用户 │ │ ┌──────────────┐ │ │ ┌───────────────────┐ │ │
│ │ 列表 │ │ │ UserInfoBar │ │ │ │ PanelModeToggle │ │ │
│ │ +待办 │ │ ├──────────────┤ │ │ │ (正常/放大) │ │ │
│ │ │ │ │ Troubleshoot │ │ │ ├───────────────────┤ │ │
│ │ │ │ ├──────────────┤ │ │ │ AiTrainingPanel │ │ │
│ │ │ │ │ 消息列表 │ │ │ │ ├ SmartTagEditor │ │ │
│ │ │ │ ├──────────────┤ │ │ │ ├ QualityFeedback │ │ │
│ │ │ │ │ReplySuggestArea│ │ │ │ ├ KnowledgeContrib│ │ │
│ │ │ │ │ (AI推荐+快回) │ │ │ │ └ UsageStats │ │ │
│ │ │ │ ├──────────────┤ │ │ └───────────────────┘ │ │
│ │ │ │ │ ReplyBox │ │ │ │ │
│ │ │ │ │ ┌──────────┐ │ │ │ │ │
│ │ │ │ │ │工具栏 │ │ │ │ │ │
│ │ │ │ │ │常规|AI │ │ │ │ │ │
│ │ │ │ │ └──────────┘ │ │ │ │ │
│ │ │ │ │ textarea │ │ │ │ │
│ │ │ │ └──────────────┘ │ │ │ │
│ └──────────┘ └───────────────────┘ └───────────────────────┘ │
└──────────────────────────┬───────────────────────────────────────┘
│ HTTP / WebSocket
┌──────────────────────────────────────────────────────────────────┐
│ FastAPI 后端 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ wingman.py (路由层) │ │
│ │ POST /wingman/autocomplete ← 自动补齐 │ │
│ │ POST /wingman/tone-adjust ← 语气调整 │ │
│ │ POST /wingman/polish ← 文字润色 │ │
│ │ POST /wingman/rewrite ← 智能改写 │ │
│ │ + 现有: draft / summary / tags │ │
│ └────────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼────────────────────────────────┐ │
│ │ WingmanService (服务层) │ │
│ │ generate_completion() ← 新增: 自动补齐 │ │
│ │ adjust_tone() ← 新增: 语气调整 │ │
│ │ polish_text() ← 新增: 文字润色 │ │
│ │ rewrite_versions() ← 新增: 智能改写 │ │
│ │ + 现有: generate_draft / generate_summary / suggest_tags│ │
│ │ │ │
│ │ _call_wingman_api() ← 改造: 支持 temperature 参数 │ │
│ │ _build_context_messages() ← 复用: 角色映射 │ │
│ └────────────────────────┬────────────────────────────────┘ │
│ │ httpx (OpenAI 兼容格式) │
└───────────────────────────┼──────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────┐
│ Dify AI (外部服务) │
│ Wingman Agent: /chat/completions (OpenAI 兼容) │
│ RAGFlow: 知识库检索 (智能改写时调用) │
└──────────────────────────────────────────────────────────────────┘
```
#### 1.3 与现有系统的关系
| 现有组件 | 关系 | 说明 |
|----------|------|------|
| `WingmanService` | 扩展 | 新增 4 个方法,改造 `_call_wingman_api()` 支持 temperature |
| `wingman.py` 路由 | 扩展 | 新增 4 个端点 + Pydantic 请求模型 |
| `wingman.ts` API 层 | 扩展 | 新增 4 个前端 API 调用函数 |
| `ReplyBox.vue` | 改造 | 工具栏重构 + AI 工具按钮 + 补齐逻辑 |
| `AiAssistantPanel.vue` | 全面重构 | 移除回复前功能,改为训练区 |
| `AiRecommendInline.vue` | 删除 | 功能被 `AiRecommendBar.vue` 替代 |
| `AiSuggestReply.vue` | 删除 | 功能合并到 `AiRecommendBar.vue` |
| `QuickReplyPanel.vue` | 改造 | 重命名为 `QuickReplyBar.vue`,横向布局 |
---
### 2. 后端架构设计
#### 2.1 WingmanService 扩展设计
现有 `WingmanService` 包含 4 个方法(generate_draft / generate_summary / suggest_tags / generate_knowledge_suggestion),全部通过 `_call_wingman_api()` 调用 Dify。本设计新增 4 个方法并改造底层调用。
**新增方法签名**
```python
class WingmanService:
# === 现有方法(保持不变) ===
# generate_draft()
# generate_summary()
# suggest_tags()
# generate_knowledge_suggestion()
# === 新增方法 ===
async def generate_completion(
self,
conversation_id: str,
current_text: str,
messages: List[Dict[str, Any]],
max_length: int = 80,
) -> Dict[str, Any]:
"""自动补齐:根据坐席当前输入内容补齐下一句"""
async def adjust_tone(
self,
conversation_id: str,
selected_text: str,
full_text: str,
tone: str, # professional / friendly / concise
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""语气调整:将选中文字改写为指定风格"""
async def polish_text(
self,
conversation_id: str,
text: str,
action: str, # expand / compress / correct
messages: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""文字润色:对输入框全部内容进行扩写/压缩/纠错"""
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]:
"""智能改写:基于对话上下文+知识库生成多个备选版本"""
```
#### 2.2 _call_wingman_api 改造
**当前问题**`_call_wingman_api()` 的 temperature 硬编码为 0.3,所有方法共用。新功能需要不同 temperature
| 方法 | temperature | 理由 |
|------|-------------|------|
| generate_completion | 0.2 | 补齐需要高确定性,避免创造性发散 |
| adjust_tone | 0.3 | 改写需保持原意,适度创造性 |
| polish_text | 0.3 | 润色需保持原意,适度创造性 |
| rewrite_versions | 0.6 | 改写需要多样化输出,提高创造性 |
| generate_draft (现有) | 0.3 | 保持不变 |
| generate_summary (现有) | 0.3 | 保持不变 |
**改造方案**
```python
# 改造前(当前代码)
async def _call_wingman_api(
self, context_messages: List[Dict[str, str]]
) -> Optional[str]:
payload = {
"model": "Chat",
"messages": context_messages,
"stream": False,
"temperature": 0.3, # ← 硬编码
}
# 改造后
async def _call_wingman_api(
self,
context_messages: List[Dict[str, str]],
temperature: float = 0.3, # ← 新增参数,默认值保持兼容
) -> Optional[str]:
payload = {
"model": "Chat",
"messages": context_messages,
"stream": False,
"temperature": temperature,
}
```
**兼容性**:现有 4 个方法调用时不传 temperature 参数,自动使用默认值 0.3,行为不变。
#### 2.3 上下文构建策略
现有 `_build_context_messages()` 将完整对话历史构建为 OpenAI Chat 格式。新功能需要差异化上下文:
| 方法 | 上下文消息条数 | 附加内容 | 原因 |
|------|--------------|---------|------|
| generate_completion | 最近 5 条 | 当前输入文本拼入最后一条 user 消息 | 补齐需要最近上下文 + 当前输入 |
| adjust_tone | 最近 5 条 | 选中文字 + 完整输入内容作为指令 | 改写需要理解选中文字的上下文 |
| polish_text | 最近 5 条 | 全部输入内容作为指令 | 润色针对输入框全部内容 |
| rewrite_versions | 最近 10 条 | 知识库检索结果(可选) | 改写需要更完整上下文 + 知识库引用 |
**实现方式**:复用 `_build_context_messages()` 构建基础对话上下文,然后在方法内部追加功能特定的指令消息:
```python
async def generate_completion(self, ...):
# 1. 构建基础上下文(最近5条对话 → system prompt + 历史消息)
context = self._build_context_messages(messages, self._COMPLETION_SYSTEM_PROMPT)
# 2. 追加当前输入作为最后一条 user 消息
context.append({
"role": "user",
"content": f"坐席正在输入的内容:{current_text}\n请补齐下一句话。"
})
# 3. 调用 Difytemperature=0.2
result = await self._call_wingman_api(context, temperature=0.2)
```
#### 2.4 智能改写的知识库集成
`rewrite_versions()` 在生成"带知识库引用"版本时需要调用 RAGFlow 检索:
```
坐席点击"改写"
→ 后端从对话消息提取关键词
→ 调用 RAGFlow API 检索相关知识
→ 将检索结果注入 Dify prompt
→ Dify 生成 3 个版本(含知识库引用版本)
```
**RAGFlow 调用**:复用现有 `RAGFlowClient`(位于 `app/integrations/`),无需新建。若 RAGFlow 不可用,降级为仅基于对话上下文生成 2 个版本。
#### 2.5 降级策略
与现有方法保持一致:Dify 不可用时返回默认值而非抛异常。
```python
# 降级返回示例(generate_completion
{
"completion": "",
"confidence": 0.0,
"error": "Wingman 服务暂不可用"
}
# 降级返回示例(rewrite_versions
{
"versions": [],
"error": "AI 服务暂不可用,请稍后重试"
}
```
---
### 3. 前端架构设计
#### 3.1 组件架构图
```
Workspace.vue (主布局)
├── ConversationList.vue (左栏 - 会话用户列表)
│ │ ├── 筛选 Tab:待处理 → 进行中 → 已完成 → 全部(默认激活"待处理",见 PRD-REQ-坐席-009
│ │ └── 数据源:Pinia conversationStoremyConversations / colleagueConversations / historyConversations
│ └── TodoPanel.vue (待办面板)
├── ChatArea.vue (中栏)
│ ├── UserInfoBar.vue (用户信息栏 - 详情默认折叠)
│ ├── TroubleshootBar.vue (排查步骤栏 - 默认折叠为图标条)
│ ├── MessageList.vue (消息列表)
│ ├── ReplySuggestArea.vue (回复建议区 - 新增)
│ │ ├── AiRecommendBar.vue (AI推荐条 - 合并自 AiRecommendInline + AiSuggestReply)
│ │ └── QuickReplyBar.vue (快速回复条 - 改造自 QuickReplyPanel)
│ └── ReplyBox.vue (输入框 - 改造)
│ ├── 工具栏 (单行左右分区)
│ │ ├── 常规工具组: [截图] [拍照] [表情] [文件] [邀请]
│ │ └── AI工具组: [补齐toggle] [语气] [润色] [改写]
│ ├── GhostTextOverlay.vue (幽灵文字覆盖层 - 新增)
│ ├── ToneAdjustPopover.vue (语气调整浮层 - 新增)
│ ├── PolishPanel.vue (润色精修面板 - 新增)
│ └── RewritePanel.vue (改写选择面板 - 新增)
└── AiAssistantPanel.vue (右栏 - 全面重构)
├── PanelModeToggle.vue (模式切换: 正常/放大 - 新增)
└── AiTrainingPanel.vue (训练区主体 - 新增)
├── SmartTagEditor.vue (智能标注)
├── QualityFeedback.vue (质量反馈)
├── KnowledgeContribute.vue (知识贡献)
└── UsageStats.vue (使用统计)
```
#### 3.2 Composable 设计
新增 3 个 composable 封装 AI 辅助逻辑:
| Composable | 职责 | 状态范围 |
|------------|------|---------|
| `useAutoComplete.ts` | 自动补齐的 debounce、请求、幽灵文字管理 | ReplyBox 局部 |
| `useAiTextTools.ts` | 语气/润色/改写的统一调用和结果管理 | ReplyBox 局部 |
| `usePanelMode.ts` | 右栏放大/缩小模式切换 | Workspace 全局 |
**useAutoComplete.ts 核心逻辑**
```typescript
export function useAutoComplete(
textareaRef: Ref<HTMLTextAreaElement>,
conversationId: Ref<string>,
enabled: Ref<boolean>,
) {
const ghostText = ref('') // 幽灵文字内容
const isLoading = ref(false) // 加载状态
const abortController = ref<AbortController | null>(null)
// debounce 800ms,输入停顿后触发
const debouncedFetch = useDebounceFn(async (text: string) => {
if (!enabled.value || text.length < 5) {
ghostText.value = ''
return
}
// 取消上一个请求
abortController.value?.abort()
abortController.value = new AbortController()
isLoading.value = true
try {
const result = await wingmanApi.autocomplete(
conversationId.value,
{ current_text: text, cursor_position: text.length },
{ signal: abortController.value.signal },
)
ghostText.value = result.completion || ''
} catch (e) {
if (!isAbortError(e)) ghostText.value = ''
} finally {
isLoading.value = false
}
}, 800)
// Tab 接受补齐
function acceptCompletion() {
// 将 ghostText 追加到输入框
// 清空 ghostText
}
// Esc 清除补齐
function dismissCompletion() {
ghostText.value = ''
}
return { ghostText, isLoading, debouncedFetch, acceptCompletion, dismissCompletion }
}
```
**usePanelMode.ts 核心逻辑**
```typescript
export function usePanelMode() {
const mode = ref<'normal' | 'expanded'>('normal')
const panelWidth = computed(() =>
mode.value === 'normal' ? '260px' : '560px'
)
const centerColVisible = computed(() =>
mode.value === 'normal'
)
function toggleMode() {
mode.value = mode.value === 'normal' ? 'expanded' : 'normal'
}
return { mode, panelWidth, centerColVisible, toggleMode }
}
```
#### 3.3 幽灵文字实现方案
PRD 确认为内联幽灵文字。实现方式为 textarea 上方覆盖透明文字层:
```
┌──────────────────────────────┐
│ textarea (z-index: 1) │ ← 文字颜色: #303133 (正常)
│ 透明背景,文字不可见的部分 │ 选中文字后的补齐文字不可见
├──────────────────────────────┤
│ ghost overlay (z-index: 2) │ ← 文字颜色: #C0C4CC (灰色)
│ pointer-events: none │ 仅在 textarea 文字末尾之后显示
│ 位置与 textarea 文字完全对齐 │ 通过 mirror div 技术计算光标坐标
└──────────────────────────────┘
```
**技术要点**
1. 创建一个与 textarea 样式完全一致的 `<div>`(mirror div),用于计算光标坐标
2. ghost overlay 定位在光标坐标处,显示灰色补齐文字
3. `pointer-events: none` 确保不拦截鼠标事件
4. Tab 键监听在 textarea 的 `keydown` 事件中,`e.key === 'Tab'` 时阻止默认行为并接受补齐
5. 继续输入时自动清除 ghost text(`input` 事件触发 debounce 重新请求)
**Mirror div 技术**(用于精确计算光标位置):
```typescript
function getCursorCoordinates(textarea: HTMLTextAreaElement, position: number) {
const div = document.createElement('div')
// 复制 textarea 的所有影响文字布局的样式
const styles = window.getComputedStyle(textarea)
Object.assign(div.style, {
position: 'absolute',
visibility: 'hidden',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
// ... 复制 font-family, font-size, padding, border, width 等
})
// 截取光标位置之前的文字
div.textContent = textarea.value.substring(0, position)
document.body.appendChild(div)
// 创建一个 span 标记光标位置
const span = document.createElement('span')
span.textContent = '|'
div.appendChild(span)
// 获取 span 的坐标(相对于 textarea
const coords = {
x: span.offsetLeft - textarea.scrollLeft,
y: span.offsetTop - textarea.scrollTop,
}
document.body.removeChild(div)
return coords
}
```
#### 3.4 前端 API 层扩展
`frontend-agent/src/api/wingman.ts` 中新增 4 个函数:
```typescript
// 自动补齐
export async function autocomplete(
conversationId: string,
data: { current_text: string; cursor_position: number; max_length?: number },
config?: { signal?: AbortSignal },
): Promise<{ completion: string; confidence: number }>
// 语气调整
export async function toneAdjust(
conversationId: string,
data: { selected_text: string; full_text: string; tone: string },
): Promise<{ rewritten_text: string; tone: string; changes_summary: string }>
// 文字润色
export async function polish(
conversationId: string,
data: { text: string; action: string },
): Promise<{ polished_text: string; action: string; changes_summary: string }>
// 智能改写
export async function rewrite(
conversationId: string,
data: { current_text: string; generate_count?: number; include_knowledge?: boolean },
): Promise<{ versions: Array<{ text: string; style: string; source: string }> }>
```
---
### 4. 布局重构架构设计
#### 4.1 CSS 变量体系
```css
:root {
/* === 布局尺寸 === */
--sidebar-width: 260px; /* 左栏: 280px → 260px */
--assistant-panel-width: 260px; /* 右栏正常: 320px → 260px */
--assistant-panel-expanded: 560px; /* 右栏放大: 新增 */
/* === 右栏模式控制 === */
--center-col-flex: 1; /* 正常: 中栏弹性 */
--center-col-width: auto; /* 正常: 中栏自动 */
--center-col-overflow: visible; /* 正常: 可见 */
/* === 回复建议区颜色 === */
--suggest-ai-bg: #FAECE7; /* AI推荐背景 - 珊瑚色 */
--suggest-ai-border: #F0997B;
--suggest-ai-text: #993C1D;
--suggest-quick-bg: #EAF3DE; /* 快速回复背景 - 绿色 */
--suggest-quick-border: #97C459;
--suggest-quick-text: #27500A;
/* === 工具栏分组颜色 === */
--toolbar-conv-bg: #E1F5EE; /* 常规工具 - 绿色系 */
--toolbar-ai-bg: #FAEEDA; /* AI工具 - 琥珀色系 */
/* === 回复建议区高度 === */
--suggest-area-height: auto; /* 动态: 选中后 0 */
--suggest-ai-height: 44px; /* AI推荐区固定高度 */
--suggest-quick-height: 56px; /* 快速回复区固定2层 */
}
```
#### 4.2 右栏放大/缩小模式实现
```vue
<!-- Workspace.vue -->
<template>
<div class="workspace" :class="{ 'panel-expanded': panelMode === 'expanded' }">
<aside class="sidebar"><ConversationList /></aside>
<main class="center-col"><ChatArea /></main>
<aside class="assistant-panel"><AiAssistantPanel /></aside>
</div>
</template>
<style scoped>
.workspace {
display: flex;
height: 100vh;
transition: all 0.3s ease;
}
.sidebar {
width: var(--sidebar-width);
flex-shrink: 0;
}
.center-col {
flex: var(--center-col-flex);
width: var(--center-col-width);
overflow: var(--center-col-overflow);
min-width: 0;
transition: all 0.3s ease;
}
.assistant-panel {
width: var(--assistant-panel-width);
flex-shrink: 0;
transition: width 0.3s ease;
}
/* 放大模式 */
.workspace.panel-expanded {
--assistant-panel-width: var(--assistant-panel-expanded);
--center-col-flex: 0;
--center-col-width: 0;
--center-col-overflow: hidden;
}
</style>
```
#### 4.3 回复建议区动态高度
```vue
<!-- ReplySuggestArea.vue -->
<template>
<div
class="reply-suggest-area"
:class="{ collapsed: !shouldShow }"
>
<div class="ai-recommend-section" v-if="shouldShow">
<AiRecommendBar />
</div>
<div class="quick-reply-section" v-if="shouldShow">
<QuickReplyBar />
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
inputText: string // 来自 ReplyBox 的输入框内容
}>()
// 输入框为空时展开,有内容时缩回
const shouldShow = computed(() => props.inputText.length === 0)
</script>
<style scoped>
.reply-suggest-area {
max-height: calc(var(--suggest-ai-height) + var(--suggest-quick-height));
overflow: hidden;
transition: max-height 0.3s ease;
}
.reply-suggest-area.collapsed {
max-height: 0;
}
</style>
```
#### 4.4 工具栏单行左右分区
```vue
<!-- ReplyBox.vue 工具栏部分 -->
<template>
<div class="toolbar">
<!-- 左侧: 常规工具 -->
<div class="toolbar-left">
<button class="tool-btn conv" @click="screenshot">截图</button>
<button class="tool-btn conv" @click="camera">拍照</button>
<button class="tool-btn conv" @click="emoji">表情</button>
<button class="tool-btn conv" @click="file">文件</button>
<button class="tool-btn conv" @click="invite">邀请</button>
</div>
<!-- 右侧: AI工具 + 发送 -->
<div class="toolbar-right">
<button
class="tool-btn ai"
:class="{ active: autoCompleteEnabled }"
@click="autoCompleteEnabled = !autoCompleteEnabled"
>补齐</button>
<button class="tool-btn ai" @click="showTonePopover" :disabled="!hasSelection">语气</button>
<button class="tool-btn ai" @click="showPolishPanel">润色</button>
<button class="tool-btn ai" @click="showRewritePanel">改写</button>
<button class="tool-btn send" @click="send">发送</button>
</div>
</div>
</template>
```
---
### 5. 数据流设计
#### 5.1 自动补齐数据流
```
坐席输入文字
→ textarea input 事件
→ useAutoComplete.debouncedFetch(text) [debounce 800ms]
→ 取消上一个 AbortController
→ 调用 wingmanApi.autocomplete(conversationId, { current_text })
→ POST /api/conversations/{id}/wingman/autocomplete
→ FastAPI 路由层
→ _get_recent_messages(conversationId, db, limit=5)
→ WingmanService.generate_completion(conversationId, text, messages)
→ _build_context_messages(messages, _COMPLETION_SYSTEM_PROMPT)
→ 追加 current_text 到上下文末尾
→ _call_wingman_api(context, temperature=0.2)
→ Dify API POST /chat/completions
→ 解析 choices[0].message.content
→ 返回 { completion, confidence }
→ 前端设置 ghostText.value = result.completion
→ ghost overlay 渲染灰色文字
坐席按 Tab
→ acceptCompletion()
→ inputText.value += ghostText.value
→ ghostText.value = ''
```
#### 5.2 语气调整数据流
```
坐席选中文字 → 点击"语气"按钮 → 弹出 ToneAdjustPopover
→ 选择"专业/友好/简洁"
→ 调用 wingmanApi.toneAdjust(conversationId, { selected_text, full_text, tone })
→ POST /api/conversations/{id}/wingman/tone-adjust
→ WingmanService.adjust_tone(...)
→ _build_context_messages(messages, _TONE_SYSTEM_PROMPT)
→ 注入 selected_text + full_text + tone 指令
→ _call_wingman_api(context, temperature=0.3)
→ 返回 { rewritten_text, tone, changes_summary }
→ 浮层显示"原文 → 改写文"对比
→ 坐席点击"替换" → 用 rewritten_text 替换选中区域
→ 坐席点击"取消" → 关闭浮层,不修改
```
#### 5.3 右栏模式切换数据流
```
坐席点击 PanelModeToggle "放大"按钮
→ usePanelMode.toggleMode()
→ mode.value = 'expanded'
→ CSS 变量更新:
--assistant-panel-width → 560px
--center-col-flex → 0
--center-col-width → 0
--center-col-overflow → hidden
→ CSS transition 0.3s 平滑动画
→ 中栏压缩隐藏,右栏展开为 560px
坐席点击"正常"按钮
→ toggleMode() → mode.value = 'normal'
→ CSS 变量恢复
→ 中栏恢复显示,右栏缩回 260px
```
---
### 6. 关键设计决策
#### 决策 1:自动补齐用 Mirror Div 而非 contenteditable
**选择**:保持 `<textarea>` + mirror div 计算 + ghost overlay 覆盖
**理由**
- `contenteditable` div 虽然原生支持幽灵文字,但会破坏现有的 Ctrl+Enter 发送、Enter 换行等快捷键逻辑
- textarea 的所有现有功能(粘贴图片、拖拽文件等)无需改动
- mirror div 是业界成熟方案(VS Code、GitHub Copilot 均用此方案)
- 实现复杂度可控,仅需一个坐标计算函数
#### 决策 2:补齐 API 用 AbortController 而非 WebSocket
**选择**HTTP POST + AbortController 取消
**理由**
- 补齐请求频率高(每次停顿 800ms 触发),但每次请求轻量(< 500 字符)
- WebSocket 需要新增消息类型和连接管理,增加复杂度
- AbortController 可在浏览器层取消请求,避免无效响应
- 现有 WingmanService 全部使用 HTTP 同步调用,保持一致性
#### 决策 3:语气/润色/改写用浮层面板而非全屏弹窗
**选择**`el-popover` / `el-drawer` 浮层
**理由**
- 坐席需要在操作时仍能看到对话上下文
- 全屏 `el-dialog` 会遮挡消息列表,打断工作流
- 浮层可定位在工具栏按钮附近,视线不跳转
- 润色和改写面板使用 `el-drawer` 从右侧滑出,宽度 400px,不遮挡消息列表
#### 决策 4:右栏模式切换用 CSS 变量而非 v-if
**选择**CSS 变量 + class 切换,不用 v-if 销毁/重建组件
**理由**
- `v-if` 会导致中栏组件销毁/重建,丢失消息列表滚动位置和输入框内容
- CSS 变量方案仅改变视觉布局,组件实例保持存活
- `overflow: hidden` + `width: 0` 确保中栏不可见但不销毁
- 切回正常模式时立即可见,无需重新渲染
#### 决策 5:回复建议区用 max-height 动画而非 v-show
**选择**`max-height` transition + `collapsed` class
**理由**
- `v-show`display: none)会导致回复建议区下方布局跳动
- `max-height``auto``0` 的过渡更平滑
- 组件实例保持存活,AI推荐数据不丢失
- 选中条目后缩回的动画体验更流畅
#### 决策 6_call_wingman_api 改造为可选参数而非新增方法
**选择**:在现有方法签名上新增 `temperature` 参数,默认值 0.3
**理由**
- 现有 4 个方法的调用无需修改(默认值兼容)
- 避免代码重复(不需要新增 `_call_wingman_api_with_temp()`
- 未来其他方法也可按需传递 temperature
- 改动面最小,降低回归风险
---
## Part B: 接口与数据结构
### 7. API 规范
#### 7.1 自动补齐
```
POST /api/conversations/{conversation_id}/wingman/autocomplete
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| current_text | string | 是 | 坐席当前输入的文字 |
| cursor_position | int | 是 | 光标位置(字符偏移量) |
| max_length | int | 否 | 补齐最大长度,默认 80 |
**响应**
```json
{
"code": 200,
"data": {
"completion": "正在查看相关工单记录,请稍候。",
"confidence": 0.85
}
}
```
#### 7.2 语气调整
```
POST /api/conversations/{conversation_id}/wingman/tone-adjust
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| selected_text | string | 是 | 坐席选中的文字 |
| full_text | string | 是 | 输入框全部内容 |
| tone | string | 是 | `professional` / `friendly` / `concise` |
**响应**
```json
{
"code": 200,
"data": {
"rewritten_text": "经排查,您的VPN连接异常由本地网络DNS解析超时引起。",
"tone": "professional",
"changes_summary": "去除口语化表达,增加技术术语"
}
}
```
#### 7.3 文字润色
```
POST /api/conversations/{conversation_id}/wingman/polish
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| text | string | 是 | 输入框全部内容 |
| action | string | 是 | `expand` / `compress` / `correct` |
**响应**
```json
{
"code": 200,
"data": {
"polished_text": "建议您按以下步骤操作:\n1. 完全退出VPN客户端\n2. 重新启动\n3. 重新连接",
"action": "expand",
"changes_summary": "增加了操作步骤细化和兜底方案"
}
}
```
#### 7.4 智能改写
```
POST /api/conversations/{conversation_id}/wingman/rewrite
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| current_text | string | 否 | 当前输入框内容(可为空) |
| generate_count | int | 否 | 生成数量,默认 3 |
| include_knowledge | bool | 否 | 是否检索知识库,默认 true |
**响应**
```json
{
"code": 200,
"data": {
"versions": [
{ "text": "VPN连接异常,建议重启客户端后重试。", "style": "简洁直接", "source": "对话上下文" },
{ "text": "VPN连接问题处理步骤:\n1. 退出VPN客户端\n2. 重启\n3. 重新连接", "style": "详细带步骤", "source": "对话上下文" },
{ "text": "根据知识库文档《VPN故障排查指南》...", "style": "带知识库引用", "source": "RAGFlow知识库" }
]
}
}
```
---
### 8. Pydantic 模型
现有 Wingman 端点未使用 Pydantic 请求模型。本设计新增请求体验证模型,提升 API 健壮性:
```python
# backend/app/schemas/wingman.py
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from enum import Enum
class ToneType(str, Enum):
PROFESSIONAL = "professional"
FRIENDLY = "friendly"
CONCISE = "concise"
class PolishAction(str, Enum):
EXPAND = "expand"
COMPRESS = "compress"
CORRECT = "correct"
class AutocompleteRequest(BaseModel):
current_text: str = Field(..., min_length=1, max_length=2000, description="坐席当前输入文字")
cursor_position: int = Field(..., ge=0, description="光标位置")
max_length: int = Field(default=80, ge=10, le=200, description="补齐最大长度")
class ToneAdjustRequest(BaseModel):
selected_text: str = Field(..., min_length=5, max_length=2000, description="选中的文字")
full_text: str = Field(..., min_length=1, max_length=5000, description="输入框全部内容")
tone: ToneType = Field(..., description="目标语气")
class PolishRequest(BaseModel):
text: str = Field(..., min_length=1, max_length=5000, description="待润色文字")
action: PolishAction = Field(..., description="润色操作")
class RewriteRequest(BaseModel):
current_text: str = Field(default="", max_length=5000, description="当前输入内容")
generate_count: int = Field(default=3, ge=1, le=5, description="生成数量")
include_knowledge: bool = Field(default=True, description="是否检索知识库")
# 响应模型
class AutocompleteResponse(BaseModel):
completion: str = Field(default="", description="补齐文字")
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="置信度")
class ToneAdjustResponse(BaseModel):
rewritten_text: str
tone: str
changes_summary: str = ""
class PolishResponse(BaseModel):
polished_text: str
action: str
changes_summary: str = ""
class RewriteVersion(BaseModel):
text: str
style: str
source: str
class RewriteResponse(BaseModel):
versions: List[RewriteVersion] = Field(default_factory=list)
```
---
### 9. Dify Prompt 模板
`WingmanService` 中新增 4 个类属性常量:
```python
class WingmanService:
# === 现有 Prompts(保持不变) ===
# _DRAFT_SYSTEM_PROMPT
# _SUMMARY_SYSTEM_PROMPT
# _TAGS_SYSTEM_PROMPT
# _KNOWLEDGE_SUGGESTION_PROMPT
# === 新增 Prompts ===
_COMPLETION_SYSTEM_PROMPT = """你是一个IT服务坐席输入助手。根据坐席当前正在输入的内容和对话上下文,补齐下一句话。
要求:
1. 补齐内容自然衔接当前文字,不要重复已有内容
2. 长度控制在1-2个短句,不超过80字
3. 语气专业、简洁,符合IT服务规范
4. 只返回补齐的文字,不要加引号或其他标记"""
_TONE_SYSTEM_PROMPT = """你是一个IT服务话术改写助手。将坐席选中的文字改写为{tone}风格。
语气定义:
- professional(专业):使用准确的技术术语,结构化表达,去除口语化内容
- friendly(友好):适当增加问候和关心语,语气更亲和
- concise(简洁):去除冗余,直奔主题,控制字数
要求:
1. 保持原意不变,只调整语气和表达方式
2. 改写后的文字长度与原文相近(±30%
3. 符合IT服务坐席的专业规范
4. 只返回改写后的文字,不要加引号或解释"""
_POLISH_SYSTEM_PROMPT = """你是一个IT服务文字润色助手。对坐席输入的文字进行{action}
操作定义:
- expand(扩写):在原文基础上增加操作步骤、注意事项、解释说明,使回复更完整
- compress(压缩):精简表达,去除重复和冗余,保留核心信息,控制字数
- correct(纠错):检查并修正错别字、语法错误、标点符号、格式问题
要求:
1. 保持原文核心意思不变
2. 符合IT服务坐席的专业规范
3. 只返回润色后的文字,不要加引号或解释"""
_REWRITE_SYSTEM_PROMPT = """你是一个IT服务回复生成助手。基于对话上下文{with_knowledge},生成{count}个不同风格的回复版本。
版本要求:
- 版本1:简洁直接,一句话说明解决方案
- 版本2:详细带步骤,包含操作步骤和注意事项
- 版本3:带知识库引用,引用相关知识库文档{knowledge_note}
要求:
1. 每个版本独立完整,可直接发送
2. 语气专业、符合IT服务规范
3. 以JSON格式输出:[{{"text": "回复内容", "style": "风格描述", "source": "来源"}}]"""
```
---
### 10. TypeScript 类型定义
```typescript
// frontend-agent/src/types/wingman-assist.ts
/** 语气类型 */
export type Tone = 'professional' | 'friendly' | 'concise'
/** 润色操作 */
export type PolishAction = 'expand' | 'compress' | 'correct'
/** 自动补齐请求 */
export interface AutocompleteRequest {
current_text: string
cursor_position: number
max_length?: number
}
/** 自动补齐响应 */
export interface AutocompleteResponse {
completion: string
confidence: number
}
/** 语气调整请求 */
export interface ToneAdjustRequest {
selected_text: string
full_text: string
tone: Tone
}
/** 语气调整响应 */
export interface ToneAdjustResponse {
rewritten_text: string
tone: string
changes_summary: string
}
/** 润色请求 */
export interface PolishRequest {
text: string
action: PolishAction
}
/** 润色响应 */
export interface PolishResponse {
polished_text: string
action: string
changes_summary: string
}
/** 改写版本 */
export interface RewriteVersion {
text: string
style: string
source: string
}
/** 改写请求 */
export interface RewriteRequest {
current_text?: string
generate_count?: number
include_knowledge?: boolean
}
/** 改写响应 */
export interface RewriteResponse {
versions: RewriteVersion[]
}
/** 右栏面板模式 */
export type PanelMode = 'normal' | 'expanded'
/** 语气选项配置 */
export const TONE_OPTIONS: Array<{ value: Tone; label: string; desc: string }> = [
{ value: 'professional', label: '专业', desc: '准确术语、结构化表达' },
{ value: 'friendly', label: '友好', desc: '增加关心语、语气亲和' },
{ value: 'concise', label: '简洁', desc: '去除冗余、直奔主题' },
]
/** 润色操作配置 */
export const POLISH_ACTIONS: Array<{ value: PolishAction; label: string; desc: string }> = [
{ value: 'expand', label: '扩写', desc: '增加步骤和细节' },
{ value: 'compress', label: '压缩', desc: '精简保留核心' },
{ value: 'correct', label: '纠错', desc: '修正错别字和语法' },
]
```
---
## Part C: 架构图
### 11. 类图
```mermaid
classDiagram
%% ===== 后端 =====
class WingmanService {
-api_url: str
-api_key: str
-timeout: int
-_client: httpx.AsyncClient
+generate_draft() Dict
+generate_summary() Dict
+suggest_tags() Dict
+generate_knowledge_suggestion() Dict
+generate_completion() Dict %% 新增
+adjust_tone() Dict %% 新增
+polish_text() Dict %% 新增
+rewrite_versions() Dict %% 新增
-_get_client() AsyncClient
-_build_context_messages() List
-_call_wingman_api(text, temp) str %% 改造: 新增temp参数
-_parse_json_response() Dict
-_estimate_confidence() float
+close() void
}
class AutocompleteRequest {
+current_text: str
+cursor_position: int
+max_length: int
}
class ToneAdjustRequest {
+selected_text: str
+full_text: str
+tone: ToneType
}
class PolishRequest {
+text: str
+action: PolishAction
}
class RewriteRequest {
+current_text: str
+generate_count: int
+include_knowledge: bool
}
class ToneType {
<<enumeration>>
PROFESSIONAL
FRIENDLY
CONCISE
}
class PolishAction {
<<enumeration>>
EXPAND
COMPRESS
CORRECT
}
%% ===== 前端 =====
class ReplyBox {
+inputText: string
+autoCompleteEnabled: boolean
-useAutoComplete()
-useAiTextTools()
+acceptCompletion()
+showTonePopover()
+showPolishPanel()
+showRewritePanel()
+send()
}
class ReplySuggestArea {
+inputText: string
+shouldShow: boolean
-AiRecommendBar
-QuickReplyBar
}
class AiAssistantPanel {
+mode: PanelMode
-PanelModeToggle
-AiTrainingPanel
+toggleMode()
}
class AiTrainingPanel {
-SmartTagEditor
-QualityFeedback
-KnowledgeContribute
-UsageStats
}
class useAutoComplete {
+ghostText: Ref~string~
+isLoading: Ref~boolean~
-debouncedFetch(text)
+acceptCompletion()
+dismissCompletion()
}
class usePanelMode {
+mode: Ref~PanelMode~
+panelWidth: ComputedRef
+centerColVisible: ComputedRef
+toggleMode()
}
WingmanService --> AutocompleteRequest : 接收
WingmanService --> ToneAdjustRequest : 接收
WingmanService --> PolishRequest : 接收
WingmanService --> RewriteRequest : 接收
ToneAdjustRequest --> ToneType
PolishRequest --> PolishAction
ReplyBox --> useAutoComplete : 使用
ReplyBox --> ReplySuggestArea : 包含
AiAssistantPanel --> usePanelMode : 使用
AiAssistantPanel --> AiTrainingPanel : 包含
```
---
### 12. 时序图
#### 12.1 自动补齐时序图
```mermaid
sequenceDiagram
participant A as 坐席
participant R as ReplyBox
participant U as useAutoComplete
participant API as wingman.ts
participant F as FastAPI
participant W as WingmanService
participant D as Dify
A->>R: 输入文字
R->>U: input event (text)
U->>U: debounce 800ms
U->>U: abort previous request
U->>API: autocomplete(convId, {current_text})
API->>F: POST /wingman/autocomplete
F->>F: _get_recent_messages(limit=5)
F->>W: generate_completion(convId, text, msgs)
W->>W: _build_context_messages(msgs, prompt)
W->>W: append current_text to context
W->>D: POST /chat/completions (temp=0.2)
D-->>W: {choices[0].message.content}
W-->>F: {completion, confidence}
F-->>API: {code:200, data:{completion,confidence}}
API-->>U: result
U->>U: ghostText.value = result.completion
U-->>R: ghost overlay 渲染灰色文字
R-->>A: 显示幽灵文字
A->>R: 按 Tab 键
R->>U: keydown Tab
U->>U: inputText += ghostText
U->>U: ghostText = ''
R-->>A: 补齐文字变为正常颜色
```
#### 12.2 语气调整时序图
```mermaid
sequenceDiagram
participant A as 坐席
participant R as ReplyBox
participant T as ToneAdjustPopover
participant API as wingman.ts
participant F as FastAPI
participant W as WingmanService
participant D as Dify
A->>R: 选中文字 (≥5字符)
A->>R: 点击"语气"按钮
R->>T: 弹出浮层 (3个语气选项)
A->>T: 选择"专业"
T->>API: toneAdjust(convId, {selected_text, full_text, tone})
API->>F: POST /wingman/tone-adjust
F->>F: _get_recent_messages(limit=5)
F->>W: adjust_tone(convId, selected, full, tone, msgs)
W->>W: _build_context_messages(msgs, _TONE_SYSTEM_PROMPT)
W->>W: 注入 selected_text + full_text + tone
W->>D: POST /chat/completions (temp=0.3)
D-->>W: 改写后的文字
W-->>F: {rewritten_text, tone, changes_summary}
F-->>API: response
API-->>T: result
T->>T: 显示"原文 → 改写文"对比
T-->>A: 展示对比结果
alt 坐席点击"替换"
A->>T: 点击"替换"
T->>R: 替换选中区域文字
T->>T: 关闭浮层
else 坐席点击"取消"
A->>T: 点击外部/Esc
T->>T: 关闭浮层,不修改
end
```
#### 12.3 右栏模式切换时序图
```mermaid
sequenceDiagram
participant A as 坐席
participant P as PanelModeToggle
participant U as usePanelMode
participant W as Workspace
participant C as ChatArea (中栏)
participant R as AiAssistantPanel (右栏)
A->>P: 点击"放大"按钮
P->>U: toggleMode()
U->>U: mode.value = 'expanded'
U->>W: class="panel-expanded"
W->>W: CSS变量更新 (transition 0.3s)
Note over W: --assistant-panel-width: 560px<br/>--center-col-flex: 0<br/>--center-col-width: 0<br/>--center-col-overflow: hidden
W->>C: flex=0, width=0, overflow=hidden
W->>R: width=560px
Note over C,R: 0.3s 平滑动画<br/>中栏压缩隐藏<br/>右栏展开为完整训练面板
A->>P: 点击"正常"按钮
P->>U: toggleMode()
U->>U: mode.value = 'normal'
U->>W: 移除 class="panel-expanded"
W->>W: CSS变量恢复
W->>C: flex=1, width=auto, overflow=visible
W->>R: width=260px
Note over C,R: 0.3s 平滑动画<br/>中栏恢复显示<br/>右栏缩回正常宽度
```
---
## Part D: 任务分解
### 13. 文件清单
#### 13.1 后端新建文件
| 文件路径 | 说明 |
|----------|------|
| `backend/app/schemas/wingman_assist.py` | 新增 Pydantic 请求/响应模型 |
#### 13.2 后端修改文件
| 文件路径 | 修改范围 | 说明 |
|----------|----------|------|
| `backend/app/services/wingman_service.py` | 扩展 | 新增 4 个方法 + 4 个 system prompt 常量;改造 `_call_wingman_api()` 新增 temperature 参数 |
| `backend/app/api/wingman.py` | 扩展 | 新增 4 个端点;引入 Pydantic 模型做请求验证 |
#### 13.3 前端新建文件
| 文件路径 | 说明 |
|----------|------|
| `frontend-agent/src/types/wingman-assist.ts` | TypeScript 类型定义 + 常量配置 |
| `frontend-agent/src/composables/useAutoComplete.ts` | 自动补齐 composable |
| `frontend-agent/src/composables/useAiTextTools.ts` | 语气/润色/改写统一 composable |
| `frontend-agent/src/composables/usePanelMode.ts` | 右栏模式切换 composable |
| `frontend-agent/src/components/chat/ReplySuggestArea.vue` | 回复建议区容器 |
| `frontend-agent/src/components/chat/AiRecommendBar.vue` | AI推荐条(合并自 AiRecommendInline + AiSuggestReply |
| `frontend-agent/src/components/chat/QuickReplyBar.vue` | 快速回复条(改造自 QuickReplyPanel |
| `frontend-agent/src/components/chat/GhostTextOverlay.vue` | 幽灵文字覆盖层 |
| `frontend-agent/src/components/chat/ToneAdjustPopover.vue` | 语气调整浮层 |
| `frontend-agent/src/components/chat/PolishPanel.vue` | 润色精修面板 |
| `frontend-agent/src/components/chat/RewritePanel.vue` | 改写选择面板 |
| `frontend-agent/src/components/assistant/PanelModeToggle.vue` | 右栏模式切换按钮组 |
| `frontend-agent/src/components/assistant/AiTrainingPanel.vue` | AI训练区主体 |
| `frontend-agent/src/components/assistant/SmartTagEditor.vue` | 智能标注编辑面板 |
| `frontend-agent/src/components/assistant/QualityFeedback.vue` | 质量反馈面板 |
| `frontend-agent/src/components/assistant/KnowledgeContribute.vue` | 知识贡献面板 |
| `frontend-agent/src/components/assistant/UsageStats.vue` | 使用统计面板 |
#### 13.4 前端修改文件
| 文件路径 | 修改范围 | 说明 |
|----------|----------|------|
| `frontend-agent/src/api/wingman.ts` | 扩展 | 新增 4 个 API 调用函数 |
| `frontend-agent/src/views/Workspace.vue` | 改造 | 三栏宽度调整;右栏模式切换 class 绑定;引入 usePanelMode |
| `frontend-agent/src/styles/global.css` | 改造 | CSS 变量体系更新 |
| `frontend-agent/src/components/chat/ChatArea.vue` | 改造 | 新增 ReplySuggestArea 层;TroubleshootBar 默认折叠 |
| `frontend-agent/src/components/chat/UserInfoBar.vue` | 小幅修改 | 详情默认折叠 |
| `frontend-agent/src/components/chat/TroubleshootBar.vue` | 小幅修改 | 默认折叠为图标条 |
| `frontend-agent/src/components/chat/ReplyBox.vue` | 大幅改造 | 工具栏重构为左右分区;新增 AI 工具按钮;集成 useAutoComplete;新增浮层组件引用 |
| `frontend-agent/src/components/assistant/AiAssistantPanel.vue` | 全面重构 | 移除回复前功能;改为 PanelModeToggle + AiTrainingPanel |
| `frontend-agent/src/components/conversation/TodoPanel.vue` | 小幅修改 | max-height 260px → 220px |
#### 13.5 前端删除文件
| 文件路径 | 原因 |
|----------|------|
| `frontend-agent/src/components/chat/AiRecommendInline.vue` | 功能被 AiRecommendBar.vue 替代 |
| `frontend-agent/src/components/assistant/AiSuggestReply.vue` | 功能合并到 AiRecommendBar.vue |
| `frontend-agent/src/components/assistant/QuickReplyPanel.vue` | 改造为 QuickReplyBar.vue(改名+横向布局) |
---
### 14. 开发计划
#### Phase 1: 后端 WingmanService 扩展(预计 1.5 天)
- [ ] 新建 `schemas/wingman_assist.py` — Pydantic 模型
- [ ] 改造 `wingman_service.py``_call_wingman_api()` 新增 temperature 参数
- [ ] 新增 4 个方法 + 4 个 system prompt 常量
- [ ] 扩展 `wingman.py` — 新增 4 个端点,引入 Pydantic 模型
- [ ] 后端单元测试:每个端点正常 + 降级场景
#### Phase 2: 前端 API 层 + Composable(预计 1 天)
- [ ] 新建 `types/wingman-assist.ts` — 类型定义
- [ ] 扩展 `api/wingman.ts` — 4 个 API 函数
- [ ] 新建 `useAutoComplete.ts` — debounce + AbortController + ghost text 管理
- [ ] 新建 `useAiTextTools.ts` — 语气/润色/改写统一调用
- [ ] 新建 `usePanelMode.ts` — 模式切换
#### Phase 3: ReplyBox 工具栏 + AI 辅助组件(预计 2 天)
- [ ] `ReplyBox.vue` 工具栏重构为左右分区
- [ ] `GhostTextOverlay.vue` — mirror div + 幽灵文字渲染
- [ ] `ToneAdjustPopover.vue` — 语气选择 + 原文/改写文对比
- [ ] `PolishPanel.vue` — 精修面板
- [ ] `RewritePanel.vue` — 改写选择面板
- [ ] 键盘快捷键:Tab 接受补齐、Esc 清除
#### Phase 4: 布局重构 + 回复建议区(预计 2 天)
- [ ] `global.css` CSS 变量更新
- [ ] `Workspace.vue` 三栏宽度调整 + 模式切换 class
- [ ] `ChatArea.vue` 新增 ReplySuggestArea 层
- [ ] `ReplySuggestArea.vue` — 容器组件
- [ ] `AiRecommendBar.vue` — 合并 AiRecommendInline + AiSuggestReply
- [ ] `QuickReplyBar.vue` — 横向 2 层布局 + 数字快捷键
- [ ] `UserInfoBar.vue` 详情默认折叠
- [ ] `TroubleshootBar.vue` 默认折叠为图标条
#### Phase 5: 右栏训练区 + 模式切换(预计 1.5 天)
- [ ] `AiAssistantPanel.vue` 全面重构
- [ ] `PanelModeToggle.vue` — 正常/放大切换
- [ ] `AiTrainingPanel.vue` — 训练区主体
- [ ] `SmartTagEditor.vue` — 智能标注(合并原"推荐排查流程"+ 标注)
- [ ] `QualityFeedback.vue` — 质量反馈面板
- [ ] `KnowledgeContribute.vue` — 知识贡献面板
- [ ] `UsageStats.vue` — 使用统计面板
#### Phase 6: 功能清理 + 联调测试(预计 1 天)
- [ ] 删除 `AiRecommendInline.vue``AiSuggestReply.vue`
- [ ] 重命名 `QuickReplyPanel.vue``QuickReplyBar.vue`
- [ ] 端到端测试:补齐/语气/润色/改写全流程
- [ ] 右栏放大/缩小切换测试
- [ ] 回复建议区展开/缩回动画测试
- [ ] 响应式断点验证(1280px / 1024px
- [ ] Dify 不可用时的降级测试
**总预估**: 9 天
---
### 15. 风险与缓解
| 风险 | 概率 | 影响 | 缓解措施 |
|------|------|------|---------|
| 幽灵文字定位不准(mirror div 坐标计算误差) | 中 | 中 | 参照 VS Code textarea-caret-position 方案;增加 padding/font 精确复制逻辑;添加回退:定位不准时降级为底部建议条 |
| 补齐 API 延迟 > 1.5s 影响体验 | 中 | 中 | 前端 1.5s 超时取消请求;Dify prompt 精简(max_length 限制 80 字);考虑后续迭代引入流式 SSE |
| Dify Wingman Agent 不支持多 temperature | 低 | 高 | 改造前先验证 Dify API 是否传递 temperature 参数;若不支持,在后端不传 temperature(使用 Dify 默认值),通过 system prompt 控制创造性 |
| 右栏放大模式下中栏组件状态丢失 | 低 | 中 | 使用 CSS 隐藏而非 v-if 销毁;组件实例保持存活;切回时立即可见 |
| 快速回复从竖向改为横向,三层导航体验下降 | 中 | 中 | 数字快捷键固定 2 层(L1: Alt+1~7, L2: 1~9),键盘操作不依赖鼠标;L3 条目列表在选中 L2 后弹出 |
| 坐席习惯右栏快速回复,移位后找不到 | 中 | 低 | 工具栏保留"快回"入口;输入框为空时回复建议区自动展开;首次使用引导提示 |
| AiAssistantPanel 全面重构影响现有草稿/摘要/标注功能 | 中 | 高 | 分步重构:先新增训练区组件,再移除旧功能;保持 WingmanService 后端不变,仅前端组件重构;保留 git 分支可回滚 |
| RAGFlow 知识库检索延迟影响改写响应 | 中 | 低 | 知识库检索设 3s 超时;超时降级为仅基于对话上下文生成 2 个版本;前端显示"知识库检索中"提示 |
---
## 附录:与主架构文档的映射关系
| 本文档章节 | 主文档对应位置 |
|-----------|-------------|
| §1 架构概述 | 主文档 §8 AI Wingman 设计(更新) |
| §2 后端架构 | 主文档 §15.8 坐席端 AI 辅助消息框(新增) |
| §3 前端架构 | 主文档 §7.2 坐席工作台模块(更新) |
| §4 布局重构 | 主文档 §7.2 坐席工作台模块(更新) |
| §7 API 规范 | 主文档 §14 API设计规范(引用) |
| §11 类图 | 主文档 §15.8(引用) |
| §12 时序图 | 主文档 §15.8(引用) |
---
## 📌 增量变更记录(不破坏 v1.0 原结构)
### 2026-08-03 — #134 坐席端会话状态 Tab 顺序调整 + 默认显示待处理
**关联 PRD**: `docs/01-产品文档/04-坐席工作台/PRD-REQ-坐席-009-会话状态Tab筛选-v1.0.md`
**关联任务**: `docs/07-项目管理/任务说明书/任务说明书-134-坐席端左栏会话状态Tab顺序调整+默认显示待处理.md`
**关联 commit**: `566bb46 feat(agent): 坐席端左栏会话状态 Tab 顺序调整 + 默认显示待处理 (#134)`
**关联原型**: `docs/01-产品文档/04-坐席工作台/原型-REQ-坐席-000-坐席工作台-v1.2.html`
**本技术方案相关位置**(已在 §3.1 组件架构图同步):
- `Workspace.vue``ConversationList.vue` 加入了"筛选 Tab:待处理 → 进行中 → 已完成 → 全部(默认激活"待处理")"的语义说明
**要点**
1. **仅顺序 + 默认激活**两处变更,不重构;不修改 store / 不修改样式 / 不修改 `activeFilter` 的 4 个 key 名
2. 数据分区(myConversations / colleagueConversations / historyConversations)不变,按 status 映射:`pending→queued``active→serving/ai_handling``done→resolved`
3. 「同事会话」在所有筛选 Tab 下都不显示(store 自然隔离);仅「全部」Tab 可见 — 用户确认接受
4. 部署后服务端验证:main chunk hash 由 `index-BDGZ_gcJ.js``index-BK_U7e10.js`jumpserver-V2 全程免登录(cache 19.7h 内有效);备份链 `dist.bak.0803_pre133` 完整保留
5. 用户无痕模式验证(2026-08-03 17:48)确认生效
**未在此文档中重写的子主题**(保持原结构,避免冗余):
- 完整 7 段需求 / 10 项验收标准 → 见 PRD #009
- API 端点(如有) → 见 §7 现有 API 设计(未变更)
- 类图 / 时序图 → 不变,见原文档 §11/§12
---
> **文档结束** | 版本 v1.0 | 2026-07-11 | 宋献 (Simon)