Merge branch 'main' into test
This commit is contained in:
326
Html/apps/_docs/ase_engine_data_sync.md
Normal file
326
Html/apps/_docs/ase_engine_data_sync.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# ASEngine 数据同步机制文档
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
本文档介绍了如何通过 `learning_progess_process.py` 实现与 ASEngine(大模型框架后端)之间的运行时数据下载和上传。该机制为客户端与大模型服务之间提供了可靠的数据同步方案,适用于需要在不同服务之间共享和同步用户数据的场景。
|
||||||
|
|
||||||
|
## 2. 使用场景
|
||||||
|
|
||||||
|
### 2.1 核心场景
|
||||||
|
|
||||||
|
- **用户学习进度同步**:将用户在客户端的学习进度同步到大模型框架后端,确保跨设备访问一致性
|
||||||
|
- **对话历史管理**:从大模型框架获取用户对话历史,用于客户端展示和分析
|
||||||
|
- **多服务数据共享**:在客户端应用与大模型服务之间建立数据桥接,实现数据互通
|
||||||
|
- **分布式系统协作**:支持多个服务实例之间的数据同步,确保系统一致性
|
||||||
|
|
||||||
|
### 2.2 扩展场景
|
||||||
|
|
||||||
|
- **离线数据同步**:支持在网络恢复后进行数据同步
|
||||||
|
- **数据备份与恢复**:通过上传/下载机制实现数据备份和恢复。
|
||||||
|
- **多租户数据隔离**:通过 namespace_token 实现不同租户数据隔离
|
||||||
|
|
||||||
|
## 3. 实现原理
|
||||||
|
|
||||||
|
### 3.1 系统架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||||
|
│ 客户端应用 │ │ ASEngine │
|
||||||
|
│ (learning_progess_process.py) │ (大模型框架后端) │
|
||||||
|
├─────────────────────────┤ ├─────────────────────────┤
|
||||||
|
│ 1. get_multiagents_dialog_list │ ←─── 下载数据 ──── │ /api/sync/download │
|
||||||
|
│ 2. upload_learning_progress_to_cloud │ ──── 上传数据 ────→ │ /api/sync/upload │
|
||||||
|
└─────────────────────────┘ └─────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 数据流向
|
||||||
|
|
||||||
|
1. **数据下载流程**:
|
||||||
|
|
||||||
|
- 客户端调用 `get_multiagents_dialog_list()` 方法
|
||||||
|
- 发送 POST 请求到 ASEngine 的 `/api/sync/download` 接口
|
||||||
|
- 获取用户对话历史数据
|
||||||
|
- 数据用于本地处理或存储
|
||||||
|
2. **数据上传流程**:
|
||||||
|
|
||||||
|
- 客户端调用 `upload_learning_progress_to_cloud()` 方法
|
||||||
|
- 序列化待上传的数据(处理特殊类型如 datetime)
|
||||||
|
- 发送 POST 请求到 ASEngine 的 `/api/sync/upload` 接口
|
||||||
|
- 接收上传结果并记录日志
|
||||||
|
|
||||||
|
## 4. 核心功能说明
|
||||||
|
|
||||||
|
### 4.1 数据下载功能
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_multiagents_dialog_list(user_id: str) -> List[Dict]:
|
||||||
|
"""
|
||||||
|
从 MultiAgents 框架获取用户对话列表
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- user_id: 用户ID
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- Dict[List]: 对话列表(key 为 History database)
|
||||||
|
"""
|
||||||
|
# 实现细节...
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能特点**:
|
||||||
|
|
||||||
|
- 从配置中动态获取 ASEngine URL 和认证信息
|
||||||
|
- 支持超时控制(10秒)
|
||||||
|
- 完善的错误处理和日志记录
|
||||||
|
- 返回结构化数据,便于后续处理
|
||||||
|
|
||||||
|
### 4.2 数据上传功能
|
||||||
|
|
||||||
|
```python
|
||||||
|
def upload_learning_progress_to_cloud(progress: Dict) -> Dict:
|
||||||
|
"""
|
||||||
|
将学习进度上传到云服务
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- progress: 学习进度数据字典
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- Dict: 上传结果,包含success和message字段
|
||||||
|
"""
|
||||||
|
# 实现细节...
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能特点**:
|
||||||
|
|
||||||
|
- 自动处理数据序列化,支持复杂数据类型
|
||||||
|
- 完善的错误处理机制
|
||||||
|
- 详细的日志记录,便于问题排查
|
||||||
|
- 返回标准化结果格式
|
||||||
|
|
||||||
|
### 4.3 辅助功能
|
||||||
|
|
||||||
|
#### 4.3.1 学习进度保存
|
||||||
|
|
||||||
|
```python
|
||||||
|
def save_learning_progress(chatmanager, user_id: str) -> bool:
|
||||||
|
"""
|
||||||
|
保存用户学习进度到MongoDB
|
||||||
|
"""
|
||||||
|
# 实现细节...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.3.2 学习进度加载
|
||||||
|
|
||||||
|
```python
|
||||||
|
def load_learning_progress(user_id: str, material_id: str, chapter_name: str, lesson_name: str) -> Dict:
|
||||||
|
"""
|
||||||
|
加载用户学习进度
|
||||||
|
"""
|
||||||
|
# 实现细节...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 配置方法
|
||||||
|
|
||||||
|
### 5.1 环境配置
|
||||||
|
|
||||||
|
在 Flask 应用的配置中添加以下配置项:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ASEngine 配置
|
||||||
|
ASE_ENGINE_URL = "https://your-ase-engine-url.com" # ASEngine 服务地址
|
||||||
|
ASE_ENGINE_URL_TOKEN = "your-namespace-token" # 命名空间认证令牌
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 依赖安装
|
||||||
|
|
||||||
|
确保项目中已安装以下依赖:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install requests # HTTP 请求库
|
||||||
|
pip install pymongo # MongoDB 驱动(如果使用MongoDB存储)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 使用示例
|
||||||
|
|
||||||
|
### 6.1 数据下载示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
from apps.services.course_services.learning_progess_process import get_multiagents_dialog_list
|
||||||
|
|
||||||
|
# 获取用户对话历史
|
||||||
|
dialog_list = get_multiagents_dialog_list(user_id="user123")
|
||||||
|
|
||||||
|
# 处理对话数据
|
||||||
|
his_data = dialog_list.get('data', [])
|
||||||
|
for dialog in his_data:
|
||||||
|
# 处理单个对话
|
||||||
|
print(f"对话ID: {dialog.get('id')}, 内容: {dialog.get('content')}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 数据上传示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
from apps.services.course_services.learning_progess_process import upload_learning_progress_to_cloud
|
||||||
|
|
||||||
|
# 准备待上传的数据
|
||||||
|
progress_data = {
|
||||||
|
"user_id": "user123",
|
||||||
|
"multiagents_dialogs": {
|
||||||
|
"databases": {...}, # 对话数据库
|
||||||
|
"global_databases": {...}, # 全局数据库
|
||||||
|
"session_info_out": {...} # 会话信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 上传数据到 ASEngine
|
||||||
|
result = upload_learning_progress_to_cloud(progress_data)
|
||||||
|
|
||||||
|
# 处理上传结果
|
||||||
|
if result["success"]:
|
||||||
|
print("数据上传成功")
|
||||||
|
else:
|
||||||
|
print(f"数据上传失败: {result['message']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 完整流程示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
from apps.services.course_services.learning_progess_process import (
|
||||||
|
get_multiagents_dialog_list,
|
||||||
|
save_learning_progress,
|
||||||
|
load_learning_progress,
|
||||||
|
upload_learning_progress_to_cloud
|
||||||
|
)
|
||||||
|
|
||||||
|
# 1. 从 ASEngine 获取对话历史
|
||||||
|
dialog_list = get_multiagents_dialog_list(user_id="user123")
|
||||||
|
|
||||||
|
# 2. 保存到本地 MongoDB
|
||||||
|
save_learning_progress(chatmanager, user_id="user123")
|
||||||
|
|
||||||
|
# 3. 从本地加载学习进度
|
||||||
|
progress = load_learning_progress(
|
||||||
|
user_id="user123",
|
||||||
|
material_id="material456",
|
||||||
|
chapter_name="第1章",
|
||||||
|
lesson_name="第1节"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. 将学习进度上传回 ASEngine
|
||||||
|
if progress["exists"]:
|
||||||
|
upload_result = upload_learning_progress_to_cloud(progress["data"])
|
||||||
|
print(f"上传结果: {upload_result}")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 代码结构与扩展
|
||||||
|
|
||||||
|
### 7.1 核心模块
|
||||||
|
|
||||||
|
| 模块 | 功能描述 | 文件位置 |
|
||||||
|
| --------------- | -------------------------- | ------------------------------------------------------------- |
|
||||||
|
| 数据同步服务 | 处理与 ASEngine 的数据交互 | `apps/services/course_services/learning_progess_process.py` |
|
||||||
|
| JSON 序列化工具 | 处理复杂数据类型序列化 | `apps/utils/json_serializer.py` |
|
||||||
|
| 配置管理 | 管理 ASEngine 连接配置 | `apps/config.py` |
|
||||||
|
|
||||||
|
### 7.2 扩展建议
|
||||||
|
|
||||||
|
1. **添加重试机制**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def upload_with_retry(progress: Dict, max_retries: int = 3) -> Dict:
|
||||||
|
for i in range(max_retries):
|
||||||
|
result = upload_learning_progress_to_cloud(progress)
|
||||||
|
if result["success"]:
|
||||||
|
return result
|
||||||
|
time.sleep(1) # 指数退避算法可进一步优化
|
||||||
|
return {"success": False, "message": f"上传失败,已重试{max_retries}次"}
|
||||||
|
```
|
||||||
|
2. **添加数据校验**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def validate_progress_data(progress: Dict) -> bool:
|
||||||
|
"""验证进度数据格式"""
|
||||||
|
required_fields = ["user_id", "multiagents_dialogs"]
|
||||||
|
for field in required_fields:
|
||||||
|
if field not in progress:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
3. **支持异步操作**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
async def async_upload_learning_progress(progress: Dict) -> Dict:
|
||||||
|
"""异步上传学习进度"""
|
||||||
|
# 异步实现
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 最佳实践
|
||||||
|
|
||||||
|
### 8.1 错误处理
|
||||||
|
|
||||||
|
- 始终检查 API 返回结果的 `success` 字段
|
||||||
|
- 实现适当的日志记录,便于问题排查
|
||||||
|
- 对网络异常和超时进行合理处理
|
||||||
|
|
||||||
|
### 8.2 性能优化
|
||||||
|
|
||||||
|
- 避免频繁的小数据上传,考虑批量处理
|
||||||
|
- 对大文件进行压缩后上传
|
||||||
|
- 合理设置超时时间,避免长时间阻塞
|
||||||
|
|
||||||
|
### 8.3 安全性
|
||||||
|
|
||||||
|
- 确保 ASEngine URL 和令牌安全存储,避免硬编码
|
||||||
|
- 使用 HTTPS 协议进行数据传输
|
||||||
|
- 对敏感数据进行加密处理
|
||||||
|
|
||||||
|
### 8.4 监控与告警
|
||||||
|
|
||||||
|
- 添加数据同步成功率监控
|
||||||
|
- 对同步失败情况进行告警
|
||||||
|
- 记录详细的操作日志,便于审计
|
||||||
|
|
||||||
|
## 9. 后续项目参考建议
|
||||||
|
|
||||||
|
### 9.1 架构设计
|
||||||
|
|
||||||
|
- **分层设计**:将数据同步逻辑与业务逻辑分离,提高代码可维护性
|
||||||
|
- **接口标准化**:定义统一的数据格式和接口规范,便于扩展
|
||||||
|
- **依赖注入**:使用依赖注入模式,提高代码可测试性
|
||||||
|
|
||||||
|
### 9.2 代码实现
|
||||||
|
|
||||||
|
- **模块化**:将不同功能模块拆分为独立函数或类
|
||||||
|
- **类型注解**:使用 Type Hints 提高代码可读性和类型安全性
|
||||||
|
- **文档完善**:为每个函数添加详细的文档字符串
|
||||||
|
- **测试覆盖**:编写单元测试和集成测试,确保功能正确性
|
||||||
|
|
||||||
|
### 9.3 扩展功能
|
||||||
|
|
||||||
|
- **支持多种数据格式**:如 JSON、Protobuf 等
|
||||||
|
- **添加数据版本控制**:支持不同版本数据的兼容处理
|
||||||
|
- **实现增量同步**:只同步变化的数据,提高同步效率
|
||||||
|
- **支持断点续传**:对于大文件上传实现断点续传功能
|
||||||
|
|
||||||
|
## 10. 总结
|
||||||
|
|
||||||
|
本文档介绍了基于 `learning_progess_process.py` 实现的 ASEngine 数据同步机制,包括使用场景、实现原理、核心功能、配置方法和使用示例。该机制为客户端与大模型服务之间提供了可靠的数据同步方案,可作为后续项目的参考模板。
|
||||||
|
|
||||||
|
通过遵循本文档中的最佳实践和扩展建议,可以构建更加健壮、高效和可扩展的数据同步系统,满足不同场景下的数据同步需求。
|
||||||
|
|
||||||
|
## 11. 版本历史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 描述 | 作者 |
|
||||||
|
| ---- | ---------- | ------------ | ------------ |
|
||||||
|
| v1.0 | 2025-12-10 | 初始版本 | 系统自动生成 |
|
||||||
|
| v1.1 | YYYY-MM-DD | 添加异步支持 | XXX |
|
||||||
|
| v1.2 | YYYY-MM-DD | 实现增量同步 | XXX |
|
||||||
|
|
||||||
|
## 12. 参考资料
|
||||||
|
|
||||||
|
- [Flask 官方文档](https://flask.palletsprojects.com/)
|
||||||
|
- [Requests 库文档](https://requests.readthedocs.io/)
|
||||||
|
- [ASEngine API 文档](https://your-ase-engine-docs.com/)
|
||||||
@@ -86,7 +86,6 @@ class AgentNamespace(Namespace):
|
|||||||
chatmanager.function_manager.add_function(next_chapter, {'user_uuid': user_uuid})
|
chatmanager.function_manager.add_function(next_chapter, {'user_uuid': user_uuid})
|
||||||
|
|
||||||
if not chatmanager.restart:# 尝试加载用户学习进度
|
if not chatmanager.restart:# 尝试加载用户学习进度
|
||||||
self.app.logger.info(f"尝试加载用户学习进度: {user_id}, {course_id}, {chapter_name}, {lesson_name}")
|
|
||||||
progress_result = load_learning_progress(user_id, course_id, chapter_name, lesson_name)
|
progress_result = load_learning_progress(user_id, course_id, chapter_name, lesson_name)
|
||||||
need_skip_chapters = 0
|
need_skip_chapters = 0
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ class AgentNamespace(Namespace):
|
|||||||
# 根据scores的数量确定需要跳过的章节数
|
# 根据scores的数量确定需要跳过的章节数
|
||||||
need_skip_chapters = len(progress['scores'])
|
need_skip_chapters = len(progress['scores'])
|
||||||
chatmanager.scores = progress['scores']
|
chatmanager.scores = progress['scores']
|
||||||
self.app.logger.info(f"恢复用户学习进度: {user_id}, 已完成 {need_skip_chapters} 个章节")
|
|
||||||
|
|
||||||
# 恢复聊天历史
|
# 恢复聊天历史
|
||||||
if 'chat_historys' in progress:
|
if 'chat_historys' in progress:
|
||||||
@@ -113,7 +111,6 @@ class AgentNamespace(Namespace):
|
|||||||
# 使用默认值,确保有完整的结构
|
# 使用默认值,确保有完整的结构
|
||||||
chatmanager.chat_historys = progress_result['data']['chat_historys']
|
chatmanager.chat_historys = progress_result['data']['chat_historys']
|
||||||
chatmanager.scores = progress_result['data']['scores']
|
chatmanager.scores = progress_result['data']['scores']
|
||||||
self.app.logger.info(f"未找到学习记录,使用默认值: {progress_result['message']}")
|
|
||||||
|
|
||||||
fmd,fmdp,fsp = load_full_markdown_file(course_id, chapter_name, lesson_name)
|
fmd,fmdp,fsp = load_full_markdown_file(course_id, chapter_name, lesson_name)
|
||||||
user_uuid2ase_client[user_uuid] = HSAEngineClient(current_app.config["ASE_ENGINE_URL"], current_app.config["ASE_ENGINE_NAMESPACE"],id=user_id, chatmanager = chatmanager)
|
user_uuid2ase_client[user_uuid] = HSAEngineClient(current_app.config["ASE_ENGINE_URL"], current_app.config["ASE_ENGINE_NAMESPACE"],id=user_id, chatmanager = chatmanager)
|
||||||
@@ -186,10 +183,8 @@ class AgentNamespace(Namespace):
|
|||||||
|
|
||||||
def on_language(self, language):
|
def on_language(self, language):
|
||||||
ex = current_app.extensions
|
ex = current_app.extensions
|
||||||
self.app.logger.info(f"Language changed to: {language}")
|
|
||||||
|
|
||||||
def on_message(self, data):
|
def on_message(self, data):
|
||||||
self.app.logger.info(f"Message from client: {data}")
|
|
||||||
ex = current_app.extensions
|
ex = current_app.extensions
|
||||||
chatmanager = ex["user_uuid2chatmanager"]
|
chatmanager = ex["user_uuid2chatmanager"]
|
||||||
uuid = session.get('user_uuid')
|
uuid = session.get('user_uuid')
|
||||||
@@ -201,11 +196,9 @@ class AgentNamespace(Namespace):
|
|||||||
chatmanager[uuid].chat_historys.append({'role': 'user', 'content': data['data']})
|
chatmanager[uuid].chat_historys.append({'role': 'user', 'content': data['data']})
|
||||||
|
|
||||||
if data['type'] == 'function': # 在这里 前端会发送允许执行的function与参数
|
if data['type'] == 'function': # 在这里 前端会发送允许执行的function与参数
|
||||||
self.app.logger.info(f"function_call: {data['data']}")
|
|
||||||
d = data['data']
|
d = data['data']
|
||||||
res = chatmanager[uuid].function_call(d['data']['name'], d['data'].get('args', {}))
|
res = chatmanager[uuid].function_call(d['data']['name'], d['data'].get('args', {}))
|
||||||
d['data'] = res.to_dict()
|
d['data'] = res.to_dict()
|
||||||
self.app.logger.info(f"function_call_res: {d['data']}")
|
|
||||||
emit(d['route'], d, room=uuid, namespace='/agent')
|
emit(d['route'], d, room=uuid, namespace='/agent')
|
||||||
chatmanager[uuid].chat_historys.append({'role': 'user', 'content': data['data']})
|
chatmanager[uuid].chat_historys.append({'role': 'user', 'content': data['data']})
|
||||||
user_uuid2ase_client[uuid].send_text(d['route'], data['data'])
|
user_uuid2ase_client[uuid].send_text(d['route'], data['data'])
|
||||||
@@ -216,10 +209,9 @@ class AgentNamespace(Namespace):
|
|||||||
uuid = session.get('user_uuid')
|
uuid = session.get('user_uuid')
|
||||||
user_uuid2ase_client = ex["user_uuid2ase_client"]
|
user_uuid2ase_client = ex["user_uuid2ase_client"]
|
||||||
res = user_uuid2ase_client[uuid].send_voice('voice_in', voice_data['data'])
|
res = user_uuid2ase_client[uuid].send_voice('voice_in', voice_data['data'])
|
||||||
self.app.logger.info(f"Send voicce to route 'voice_in' success: {res}")
|
|
||||||
|
|
||||||
def on_initiative(self,data): # 主动call
|
def on_initiative(self,data): # 主动call
|
||||||
self.app.logger.info("User active function call")
|
|
||||||
ex = current_app.extensions
|
ex = current_app.extensions
|
||||||
uuid = session.get('user_uuid')
|
uuid = session.get('user_uuid')
|
||||||
manager = ex["user_uuid2chatmanager"][uuid]
|
manager = ex["user_uuid2chatmanager"][uuid]
|
||||||
@@ -247,7 +239,6 @@ class AgentNamespace(Namespace):
|
|||||||
chatmanager = user_uuid2chatmanager.get(uuid)
|
chatmanager = user_uuid2chatmanager.get(uuid)
|
||||||
if chatmanager:
|
if chatmanager:
|
||||||
save_result = save_learning_progress(chatmanager, user_id)
|
save_result = save_learning_progress(chatmanager, user_id)
|
||||||
self.app.logger.info(f"保存学习进度结果: {save_result}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.app.logger.error(f"保存学习进度时发生异常: {str(e)}")
|
self.app.logger.error(f"保存学习进度时发生异常: {str(e)}")
|
||||||
|
|||||||
Reference in New Issue
Block a user