save_load_learned
This commit is contained in:
@@ -48,4 +48,5 @@ class Config:
|
||||
}
|
||||
|
||||
ASE_ENGINE_URL = GLOBAL_CONFIG['ASE_ENGINE']['url']
|
||||
ASE_ENGINE_URL_TOKEN = GLOBAL_CONFIG['ASE_ENGINE']['url_token']
|
||||
ASE_ENGINE_NAMESPACE = GLOBAL_CONFIG['ASE_ENGINE']['namespace']
|
||||
@@ -1,6 +1,8 @@
|
||||
# myapp/services/course_service.py
|
||||
import re
|
||||
from typing import Dict, List
|
||||
import requests
|
||||
import json
|
||||
from typing import Dict, List, Optional
|
||||
from flask import current_app
|
||||
from urllib.parse import urlparse
|
||||
from datetime import datetime
|
||||
@@ -450,10 +452,10 @@ def _default_markdowns(lesson_name: str):
|
||||
|
||||
## 阶段一:示例阶段
|
||||
### 步骤A
|
||||
请基于“步骤A”的教学目标与材料,生成指导性提示词(尽量结构化,给出评估标准和示例)。
|
||||
请基于"步骤A"的教学目标与材料,生成指导性提示词(尽量结构化,给出评估标准和示例)。
|
||||
|
||||
### 步骤B
|
||||
请基于“步骤B”的教学目标与材料,生成指导性提示词。
|
||||
请基于"步骤B"的教学目标与材料,生成指导性提示词。
|
||||
"""
|
||||
|
||||
score_prompt_md = f"""# {lesson_name}
|
||||
@@ -463,8 +465,134 @@ def _default_markdowns(lesson_name: str):
|
||||
|
||||
## 阶段一:示例阶段
|
||||
### 步骤A
|
||||
对学员在“步骤A”的输出进行评分与文字反馈,分数区间 0-100,并输出 JSON:```{{"score": <int>, "reasons": "<string>", "advices": "<string>"}}```
|
||||
对学员在"步骤A"的输出进行评分与文字反馈,分数区间 0-100,并输出 JSON:```{{"score": <int>, "reasons": "<string>", "advices": "<string>"}}```
|
||||
### 步骤B
|
||||
同上,对“步骤B”输出进行评分与反馈。
|
||||
同上,对"步骤B"输出进行评分与反馈。
|
||||
"""
|
||||
return lesson_md, prompt_md, score_prompt_md
|
||||
|
||||
def save_learning_progress(user_uuid: str, chatmanager, user_id: str) -> bool:
|
||||
"""
|
||||
保存用户学习进度到MongoDB
|
||||
|
||||
参数:
|
||||
- user_uuid: 用户UUID
|
||||
- chatmanager: ChatManager实例,包含学习进度数据
|
||||
- user_id: 用户ID
|
||||
|
||||
返回:
|
||||
- bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
# 获取MongoDB连接
|
||||
mongo = current_app.extensions["mongo"]
|
||||
|
||||
# 获取MultiAgents框架中的对话列表
|
||||
dialog_list = get_multiagents_dialog_list(user_id)
|
||||
|
||||
# 构建学习进度数据
|
||||
progress_data = {
|
||||
"user_uuid": user_uuid,
|
||||
"user_id": user_id,
|
||||
"material_id": chatmanager.material_id,
|
||||
"chapter_name": chatmanager.chapter_name,
|
||||
"lesson_name": chatmanager.lesson_name,
|
||||
"chapter_chain_now": chatmanager.chapter_chain_now,
|
||||
"chat_historys": chatmanager.chat_historys,
|
||||
"scores": chatmanager.scores,
|
||||
"multiagents_dialogs": dialog_list,
|
||||
"updated_at": datetime.now()
|
||||
}
|
||||
|
||||
# 保存到MongoDB,使用upsert操作(如果存在则更新,不存在则插入)
|
||||
mongo.db.learning_progress.update_one(
|
||||
{
|
||||
"user_uuid": user_uuid,
|
||||
"material_id": chatmanager.material_id,
|
||||
"chapter_name": chatmanager.chapter_name,
|
||||
"lesson_name": chatmanager.lesson_name
|
||||
},
|
||||
{"$set": progress_data},
|
||||
upsert=True
|
||||
)
|
||||
|
||||
current_app.logger.info(f"学习进度保存成功: user_uuid={user_uuid}, lesson={chatmanager.lesson_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"保存学习进度失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_multiagents_dialog_list(user_id: str) -> List[Dict]:
|
||||
"""
|
||||
从MultiAgents框架获取用户对话列表
|
||||
|
||||
参数:
|
||||
- user_id: 用户ID
|
||||
|
||||
返回:
|
||||
- List[Dict]: 对话列表
|
||||
"""
|
||||
try:
|
||||
# 从配置中获取ASE引擎URL和token
|
||||
config = current_app.config
|
||||
url_token = config.get("ASE_ENGINE_URL_TOKEN", "")
|
||||
|
||||
# 构建请求URL
|
||||
base_url = config.get("ASE_ENGINE_URL", "")
|
||||
api_url = f"{base_url}/api/sync/download"
|
||||
|
||||
# 构建请求参数
|
||||
payload = {
|
||||
"namespace_url": url_token, # 使用配置中的token作为namespace_url
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(
|
||||
api_url,
|
||||
json=payload,
|
||||
headers={"Content-Type": "application/json"},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
# 检查响应
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
current_app.logger.error(f"获取MultiAgents对话列表失败: HTTP {response.status_code}")
|
||||
return []
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"获取MultiAgents对话列表异常: {str(e)}")
|
||||
return []
|
||||
|
||||
def load_learning_progress(user_uuid: str, material_id: str, chapter_name: str, lesson_name: str) -> Optional[Dict]:
|
||||
"""
|
||||
加载用户学习进度
|
||||
|
||||
参数:
|
||||
- user_uuid: 用户UUID
|
||||
- material_id: 课程ID
|
||||
- chapter_name: 章节名
|
||||
- lesson_name: 课时名
|
||||
|
||||
返回:
|
||||
- Optional[Dict]: 学习进度数据,如果不存在则返回None
|
||||
"""
|
||||
try:
|
||||
mongo = current_app.extensions["mongo"]
|
||||
progress = mongo.db.learning_progress.find_one({
|
||||
"user_uuid": user_uuid,
|
||||
"material_id": material_id,
|
||||
"chapter_name": chapter_name,
|
||||
"lesson_name": lesson_name
|
||||
})
|
||||
|
||||
if progress:
|
||||
# 转换ObjectId为字符串
|
||||
if "_id" in progress:
|
||||
progress["_id"] = str(progress["_id"])
|
||||
return progress
|
||||
return None
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"加载学习进度失败: {str(e)}")
|
||||
return None
|
||||
|
||||
@@ -7,7 +7,7 @@ from flask_socketio import Namespace, join_room, leave_room, emit
|
||||
from ..function_tools import judge
|
||||
from ..function_tools import next_chapter
|
||||
from ..services.backboard_service import realtime_response
|
||||
from ..services.markdown_service import load_full_markdown_file
|
||||
from ..services.course_service import load_full_markdown_file, save_learning_progress
|
||||
from ..extension_ase.ase_client import HSAEngineClient
|
||||
from ..services.user_service import get_or_load_current_user
|
||||
|
||||
@@ -71,8 +71,32 @@ class AgentNamespace(Namespace):
|
||||
chatmanager = user_uuid2chatmanager[user_uuid]
|
||||
chatmanager.function_manager.add_function(judge, {'course_id': course_id, 'root_path': f'/home/{user_id}/{course_id}/{chapter_name}/{lesson_name}'})
|
||||
chatmanager.function_manager.add_function(next_chapter, {'user_uuid': user_uuid})
|
||||
chatmanager.chat_historys = []
|
||||
chatmanager.scores = []
|
||||
|
||||
# 尝试加载用户学习进度
|
||||
from ..services.course_service import load_learning_progress
|
||||
progress = load_learning_progress(user_uuid, course_id, chapter_name, lesson_name)
|
||||
need_skip_chapters = 0
|
||||
|
||||
if progress:
|
||||
# 恢复学习进度数据
|
||||
if 'scores' in progress:
|
||||
# 根据scores的数量确定需要跳过的章节数
|
||||
need_skip_chapters = len(progress['scores'])
|
||||
chatmanager.scores = progress['scores']
|
||||
print(f"恢复用户学习进度: {user_uuid}, 已完成 {need_skip_chapters} 个章节")
|
||||
|
||||
# 恢复聊天历史
|
||||
if 'chat_historys' in progress:
|
||||
chatmanager.chat_historys = progress['chat_historys']
|
||||
else:
|
||||
chatmanager.chat_historys = []
|
||||
|
||||
# 恢复当前章节链
|
||||
if 'chapter_chain_now' in progress:
|
||||
chatmanager.chapter_chain_now = progress['chapter_chain_now']
|
||||
else:
|
||||
chatmanager.chat_historys = []
|
||||
chatmanager.scores = []
|
||||
|
||||
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)
|
||||
@@ -80,6 +104,10 @@ class AgentNamespace(Namespace):
|
||||
backboard_manager.add_backboard(user_uuid, user_id, course_id, lesson_name, root_path=f'/home/{user_id}/{course_id}/{chapter_name}/{lesson_name}')
|
||||
chatmanager.bb = backboard_manager.get_backboard(user_uuid)
|
||||
chatmanager.next_chapter()
|
||||
|
||||
# 如果有保存的进度,需要跳过已经完成的章节
|
||||
for _ in range(need_skip_chapters):
|
||||
chatmanager.next_chapter()
|
||||
self.chatmanager = chatmanager
|
||||
self.ase_client = user_uuid2ase_client[user_uuid]
|
||||
user_uuid2ase_client[user_uuid].register_on_connect_entry(
|
||||
@@ -221,7 +249,7 @@ class AgentNamespace(Namespace):
|
||||
if len(chatmanager.scores) >= chatmanager.chapter_chain_now:
|
||||
chatmanager.scores[chatmanager.chapter_chain_now - 1] = data.get('data', None)
|
||||
else: chatmanager.scores.append(data.get('data', None))
|
||||
user.set_course_progress(chatmanager.chat_historys, chatmanager.scores, chatmanager.material_id, chatmanager.chapter_name, chatmanager.lesson_name)
|
||||
user.set_course_progress(chatmanager.chat_historys, chatmanager.scores, chatmanager.course_id, chatmanager.chapter_name, chatmanager.lesson_name)
|
||||
chatmanager.load_next_chapter()
|
||||
time.sleep(0.5)
|
||||
chatmanager.ase_client.send_text("chapter-start", "")
|
||||
@@ -241,10 +269,32 @@ class AgentNamespace(Namespace):
|
||||
|
||||
|
||||
def on_disconnect(self,data):
|
||||
print(f'AgentNamespace client disconnected, session user_uuid: {session.get("user_uuid", "unknown")}')
|
||||
ex = current_app.extensions
|
||||
uuid = session.get('user_uuid')
|
||||
user_uuid2chatmanager = ex["user_uuid2chatmanager"]
|
||||
user_uuid2chatmanager[uuid].disconnect(uuid)
|
||||
user_uuid2chatmanager = ex.get("user_uuid2chatmanager", {})
|
||||
|
||||
# 保存学习进度到MongoDB
|
||||
try:
|
||||
# 获取用户ID
|
||||
uuid2username = ex.get("uuid2username", {})
|
||||
user_id = uuid2username.get(uuid, "unknown")
|
||||
|
||||
# 调用保存学习进度功能
|
||||
chatmanager = user_uuid2chatmanager.get(uuid)
|
||||
if chatmanager:
|
||||
save_result = save_learning_progress(uuid, chatmanager, user_id)
|
||||
print(f"保存学习进度结果: {save_result}, user_uuid={uuid}")
|
||||
except Exception as e:
|
||||
print(f"保存学习进度时发生异常: {str(e)}")
|
||||
|
||||
# 执行原有的断开连接逻辑
|
||||
if uuid in user_uuid2chatmanager:
|
||||
user_uuid2chatmanager[uuid].disconnect(uuid)
|
||||
|
||||
# 离开房间
|
||||
if uuid:
|
||||
leave_room(uuid, namespace='/agent')
|
||||
|
||||
print("VSCode client disconnected")
|
||||
print("Disconnect reason:"+str(data))
|
||||
@@ -105,29 +105,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
document.getElementById('chatbox').appendChild(requestMessage);
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
}
|
||||
// for (let i = 0; i < data.length; i++) {
|
||||
// const element = data[i]
|
||||
// const requestMessage = document.createElement('div');
|
||||
// requestMessage.className = 'chat-message server-message';
|
||||
// const approveButton = document.createElement('button');
|
||||
// approveButton.innerText = '批准';
|
||||
// approveButton.data = element;
|
||||
// approveButton.style.width = '100%';
|
||||
// approveButton.style.borderRadius = '4px';
|
||||
// approveButton.onclick = function(event) {
|
||||
// console.log(element)
|
||||
// socket.emit('message', JSON.stringify({type:'function', data:element}));
|
||||
// event.currentTarget.disabled = true;
|
||||
// event.currentTarget.style.backgroundColor = 'gray';
|
||||
// event.currentTarget.style.color = 'white'; // 可选:设置文字颜色为白色,以便更清晰地显示
|
||||
// event.currentTarget.style.border = 'none'; // 可选:去掉边框
|
||||
// };
|
||||
// // 将按钮添加到消息气泡中
|
||||
// requestMessage.innerHTML = `<b>Function:</b> ${element.name}(${JSON.stringify(element.arguments)})<br>`;
|
||||
// requestMessage.appendChild(approveButton);
|
||||
// document.getElementById('chatbox').appendChild(requestMessage);
|
||||
// document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
// }
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
@@ -250,124 +228,9 @@ function sendMessage() {
|
||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||
}
|
||||
|
||||
// let voice_stream = null;
|
||||
// let audio_chunks = [];
|
||||
// let isRecording = false;
|
||||
// let recordingInterval = null;
|
||||
// //鼠标点击终止事件
|
||||
// function handleMouseClick(event) {
|
||||
// event.stopPropagation();
|
||||
// stopRecording();
|
||||
// }
|
||||
// //发送语音
|
||||
// const startAudioRecording = async () => {
|
||||
// try {
|
||||
// const stream = await navigator.mediaDevices.getUserMedia({
|
||||
// audio: {
|
||||
// sampleRate: 16000,
|
||||
// channelCount: 1,
|
||||
// echoCancellation: true,
|
||||
// noiseSuppression: true
|
||||
// }
|
||||
// });
|
||||
// const mediaRecorder = new MediaRecorder(stream);
|
||||
// const audioContext = new AudioContext({
|
||||
// sampleRate: 16000
|
||||
// });
|
||||
|
||||
// // Load the AudioWorklet
|
||||
// await audioContext.audioWorklet.addModule('/static/js/audio-processor.js');
|
||||
|
||||
// const source = audioContext.createMediaStreamSource(stream);
|
||||
// const workletNode = new AudioWorkletNode(audioContext, 'audio-processor');
|
||||
// let audioChunks = [];
|
||||
|
||||
// workletNode.port.onmessage = (e) => {
|
||||
// audioChunks.push(new Float32Array(e.data));
|
||||
|
||||
// if (audioChunks.length >= 16) {
|
||||
// // 合并所有音频块
|
||||
// const totalLength = audioChunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
||||
// const mergedData = new Float32Array(totalLength);
|
||||
// let offset = 0;
|
||||
// for (const chunk of audioChunks) {
|
||||
// mergedData.set(chunk, offset);
|
||||
// offset += chunk.length;
|
||||
// }
|
||||
|
||||
// const audioMessage = {
|
||||
// id: window.data.user_uuid,
|
||||
// type: 'audio',
|
||||
// data: mergedData,
|
||||
// sampleRate: 16000,
|
||||
// timestamp: Date.now()
|
||||
// };
|
||||
// socket.emit('voice', audioMessage);
|
||||
// console.log("voice sended")
|
||||
// audioChunks = [];
|
||||
// }
|
||||
// };
|
||||
|
||||
// source.connect(workletNode);
|
||||
// workletNode.connect(audioContext.destination);
|
||||
|
||||
// mediaRecorder.value = {
|
||||
// stop: () => {
|
||||
// source.disconnect();
|
||||
// workletNode.disconnect();
|
||||
// stream.getTracks().forEach(track => track.stop());
|
||||
// audioContext.close();
|
||||
// }
|
||||
// }
|
||||
|
||||
// isRecording.value = true;
|
||||
// } catch (error) {
|
||||
// console.error('Error starting audio recording:', error);
|
||||
// }
|
||||
// };
|
||||
// function sendVoiceMessage() {
|
||||
// startAudioRecording()
|
||||
// //变换录音中图标
|
||||
// document.getElementById('recordIcon').className = 'bi bi-record-circle';
|
||||
// document.addEventListener('click', handleMouseClick);
|
||||
// }
|
||||
|
||||
// //停止录音
|
||||
// function stopRecording() {
|
||||
// if (!isRecording) return;
|
||||
// isRecording = false;
|
||||
// //取消监听
|
||||
// document.removeEventListener('click', handleMouseClick);
|
||||
// //取消定时器
|
||||
// if (recordingInterval) {
|
||||
// clearInterval(recordingInterval);
|
||||
// recordingInterval = null;
|
||||
// }
|
||||
// //变换图标
|
||||
// document.getElementById('recordIcon').className = 'bi bi-mic';
|
||||
// //发送剩下的数据
|
||||
// if (audio_chunks.length > 0) {
|
||||
// const merged_data = new Uint8Array(audio_chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
// let offset = 0;
|
||||
// for (const chunk of audio_chunks) {
|
||||
// merged_data.set(new Uint8Array(chunk), offset);
|
||||
// offset += chunk.length;
|
||||
// }
|
||||
// socket.emit('VoiceMessage', merged_data, { binary: true });
|
||||
// audio_chunks = [];
|
||||
// }
|
||||
// overlay.style.display = 'none';
|
||||
// //关闭音频流
|
||||
// if (voice_stream) {
|
||||
// voice_stream.stop_stream();
|
||||
// voice_stream.close();
|
||||
// voice_stream = null;
|
||||
// }
|
||||
// if (p) {
|
||||
// p.terminate();
|
||||
// p = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//语音这一块//
|
||||
|
||||
Reference in New Issue
Block a user