- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
284 lines
6.8 KiB
Markdown
284 lines
6.8 KiB
Markdown
# 与课程对话功能实施总结
|
||
|
||
> 完成时间:2025-10-14
|
||
> 功能状态:✅ 已完成实施
|
||
|
||
## 📋 功能概述
|
||
|
||
基于 Dify 对话流实现了与课程的智能对话功能,用户可以在课程中心点击"对话"按钮,与课程内容进行智能问答互动。
|
||
|
||
## 🎯 技术方案
|
||
|
||
### 架构设计
|
||
|
||
```
|
||
前端 chat-course.vue
|
||
↓ (SSE)
|
||
后端 /api/v1/course/chat
|
||
↓ (HTTP Stream)
|
||
Dify 对话流 API
|
||
```
|
||
|
||
### 核心特性
|
||
|
||
1. **流式响应**:使用 SSE(Server-Sent Events)实现实时对话
|
||
2. **会话管理**:conversation_id 由前端管理,支持多轮对话
|
||
3. **无持久化**:对话历史由 Dify 托管,系统不存储
|
||
4. **纯文本**:当前版本仅支持文本对话
|
||
|
||
## 📁 已修改文件
|
||
|
||
### 后端
|
||
|
||
1. **配置文件**:`kaopeilian-backend/app/core/config.py`
|
||
- 添加 `DIFY_COURSE_CHAT_API_KEY = "app-lJzD6COkL8z7Eez8t6ZrYoJS"`
|
||
|
||
2. **API 接口**:`kaopeilian-backend/app/api/v1/course_chat.py`(新建)
|
||
- `POST /api/v1/course/chat` - 与课程对话接口
|
||
- 实现 SSE 流式代理
|
||
- 事件转换:Dify → 前端友好格式
|
||
|
||
3. **路由注册**:`kaopeilian-backend/app/api/v1/__init__.py`
|
||
- 注册 `course_chat_router` 到 `/course` 前缀
|
||
|
||
### 前端
|
||
|
||
1. **API 封装**:`kaopeilian-frontend/src/api/courseChat.ts`(新建)
|
||
- `courseChatApi.sendMessage()` - 发送消息并返回 ReadableStream
|
||
- TypeScript 类型定义:`CourseChatEvent`
|
||
|
||
2. **对话页面**:`kaopeilian-frontend/src/views/trainee/chat-course.vue`
|
||
- 删除 Coze 集成代码
|
||
- 改用 Dify 对话流
|
||
- 前端管理 `conversationId`
|
||
- SSE 事件处理(conversation_started / message_content / message_end)
|
||
|
||
### 测试
|
||
|
||
1. **测试脚本**:`test_course_chat.py`(新建)
|
||
- 测试登录 → 首次对话 → 续接对话
|
||
- 验证 SSE 事件流
|
||
- 验证会话管理
|
||
|
||
## 🔄 SSE 事件流程
|
||
|
||
### Dify 原始事件
|
||
|
||
```
|
||
workflow_started → node_finished → workflow_finished → message_end
|
||
```
|
||
|
||
### 后端转换后的事件
|
||
|
||
```json
|
||
// 1. 会话开始(首次对话)
|
||
{"event": "conversation_started", "conversation_id": "xxx"}
|
||
|
||
// 2. 消息块(逐字返回,实现打字机效果)
|
||
{"event": "message_chunk", "chunk": "这"}
|
||
{"event": "message_chunk", "chunk": "门"}
|
||
{"event": "message_chunk", "chunk": "课"}
|
||
...
|
||
|
||
// 3. 消息结束
|
||
{"event": "message_end"}
|
||
|
||
// 4. 错误(如有)
|
||
{"event": "error", "message": "错误信息"}
|
||
```
|
||
|
||
## 📊 数据流
|
||
|
||
### 首次对话
|
||
|
||
```
|
||
用户输入问题
|
||
↓
|
||
前端调用 courseChatApi.sendMessage({course_id, query})
|
||
↓
|
||
后端转发到 Dify (无 conversation_id)
|
||
↓
|
||
Dify 创建新会话
|
||
↓
|
||
SSE: conversation_started → 前端保存 conversation_id
|
||
SSE: message_content → 前端显示答案
|
||
SSE: message_end → 对话完成
|
||
```
|
||
|
||
### 续接对话
|
||
|
||
```
|
||
用户输入问题
|
||
↓
|
||
前端调用 courseChatApi.sendMessage({course_id, query, conversation_id})
|
||
↓
|
||
后端转发到 Dify (带 conversation_id)
|
||
↓
|
||
Dify 基于上下文回答
|
||
↓
|
||
SSE: message_content → 前端显示答案
|
||
SSE: message_end → 对话完成
|
||
```
|
||
|
||
## 🧪 测试步骤
|
||
|
||
### 1. 启动服务
|
||
|
||
```bash
|
||
# 后端
|
||
cd kaopeilian-backend
|
||
docker-compose up -d
|
||
|
||
# 前端
|
||
cd kaopeilian-frontend
|
||
npm run dev
|
||
```
|
||
|
||
### 2. 运行测试脚本
|
||
|
||
```bash
|
||
python test_course_chat.py
|
||
```
|
||
|
||
### 3. 手动测试
|
||
|
||
1. 登录系统
|
||
2. 进入课程中心:http://localhost:3001/trainee/course-center
|
||
3. 点击课程卡片的"对话"按钮
|
||
4. 输入问题并发送
|
||
5. 验证:
|
||
- AI 回复显示正常
|
||
- 可以进行多轮对话
|
||
- 点击"清空对话"后会话重置
|
||
|
||
## 🎨 UI 特性
|
||
|
||
- ✅ 欢迎界面(首次进入时显示)
|
||
- ✅ 快速提问(预设问题点击发送)
|
||
- ✅ 消息加载动画(三个点跳动)
|
||
- ✅ 消息复制功能
|
||
- ✅ 消息收藏功能
|
||
- ✅ 侧边栏(知识要点、对话历史)
|
||
- ✅ 响应式设计(移动端适配)
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
### 1. conversation_id 管理
|
||
|
||
- 前端使用 `ref<string>` 保存
|
||
- 页面刷新后丢失(符合"每次进入对话页面都创建新会话"的需求)
|
||
- 点击"清空对话"时重置
|
||
|
||
### 2. 流式打字机效果
|
||
|
||
- ✅ 已实现!Dify streaming 模式支持 `event: message` 逐字返回
|
||
- 前端通过 `message_chunk` 事件逐字追加文本
|
||
- 实现类似 ChatGPT 的实时打字效果
|
||
|
||
### 3. 超时设置
|
||
|
||
- 后端:180 秒(httpx.Timeout)
|
||
- 前端:依赖浏览器默认(通常无限制)
|
||
|
||
### 4. 错误处理
|
||
|
||
- 网络错误:显示友好提示
|
||
- Dify API 错误:记录日志并返回错误事件
|
||
- 解析错误:跳过当前行,继续处理
|
||
|
||
## 📝 API 接口文档
|
||
|
||
### POST /api/v1/course/chat
|
||
|
||
**请求体:**
|
||
|
||
```json
|
||
{
|
||
"course_id": 1,
|
||
"query": "这门课程讲什么?",
|
||
"conversation_id": "可选,续接对话时传入"
|
||
}
|
||
```
|
||
|
||
**响应(SSE):**
|
||
|
||
```
|
||
data: {"event":"conversation_started","conversation_id":"xxx"}
|
||
|
||
data: {"event":"message_content","answer":"这门课程..."}
|
||
|
||
data: {"event":"message_end"}
|
||
```
|
||
|
||
## 🔍 关键代码片段
|
||
|
||
### 后端 SSE 生成
|
||
|
||
```python
|
||
async def generate_stream():
|
||
async with client.stream("POST", url, headers=headers, json=payload) as response:
|
||
async for line in response.aiter_lines():
|
||
if line.startswith("data: "):
|
||
event_data = json.loads(line[6:])
|
||
# 处理事件...
|
||
yield f"data: {json.dumps(frontend_event)}\n\n"
|
||
```
|
||
|
||
### 前端 SSE 消费
|
||
|
||
```typescript
|
||
const stream = await courseChatApi.sendMessage({...})
|
||
const reader = stream.getReader()
|
||
const decoder = new TextDecoder()
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read()
|
||
if (done) break
|
||
|
||
const text = decoder.decode(value)
|
||
// 解析 SSE 事件...
|
||
}
|
||
```
|
||
|
||
## ✅ 验收标准
|
||
|
||
- [x] 后端配置添加完成
|
||
- [x] 后端 API 接口实现
|
||
- [x] 后端路由注册
|
||
- [x] 前端 API 封装
|
||
- [x] 前端页面改造
|
||
- [x] SSE 流式响应正常
|
||
- [x] 会话管理正常
|
||
- [x] 错误处理完善
|
||
- [x] 无 linter 错误
|
||
- [x] 测试脚本创建
|
||
|
||
## 🚀 下一步
|
||
|
||
### 可选优化
|
||
|
||
1. **Markdown 渲染**:如果 Dify 返回 Markdown 格式,前端可添加 Markdown 渲染器
|
||
2. **会话持久化**:如需要持久化历史,可在后端存储 conversation_id 与用户/课程的映射
|
||
3. **实时打字效果**:如 Dify 支持逐字返回,可修改事件处理逻辑
|
||
4. **语音对话**:未来可集成语音输入/输出
|
||
|
||
## 📚 相关文档
|
||
|
||
- [Dify 对话流 API 文档](./Dify对话流API文档.md)
|
||
- [实施计划](../../../------dify---.plan.md)
|
||
- [规范与约定-团队基线](../../规范与约定-团队基线.md)
|
||
- [联调经验汇总](../../联调经验汇总.md)
|
||
|
||
## 🎉 总结
|
||
|
||
成功将与课程对话功能从 Coze 迁移到 Dify 对话流,实现了:
|
||
|
||
✅ 完整的 SSE 流式对话
|
||
✅ 会话持续性(conversation_id 管理)
|
||
✅ 前后端解耦(API 代理模式)
|
||
✅ 良好的错误处理
|
||
✅ 友好的用户界面
|
||
|
||
代码质量:无 linter 错误,代码结构清晰,注释完善。
|
||
|