## 完整代碼實現
### 后端代碼 (app.py)
```python
import os
import json
import uuid
import requests
from datetime import datetime
from flask import Flask, render_template, request, jsonify
from dotenv import load_dotenv
# 加載環境變量
load_dotenv()
app = Flask(__name__)
# 文件路徑配置
USER_DATA_FILE = 'data/users.json'
INTERACTION_DATA_FILE = 'data/interactions/{user_id}.json'
KNOWLEDGE_GRAPH_FILE = 'data/knowledge_graphs/{topic}.json'
# 確保數據目錄存在
os.makedirs('data/users', exist_ok=True)
os.makedirs('data/interactions', exist_ok=True)
os.makedirs('data/knowledge_graphs', exist_ok=True)
# DeepSeek API 配置
DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
# 主頁路由
@app.route('/')
def home():
????return render_template('index.html')
# 用戶認證路由
@app.route('/auth', methods=['POST'])
def auth():
????action = request.json.get('action')
????username = request.json.get('username')
????password = request.json.get('password')
????email = request.json.get('email', '')
????if action == 'login':
????????return login_user(username, password)
????elif action == 'register':
????????return register_user(username, password, email)
????else:
????????return jsonify({'success': False, 'message': '無效操作'})
def login_user(username, password):
????try:
????????with open(USER_DATA_FILE, 'r') as f:
????????????users = json.load(f)
????????
????????for user in users:
????????????if user['username'] == username and user['password'] == password:
????????????????# 創建用戶會話
????????????????session_id = str(uuid.uuid4())
????????????????user['session_id'] = session_id
????????????????user['last_login'] = datetime.now().isoformat()
????????????????
????????????????# 更新用戶數據
????????????????with open(USER_DATA_FILE, 'w') as f:
????????????????????json.dump(users, f, indent=2)
????????????????
????????????????return jsonify({
????????????????????'success': True,
????????????????????'session_id': session_id,
????????????????????'user': {
????????????????????????'id': user['id'],
????????????????????????'username': username,
????????????????????????'email': user['email'],
????????????????????????'learning_style': user.get('learning_style', 'visual'),
????????????????????????'interests': user.get('interests', ['科技'])
????????????????????}
????????????????})
????????
????????return jsonify({'success': False, 'message': '用戶名或密碼錯誤'})
????except Exception as e:
????????return jsonify({'success': False, 'message': f'登錄失敗: {str(e)}'})
def register_user(username, password, email):
????try:
????????# 創建或加載用戶數據
????????if os.path.exists(USER_DATA_FILE):
????????????with open(USER_DATA_FILE, 'r') as f:
????????????????users = json.load(f)
????????else:
????????????users = []
????????
????????# 檢查用戶名是否已存在
????????if any(user['username'] == username for user in users):
????????????return jsonify({'success': False, 'message': '用戶名已存在'})
????????
????????# 創建新用戶
????????new_user = {
????????????'id': str(uuid.uuid4()),
????????????'username': username,
????????????'password': password,
????????????'email': email,
????????????'created_at': datetime.now().isoformat(),
????????????'learning_style': 'balanced', ?# 默認學習風格
????????????'interests': ['科技', '教育'], ?# 默認興趣
????????????'knowledge_level': 'intermediate' ?# 默認知識水平
????????}
????????
????????users.append(new_user)
????????
????????with open(USER_DATA_FILE, 'w') as f:
????????????json.dump(users, f, indent=2)
????????
????????return jsonify({'success': True, 'message': '注冊成功'})
????except Exception as e:
????????return jsonify({'success': False, 'message': f'注冊失敗: {str(e)}'})
# 核心AI對話功能
@app.route('/conversation', methods=['POST'])
def conversation():
????session_id = request.headers.get('X-Session-ID')
????user_query = request.json.get('query')
????context = request.json.get('context', [])
????
????if not session_id:
????????return jsonify({'success': False, 'message': '缺少會話ID'})
????
????# 獲取用戶信息
????user = get_user_by_session(session_id)
????if not user:
????????return jsonify({'success': False, 'message': '用戶未認證'})
????
????# 構建對話歷史
????full_context = [
????????{"role": "system", "content": build_system_prompt(user)},
????????*context[-5:], ?# 保留最近5輪對話
????????{"role": "user", "content": user_query}
????]
????
????# 調用DeepSeek API
????response = call_deepseek_api(full_context)
????
????if not response:
????????return jsonify({'success': False, 'message': 'AI服務不可用'})
????
????ai_reply = response['choices'][0]['message']['content']
????
????# 保存交互記錄
????save_interaction(user['id'], user_query, ai_reply)
????
????# 生成后續問題建議
????suggestions = generate_suggestions(ai_reply)
????
????# 情感分析
????emotion = analyze_emotion(user_query)
????
????return jsonify({
????????'success': True,
????????'reply': ai_reply,
????????'suggestions': suggestions,
????????'emotion': emotion,
????????'context': full_context
????})
def build_system_prompt(user):
????return f"""
????你是一位名為EduPal的AI學習伴侶,當前用戶信息如下:
????- 用戶名: {user['username']}
????- 學習風格: {user['learning_style']}
????- 知識水平: {user['knowledge_level']}
????- 興趣領域: {', '.join(user['interests'])}
????
????請根據用戶的學習風格和知識水平調整回答方式,融入其興趣領域的知識。
????回答應簡潔(100-200字),使用{user['learning_style']}學習風格。
????在適當的時候使用比喻、實例和可視化描述。
????"""
def call_deepseek_api(messages):
????headers = {
????????"Authorization": f"Bearer {DEEPSEEK_API_KEY}",
????????"Content-Type": "application/json"
????}
????
????payload = {
????????"model": "deepseek-chat",
????????"messages": messages,
????????"temperature": 0.7,
????????"max_tokens": 500,
????????"top_p": 0.9
????}
????
????try:
????????response = requests.post(DEEPSEEK_API_URL, headers=headers, json=payload, timeout=30)
????????return response.json()
????except Exception as e:
????????print(f"API調用失敗: {str(e)}")
????????return None
def generate_suggestions(ai_reply):
????"""生成后續問題建議"""
????prompt = f"""
????基于以下回復內容,生成3個用戶可能繼續問的問題:
????{ai_reply}
????
????要求:
????1. 問題要自然且相關
????2. 每個問題不超過10個字
????3. 返回JSON格式: {{"suggestions": ["問題1", "問題2", "問題3"]}}
????"""
????
????response = call_deepseek_api([
????????{"role": "user", "content": prompt}
????])
????
????if response:
????????try:
????????????return json.loads(response['choices'][0]['message']['content'])['suggestions']
????????except:
????????????return ["還想了解什么?", "需要進一步解釋嗎?", "還有其他問題嗎?"]
????return []
def analyze_emotion(text):
????"""分析用戶情緒"""
????prompt = f"""
????分析以下文本的情感傾向:
????"{text}"
????
????返回JSON格式:{{"emotion": "positive/negative/neutral", "intensity": 0-10}}
????"""
????
????response = call_deepseek_api([
????????{"role": "user", "content": prompt}
????])
????
????if response:
????????try:
????????????return json.loads(response['choices'][0]['message']['content'])
????????except:
????????????return {"emotion": "neutral", "intensity": 5}
????return {"emotion": "neutral", "intensity": 5}
# 知識圖譜功能
@app.route('/knowledge-graph', methods=['POST'])
def generate_knowledge_graph():
????session_id = request.headers.get('X-Session-ID')
????topic = request.json.get('topic')
????
????if not session_id:
????????return jsonify({'success': False, 'message': '缺少會話ID'})
????
????user = get_user_by_session(session_id)
????if not user:
????????return jsonify({'success': False, 'message': '用戶未認證'})
????
????# 檢查是否有緩存
????graph_file = KNOWLEDGE_GRAPH_FILE.format(topic=topic)
????if os.path.exists(graph_file):
????????with open(graph_file, 'r') as f:
????????????return jsonify({'success': True, 'graph': json.load(f)})
????
????# 生成知識圖譜
????prompt = f"""
????為以下主題創建知識圖譜:
????{topic}
????
????要求:
????1. 包含5-7個核心概念
????2. 顯示概念之間的關系
????3. 使用Mermaid語法格式
????4. 返回JSON格式: {{"mermaid": "graph TD\nA-->B\n..."}}
????"""
????
????response = call_deepseek_api([
????????{"role": "user", "content": prompt}
????])
????
????if not response:
????????return jsonify({'success': False, 'message': '生成失敗'})
????
????try:
????????graph_data = json.loads(response['choices'][0]['message']['content'])
????????with open(graph_file, 'w') as f:
????????????json.dump(graph_data, f)
????????return jsonify({'success': True, 'graph': graph_data})
????except:
????????return jsonify({'success': False, 'message': '解析失敗'})
# 學習報告功能
@app.route('/learning-report', methods=['GET'])
def generate_learning_report():
????session_id = request.headers.get('X-Session-ID')
????
????if not session_id:
????????return jsonify({'success': False, 'message': '缺少會話ID'})
????
????user = get_user_by_session(session_id)
????if not user:
????????return jsonify({'success': False, 'message': '用戶未認證'})
????
????# 加載用戶交互歷史
????interactions = load_interactions(user['id'])
????
????# 生成學習報告
????prompt = f"""
????基于以下學習交互記錄生成學習報告:
????{json.dumps(interactions[-10:], ensure_ascii=False)}
????
????報告要求:
????1. 總結學習軌跡
????2. 分析知識掌握情況
????3. 識別優勢領域
????4. 提出改進建議
????5. 推薦后續學習主題
????
????返回JSON格式:
????{{
????????"summary": "總體總結",
????????"progress": {{
????????????"strengths": ["優勢1", "優勢2"],
????????????"weaknesses": ["待改進領域1", "待改進領域2"]
????????}},
????????"recommendations": {{
????????????"resources": ["推薦資源1", "推薦資源2"],
????????????"topics": ["推薦主題1", "推薦主題2"]
????????}}
????}}
????"""
????
????response = call_deepseek_api([
????????{"role": "user", "content": prompt}
????])
????
????if not response:
????????return jsonify({'success': False, 'message': '生成失敗'})
????
????try:
????????report = json.loads(response['choices'][0]['message']['content'])
????????return jsonify({'success': True, 'report': report})
????except:
????????return jsonify({'success': False, 'message': '解析失敗'})
# 情感支持功能
@app.route('/emotional-support', methods=['POST'])
def emotional_support():
????session_id = request.headers.get('X-Session-ID')
????message = request.json.get('message')
????
????if not session_id:
????????return jsonify({'success': False, 'message': '缺少會話ID'})
????
????user = get_user_by_session(session_id)
????if not user:
????????return jsonify({'success': False, 'message': '用戶未認證'})
????
????# 情感分析
????emotion = analyze_emotion(message)
????
????# 生成支持性回復
????prompt = f"""
????用戶情緒狀態: {emotion['emotion']} (強度: {emotion['intensity']}/10)
????用戶留言: "{message}"
????
????請生成:
????1. 情感認可
????2. 建設性建議
????3. 鼓勵性結語
????
????保持溫暖、支持性的語氣,不超過150字。
????"""
????
????response = call_deepseek_api([
????????{"role": "user", "content": prompt}
????])
????
????if not response:
????????return jsonify({'success': False, 'message': '生成失敗'})
????
????return jsonify({
????????'success': True,
????????'reply': response['choices'][0]['message']['content'],
????????'emotion': emotion
????})
# 輔助函數
def get_user_by_session(session_id):
????if not os.path.exists(USER_DATA_FILE):
????????return None
????
????with open(USER_DATA_FILE, 'r') as f:
????????users = json.load(f)
????
????for user in users:
????????if user.get('session_id') == session_id:
????????????return user
????return None
def save_interaction(user_id, query, reply):
????interaction_file = INTERACTION_DATA_FILE.format(user_id=user_id)
????
????# 創建或加載交互記錄
????if os.path.exists(interaction_file):
????????with open(interaction_file, 'r') as f:
????????????interactions = json.load(f)
????else:
????????interactions = []
????
????# 添加新記錄
????interactions.append({
????????"timestamp": datetime.now().isoformat(),
????????"query": query,
????????"reply": reply
????})
????
????with open(interaction_file, 'w') as f:
????????json.dump(interactions, f, indent=2)
def load_interactions(user_id):
????interaction_file = INTERACTION_DATA_FILE.format(user_id=user_id)
????
????if os.path.exists(interaction_file):
????????with open(interaction_file, 'r') as f:
????????????return json.load(f)
????return []
if __name__ == '__main__':
????app.run(host='0.0.0.0', port=5000, debug=True)
```
### 前端核心代碼 (index.html)
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
????<meta charset="UTF-8">
????<meta name="viewport" content="width=device-width, initial-scale=1.0">
????<title>EduPal - AI學習伴侶</title>
????<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
????<script src="https://cdn.jsdelivr.net/npm/mermaid@9.1.3/dist/mermaid.min.js"></script>
????<style>
????????:root {
????????????--primary: #4361ee;
????????????--secondary: #3f37c9;
????????????--accent: #4895ef;
????????????--light: #f8f9fa;
????????????--dark: #212529;
????????????--success: #4cc9f0;
????????????--danger: #f72585;
????????}
????????
????????body {
????????????font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
????????????background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
????????????min-height: 100vh;
????????????margin: 0;
????????????padding: 0;
????????????color: var(--dark);
????????}
????????
????????.container {
????????????max-width: 1200px;
????????????margin: 0 auto;
????????????padding: 20px;
????????}
????????
????????header {
????????????display: flex;
????????????justify-content: space-between;
????????????align-items: center;
????????????padding: 15px 0;
????????????border-bottom: 1px solid rgba(0,0,0,0.1);
????????}
????????
????????.logo {
????????????display: flex;
????????????align-items: center;
????????????gap: 10px;
????????}
????????
????????.logo h1 {
????????????margin: 0;
????????????color: var(--primary);
????????}
????????
????????.user-section {
????????????display: flex;
????????????align-items: center;
????????????gap: 15px;
????????}
????????
????????.avatar {
????????????width: 40px;
????????????height: 40px;
????????????border-radius: 50%;
????????????background-color: var(--accent);
????????????display: flex;
????????????align-items: center;
????????????justify-content: center;
????????????color: white;
????????????font-weight: bold;
????????}
????????
????????.main-content {
????????????display: grid;
????????????grid-template-columns: 1fr 350px;
????????????gap: 25px;
????????????margin-top: 25px;
????????}
????????
????????.chat-container {
????????????background: white;
????????????border-radius: 15px;
????????????box-shadow: 0 5px 15px rgba(0,0,0,0.08);
????????????overflow: hidden;
????????????display: flex;
????????????flex-direction: column;
????????????height: 75vh;
????????}
????????
????????.chat-header {
????????????padding: 15px 20px;
????????????background: var(--primary);
????????????color: white;
????????}
????????
????????.messages {
????????????flex: 1;
????????????overflow-y: auto;
????????????padding: 20px;
????????????display: flex;
????????????flex-direction: column;
????????????gap: 15px;
????????}
????????
????????.message {
????????????max-width: 80%;
????????????padding: 12px 18px;
????????????border-radius: 18px;
????????????line-height: 1.5;
????????}
????????
????????.user-message {
????????????align-self: flex-end;
????????????background: var(--primary);
????????????color: white;
????????????border-bottom-right-radius: 5px;
????????}
????????
????????.ai-message {
????????????align-self: flex-start;
????????????background: #eef2ff;
????????????color: var(--dark);
????????????border-bottom-left-radius: 5px;
????????}
????????
????????.input-area {
????????????display: flex;
????????????padding: 15px;
????????????border-top: 1px solid #eee;
????????????gap: 10px;
????????}
????????
????????.input-area input {
????????????flex: 1;
????????????padding: 12px 15px;
????????????border: 1px solid #ddd;
????????????border-radius: 25px;
????????????font-size: 16px;
????????}
????????
????????.input-area button {
????????????background: var(--primary);
????????????color: white;
????????????border: none;
????????????border-radius: 25px;
????????????padding: 12px 25px;
????????????cursor: pointer;
????????????font-weight: 600;
????????}
????????
????????.suggestions {
????????????display: flex;
????????????gap: 10px;
????????????padding: 0 15px 15px;
????????????flex-wrap: wrap;
????????}
????????
????????.suggestion {
????????????background: #edf2ff;
????????????color: var(--primary);
????????????border-radius: 20px;
????????????padding: 8px 15px;
????????????font-size: 14px;
????????????cursor: pointer;
????????????transition: all 0.2s;
????????}
????????
????????.suggestion:hover {
????????????background: #dbe4ff;
????????}
????????
????????.sidebar {
????????????display: flex;
????????????flex-direction: column;
????????????gap: 25px;
????????}
????????
????????.card {
????????????background: white;
????????????border-radius: 15px;
????????????box-shadow: 0 5px 15px rgba(0,0,0,0.08);
????????????padding: 20px;
????????}
????????
????????.card h2 {
????????????margin-top: 0;
????????????color: var(--secondary);
????????????font-size: 1.3rem;
????????}
????????
????????#knowledge-graph {
????????????height: 300px;
????????????background: #f8f9fa;
????????????border-radius: 10px;
????????????margin: 15px 0;
????????????overflow: auto;
????????}
????????
????????.emotion-indicator {
????????????display: flex;
????????????align-items: center;
????????????gap: 10px;
????????????margin: 10px 0;
????????}
????????
????????.emotion-bar {
????????????flex: 1;
????????????height: 8px;
????????????background: #e9ecef;
????????????border-radius: 4px;
????????????overflow: hidden;
????????}
????????
????????.emotion-fill {
????????????height: 100%;
????????????background: var(--accent);
????????????border-radius: 4px;
????????}
????????
????????.avatar-3d {
????????????height: 200px;
????????????background: #f8f9fa;
????????????border-radius: 10px;
????????????display: flex;
????????????align-items: center;
????????????justify-content: center;
????????????color: #6c757d;
????????}
????????
????????.report-section {
????????????margin-top: 15px;
????????}
????????
????????.strength-item, .weakness-item {
????????????display: flex;
????????????align-items: center;
????????????gap: 8px;
????????????margin: 8px 0;
????????}
????????
????????.strength-item::before {
????????????content: "?";
????????????color: var(--success);
????????}
????????
????????.weakness-item::before {
????????????content: "!";
????????????color: var(--danger);
????????}
????????
????????.modal {
????????????display: none;
????????????position: fixed;
????????????top: 0;
????????????left: 0;
????????????width: 100%;
????????????height: 100%;
????????????background: rgba(0,0,0,0.5);
????????????z-index: 1000;
????????????justify-content: center;
????????????align-items: center;
????????}
????????
????????.modal-content {
????????????background: white;
????????????border-radius: 15px;
????????????width: 90%;
????????????max-width: 500px;
????????????max-height: 90vh;
????????????overflow-y: auto;
????????????padding: 25px;
????????}
????????
????????.auth-form {
????????????display: flex;
????????????flex-direction: column;
????????????gap: 15px;
????????}
????????
????????.auth-form input {
????????????padding: 12px 15px;
????????????border: 1px solid #ddd;
????????????border-radius: 8px;
????????????font-size: 16px;
????????}
????????
????????.auth-form button {
????????????background: var(--primary);
????????????color: white;
????????????border: none;
????????????border-radius: 8px;
????????????padding: 12px;
????????????cursor: pointer;
????????????font-weight: 600;
????????????font-size: 16px;
????????}
????????
????????.tabs {
????????????display: flex;
????????????margin-bottom: 20px;
????????}
????????
????????.tab {
????????????flex: 1;
????????????text-align: center;
????????????padding: 12px;
????????????background: #e9ecef;
????????????cursor: pointer;
????????}
????????
????????.tab.active {
????????????background: var(--primary);
????????????color: white;
????????}
????????
????????.tab:first-child {
????????????border-radius: 8px 0 0 8px;
????????}
????????
????????.tab:last-child {
????????????border-radius: 0 8px 8px 0;
????????}
????</style>
</head>
<body>
????<div class="container">
????????<header>
????????????<div class="logo">
????????????????<h1>EduPal</h1>
????????????????<span>AI學習伴侶</span>
????????????</div>
????????????<div class="user-section">
????????????????<div class="avatar" id="user-avatar">U</div>
????????????????<button id="auth-btn">登錄/注冊</button>
????????????</div>
????????</header>
????????
????????<main class="main-content">
????????????<div class="chat-container">
????????????????<div class="chat-header">
????????????????????<h2>與EduPal對話</h2>
????????????????</div>
????????????????<div class="messages" id="messages">
????????????????????<div class="message ai-message">
????????????????????????你好!我是EduPal,你的AI學習伴侶。我可以幫助你學習新知識、解答問題,還能根據你的學習風格個性化教學。你想了解什么?
????????????????????</div>
????????????????</div>
????????????????<div class="suggestions" id="suggestions">
????????????????????<div class="suggestion">推薦一個學習計劃</div>
????????????????????<div class="suggestion">解釋量子計算</div>
????????????????????<div class="suggestion">如何提高學習效率?</div>
????????????????</div>
????????????????<div class="input-area">
????????????????????<input type="text" id="user-input" placeholder="輸入你的問題...">
????????????????????<button id="send-btn">發送</button>
????????????????</div>
????????????</div>
????????????
????????????<div class="sidebar">
????????????????<div class="card">
????????????????????<h2>3D學習助手</h2>
????????????????????<div class="avatar-3d" id="avatar-3d">
????????????????????????<canvas id="avatar-canvas"></canvas>
????????????????????</div>
????????????????????<div class="emotion-indicator">
????????????????????????<span>情緒狀態:</span>
????????????????????????<div class="emotion-bar">
????????????????????????????<div class="emotion-fill" id="emotion-fill" style="width: 50%"></div>
????????????????????????</div>
????????????????????????<span id="emotion-label">中立</span>
????????????????????</div>
????????????????</div>
????????????????
????????????????<div class="card">
????????????????????<h2>知識圖譜</h2>
????????????????????<div id="knowledge-graph"></div>
????????????????????<button id="generate-graph-btn" style="width:100%">生成當前主題圖譜</button>
????????????????</div>
????????????????
????????????????<div class="card">
????????????????????<h2>學習報告</h2>
????????????????????<div id="report-summary">暫無報告數據</div>
????????????????????<button id="generate-report-btn" style="width:100%; margin-top:15px">生成學習報告</button>
????????????????</div>
????????????</div>
????????</main>
????</div>
????
????<!-- 認證模態框 -->
????<div class="modal" id="auth-modal">
????????<div class="modal-content">
????????????<div class="tabs">
????????????????<div class="tab active" data-tab="login">登錄</div>
????????????????<div class="tab" data-tab="register">注冊</div>
????????????</div>
????????????
????????????<form class="auth-form" id="login-form">
????????????????<input type="text" id="login-username" placeholder="用戶名" required>
????????????????<input type="password" id="login-password" placeholder="密碼" required>
????????????????<button type="submit">登錄</button>
????????????</form>
????????????
????????????<form class="auth-form" id="register-form" style="display:none">
????????????????<input type="text" id="register-username" placeholder="用戶名" required>
????????????????<input type="email" id="register-email" placeholder="郵箱" required>
????????????????<input type="password" id="register-password" placeholder="密碼" required>
????????????????<input type="password" id="register-confirm" placeholder="確認密碼" required>
????????????????<button type="submit">注冊</button>
????????????</form>
????????</div>
????</div>
????
????<script>
????????// 全局狀態
????????let sessionId = null;
????????let currentUser = null;
????????let conversationContext = [];
????????
????????// DOM元素
????????const authModal = document.getElementById('auth-modal');
????????const authBtn = document.getElementById('auth-btn');
????????const loginForm = document.getElementById('login-form');
????????const registerForm = document.getElementById('register-form');
????????const tabs = document.querySelectorAll('.tab');
????????const messagesContainer = document.getElementById('messages');
????????const userInput = document.getElementById('user-input');
????????const sendBtn = document.getElementById('send-btn');
????????const suggestionsContainer = document.getElementById('suggestions');
????????const knowledgeGraphContainer = document.getElementById('knowledge-graph');
????????const generateGraphBtn = document.getElementById('generate-graph-btn');
????????const emotionFill = document.getElementById('emotion-fill');
????????const emotionLabel = document.getElementById('emotion-label');
????????const reportSummary = document.getElementById('report-summary');
????????const generateReportBtn = document.getElementById('generate-report-btn');
????????
????????// 初始化3D頭像
????????function init3DAvatar() {
????????????const canvas = document.getElementById('avatar-canvas');
????????????const scene = new THREE.Scene();
????????????const camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
????????????const renderer = new THREE.WebGLRenderer({ canvas, alpha: true });
????????????
????????????renderer.setSize(canvas.clientWidth, canvas.clientHeight);
????????????
????????????// 創建基礎幾何體
????????????const geometry = new THREE.SphereGeometry(1, 32, 32);
????????????const material = new THREE.MeshBasicMaterial({ color: 0x4361ee });
????????????const sphere = new THREE.Mesh(geometry, material);
????????????
????????????scene.add(sphere);
????????????camera.position.z = 3;
????????????
????????????// 動畫循環
????????????function animate() {
????????????????requestAnimationFrame(animate);
????????????????sphere.rotation.x += 0.01;
????????????????sphere.rotation.y += 0.01;
????????????????renderer.render(scene, camera);
????????????}
????????????
????????????animate();
????????}
????????
????????// 初始化Mermaid
????????mermaid.initialize({
????????????startOnLoad: true,
????????????theme: 'neutral',
????????????securityLevel: 'loose'
????????});
????????
????????// 顯示認證模態框
????????authBtn.addEventListener('click', () => {
????????????authModal.style.display = 'flex';
????????});
????????
????????// 切換登錄/注冊標簽
????????tabs.forEach(tab => {
????????????tab.addEventListener('click', () => {
????????????????tabs.forEach(t => t.classList.remove('active'));
????????????????tab.classList.add('active');
????????????????
????????????????if (tab.dataset.tab === 'login') {
????????????????????loginForm.style.display = 'flex';
????????????????????registerForm.style.display = 'none';
????????????????} else {
????????????????????loginForm.style.display = 'none';
????????????????????registerForm.style.display = 'flex';
????????????????}
????????????});
????????});
????????
????????// 登錄處理
????????loginForm.addEventListener('submit', async (e) => {
????????????e.preventDefault();
????????????
????????????const username = document.getElementById('login-username').value;
????????????const password = document.getElementById('login-password').value;
????????????
????????????const response = await fetch('/auth', {
????????????????method: 'POST',
????????????????headers: { 'Content-Type': 'application/json' },
????????????????body: JSON.stringify({
????????????????????action: 'login',
????????????????????username,
????????????????????password
????????????????})
????????????});
????????????
????????????const data = await response.json();
????????????
????????????if (data.success) {
????????????????sessionId = data.session_id;
????????????????currentUser = data.user;
????????????????document.getElementById('user-avatar').textContent = currentUser.username.charAt(0).toUpperCase();
????????????????authModal.style.display = 'none';
????????????????
????????????????// 添加歡迎消息
????????????????addMessage(`歡迎回來,${currentUser.username}!今天想學習什么?`, 'ai');
????????????} else {
????????????????alert(data.message);
????????????}
????????});
????????
????????// 注冊處理
????????registerForm.addEventListener('submit', async (e) => {
????????????e.preventDefault();
????????????
????????????const username = document.getElementById('register-username').value;
????????????const email = document.getElementById('register-email').value;
????????????const password = document.getElementById('register-password').value;
????????????const confirm = document.getElementById('register-confirm').value;
????????????
????????????if (password !== confirm) {
????????????????alert('兩次輸入的密碼不一致');
????????????????return;
????????????}
????????????
????????????const response = await fetch('/auth', {
????????????????method: 'POST',
????????????????headers: { 'Content-Type': 'application/json' },
????????????????body: JSON.stringify({
????????????????????action: 'register',
????????????????????username,
????????????????????password,
????????????????})
????????????});
????????????
????????????const data = await response.json();
????????????
????????????if (data.success) {
????????????????alert('注冊成功!請登錄');
????????????????tabs[0].click();
????????????} else {
????????????????alert(data.message);
????????????}
????????});
????????
????????// 發送消息
????????sendBtn.addEventListener('click', sendMessage);
????????userInput.addEventListener('keypress', (e) => {
????????????if (e.key === 'Enter') sendMessage();
????????});
????????
????????// 建議點擊
????????suggestionsContainer.addEventListener('click', (e) => {
????????????if (e.target.classList.contains('suggestion')) {
????????????????userInput.value = e.target.textContent;
????????????????sendMessage();
????????????}
????????});
????????
????????// 生成知識圖譜
????????generateGraphBtn.addEventListener('click', async () => {
????????????if (!currentUser) {
????????????????alert('請先登錄');
????????????????return;
????????????}
????????????
????????????const lastUserMessage = conversationContext
????????????????.filter(msg => msg.role === 'user')
????????????????.pop()?.content;
????????????
????????????if (!lastUserMessage) {
????????????????alert('請先進行對話');
????????????????return;
????????????}
????????????
????????????const response = await fetch('/knowledge-graph', {
????????????????method: 'POST',
????????????????headers: {
????????????????????'Content-Type': 'application/json',
????????????????????'X-Session-ID': sessionId
????????????????},
????????????????body: JSON.stringify({ topic: lastUserMessage })
????????????});
????????????
????????????const data = await response.json();
????????????
????????????if (data.success) {
????????????????knowledgeGraphContainer.innerHTML = '';
????????????????const graphDiv = document.createElement('div');
????????????????graphDiv.className = 'mermaid';
????????????????graphDiv.textContent = data.graph.mermaid;
????????????????knowledgeGraphContainer.appendChild(graphDiv);
????????????????mermaid.init(undefined, graphDiv);
????????????} else {
????????????????alert('生成失敗: ' + data.message);
????????????}
????????});
????????
????????// 生成學習報告
????????generateReportBtn.addEventListener('click', async () => {
????????????if (!currentUser) {
????????????????alert('請先登錄');
????????????????return;
????????????}
????????????
????????????const response = await fetch('/learning-report', {
????????????????method: 'GET',
????????????????headers: { 'X-Session-ID': sessionId }
????????????});
????????????
????????????const data = await response.json();
????????????
????????????if (data.success) {
????????????????const report = data.report;
????????????????let html = `
????????????????????<h3>學習總結</h3>
????????????????????<p>${report.summary}</p>
????????????????????
????????????????????<h3>學習進度</h3>
????????????????????<div class="report-section">
????????????????????????<h4>優勢領域</h4>
????????????????????????${report.progress.strengths.map(s => `<div class="strength-item">${s}</div>`).join('')}
????????????????????????
????????????????????????<h4>待改進領域</h4>
????????????????????????${report.progress.weaknesses.map(w => `<div class="weakness-item">${w}</div>`).join('')}
????????????????????</div>
????????????????????
????????????????????<h3>學習建議</h3>
????????????????????<div class="report-section">
????????????????????????<h4>推薦資源</h4>
????????????????????????<ul>${report.recommendations.resources.map(r => `<li>${r}</li>`).join('')}</ul>
????????????????????????
????????????????????????<h4>推薦主題</h4>
????????????????????????<ul>${report.recommendations.topics.map(t => `<li>${t}</li>`).join('')}</ul>
????????????????????</div>
????????????????`;
????????????????
????????????????reportSummary.innerHTML = html;
????????????} else {
????????????????alert('生成失敗: ' + data.message);
????????????}
????????});
????????
????????// 添加消息到對話框
????????function addMessage(text, sender) {
????????????const messageDiv = document.createElement('div');
????????????messageDiv.className = `message ${sender}-message`;
????????????messageDiv.textContent = text;
????????????messagesContainer.appendChild(messageDiv);
????????????messagesContainer.scrollTop = messagesContainer.scrollHeight;
????????}
????????
????????// 發送消息
????????async function sendMessage() {
????????????const message = userInput.value.trim();
????????????if (!message) return;
????????????
????????????if (!currentUser) {
????????????????alert('請先登錄');
????????????????return;
????????????}
????????????
????????????// 添加用戶消息
????????????addMessage(message, 'user');
????????????userInput.value = '';
????????????
????????????// 添加到上下文
????????????conversationContext.push({ role: 'user', content: message });
????????????
????????????// 發送到后端
????????????const response = await fetch('/conversation', {
????????????????method: 'POST',
????????????????headers: {
????????????????????'Content-Type': 'application/json',
????????????????????'X-Session-ID': sessionId
????????????????},
????????????????body: JSON.stringify({
????????????????????query: message,
????????????????????context: conversationContext
????????????????})
????????????});
????????????
????????????const data = await response.json();
????????????
????????????if (data.success) {
????????????????// 添加AI回復
????????????????addMessage(data.reply, 'ai');
????????????????
????????????????// 更新上下文
????????????????conversationContext = data.context;
????????????????
????????????????// 更新建議
????????????????updateSuggestions(data.suggestions);
????????????????
????????????????// 更新情緒狀態
????????????????updateEmotion(data.emotion);
????????????} else {
????????????????addMessage('抱歉,我暫時無法回答這個問題。請稍后再試。', 'ai');
????????????}
????????}
????????
????????// 更新建議
????????function updateSuggestions(suggestions) {
????????????suggestionsContainer.innerHTML = '';
????????????suggestions.forEach(suggestion => {
????????????????const div = document.createElement('div');
????????????????div.className = 'suggestion';
????????????????div.textContent = suggestion;
????????????????suggestionsContainer.appendChild(div);
????????????});
????????}
????????
????????// 更新情緒狀態
????????function updateEmotion(emotion) {
????????????const intensity = emotion.intensity;
????????????emotionFill.style.width = `${intensity * 10}%`;
????????????
????????????let label = '';
????????????if (emotion.emotion === 'positive') {
????????????????label = `積極 (${intensity}/10)`;
????????????????emotionFill.style.backgroundColor = '#4ade80';
????????????} else if (emotion.emotion === 'negative') {
????????????????label = `消極 (${intensity}/10)`;
????????????????emotionFill.style.backgroundColor = '#f87171';
????????????} else {
????????????????label = `中立 (${intensity}/10)`;
????????????????emotionFill.style.backgroundColor = '#60a5fa';
????????????}
????????????
????????????emotionLabel.textContent = label;
????????}
????????
????????// 初始化
????????window.addEventListener('load', () => {
????????????init3DAvatar();
????????});
????</script>
</body>
</html>
```
### 環境文件 (.env)
```
DEEPSEEK_API_KEY=your_api_key_here
```
---
## 項目策劃書:EduPal - AI學習伴侶
### 1. 項目概述
**項目名稱**:EduPal ?
**項目定位**:基于大模型的個性化AI學習伴侶系統 ?
**核心創新**:融合情感智能與知識圖譜的學習體驗 ?
**目標用戶**:中學生、大學生及終身學習者 ?
**技術棧**: ?
- 后端:Python/Flask ?
- 前端:HTML5/CSS3/JavaScript + Three.js ?
- AI模型:DeepSeek API ?
- 可視化:Mermaid.js ?
### 2. 核心功能設計
#### 2.1 智能學習伴侶
- **個性化學習路徑**:基于用戶畫像動態調整教學內容
- **情境感知對話**:記憶對話歷史,保持對話連貫性
- **智能追問建議**:AI生成后續學習問題
- **多模態解釋**:結合文本、圖表、可視化解釋復雜概念
#### 2.2 情感智能支持
- **情緒識別系統**:分析用戶文本情感傾向與強度
- **共情式回應**:生成溫暖、支持性的回復
- **學習狀態監測**:識別學習挫折并提供鼓勵
#### 2.3 知識圖譜系統
- **動態知識圖譜**:自動生成學科概念關系圖
- **交互式探索**:點擊節點獲取詳細解釋
- **知識關聯發現**:揭示跨學科知識連接
#### 2.4 學習分析報告
- **學習軌跡可視化**:展示知識掌握進度
- **優勢領域識別**:突出用戶擅長領域
- **個性化建議**:推薦學習資源和改進方向
### 3. 技術創新點
#### 3.1 動態用戶畫像系統
```mermaid
graph LR
A[用戶交互] --> B[行為分析]
B --> C[知識水平評估]
B --> D[學習風格識別]
B --> E[興趣領域挖掘]
C --> F[個性化回復]
D --> F
E --> F
```
#### 3.2 情感智能引擎
- **情感分析模型**:基于文本的情感分類與強度量化
- **CBT(認知行為療法)融合**:在回復中融入心理學原理
- **情緒可視化**:實時情緒狀態儀表盤
#### 3.3 知識圖譜生成技術
- **概念提取算法**:自動識別核心知識點
- **關系推理引擎**:建立概念間的邏輯聯系
- **跨學科連接**:發現不同領域的知識關聯
### 4. 系統架構
#### 4.1 技術架構
```mermaid
graph TD
A[前端界面] --> B[Flask API網關]
B --> C[用戶管理系統]
B --> D[對話引擎]
B --> E[知識圖譜生成]
B --> F[學習分析模塊]
C --> G[用戶數據存儲]
D --> H[DeepSeek API]
E --> H
F --> G
```
#### 4.2 數據流設計
```mermaid
sequenceDiagram
用戶->>前端: 發起請求
前端->>后端: API調用
后端->>AI模型: 請求處理
AI模型->>后端: 返回結果
后端->>前端: 響應數據
前端->>用戶: 展示結果
前端->>后端: 用戶反饋
后端->>AI模型: 模型優化
```
### 5. 實現計劃
| 階段 | 時間 | 主要任務 | 交付物 |
|------|------|----------|--------|
| 1. 基礎框架 | 第1周 | 用戶系統搭建、API接口設計 | 可運行的原型系統 |
| 2. 核心AI | 第2周 | 對話引擎、情感分析模塊 | 智能對話功能 |
| 3. 知識可視化 | 第3周 | 知識圖譜生成、3D虛擬形象 | 交互式學習界面 |
| 4. 優化部署 | 第4周 | 性能優化、云端部署 | 可上線應用 |
### 6. 創新亮點
1. **情感智能融合** ?
???業內首個將情感分析深度融入教育場景的AI系統
2. **動態知識圖譜** ?
???實時生成個性化知識結構圖,輔助構建知識體系
3. **成長陪伴模式** ?
???從單純答疑進化為學習旅程的全程伴侶
4. **多模態交互** ?
???結合3D虛擬形象、知識圖譜和自然語言對話
### 7. 預期成果
1. **功能完備的Web應用** ?
???- 個性化學習對話系統 ?
???- 情感支持模塊 ?
???- 知識圖譜可視化 ?
???- 學習分析報告 ?
2. **技術文檔** ?
???- 系統架構設計 ?
???- API文檔 ?
???- 部署指南 ?
3. **演示材料** ?
???- 產品演示視頻 ?
???- 用戶測試報告 ?
???- 創新點說明文檔 ?
### 8. 部署方案
```bash
# 部署步驟
1. 安裝依賴
pip install flask python-dotenv requests
2. 設置環境變量
echo "DEEPSEEK_API_KEY=your_api_key" > .env
3. 啟動服務
python app.py
4. 配置生產環境
gunicorn -w 4 -b 0.0.0.0:5000 app:app
```
### 9. 項目優勢
1. **教育普惠性**:降低個性化教育門檻 ?
2. **情感連接**:解決傳統AI冷漠問題 ?
3. **知識結構化**:幫助構建系統知識體系 ?
4. **可擴展性**:支持多學科知識領域 ?
5. **用戶粘性**:成長陪伴式學習體驗 ?
---
## 下一步建議
1. **申請DeepSeek API Key**:替換.env文件中的API密鑰
2. **部署測試環境**:使用Vercel或Render部署前端
3. **豐富學科知識庫**:添加特定領域的學習資料
4. **開發移動應用**:基于Flutter開發跨平臺APP
5. **接入更多AI服務**:集成多模態模型支持圖像解釋