feat: 后台管理系统 (Python Flask)

后台管理功能:
- 大模型接口配置管理(增删改、设为默认)
- 智能体管理(增删改、类别、标签、prompt、LLM配置、热度等)
- 搜索配置管理(Tavily等搜索接口)
- 系统设置(应用名称、版本、游客限制、管理员密码)
- 统计信息(LLM调用、搜索调用、智能体使用排行)

技术方案:
- Python Flask 后端服务 (端口 19020)
- SQLite 数据库存储配置
- 前端后台管理界面 (/admin)
- RESTful API 接口

后台入口:
- URL: http://localhost:19020/admin
- 默认账号: admin / admin123

前端配置获取:
- /api/config 获取LLM、搜索、智能体、系统配置
This commit is contained in:
2026-04-27 12:34:01 +08:00
parent f24cefb2ab
commit 3411de1612
6 changed files with 1965 additions and 0 deletions

611
backend/app.py Normal file
View File

@@ -0,0 +1,611 @@
#!/usr/bin/env python3
"""
AI Chat App - 后台管理服务
端口: 19020 (与前端同一端口)
"""
from flask import Flask, jsonify, request, send_from_directory
from flask_cors import CORS
import os
import json
import sqlite3
from datetime import datetime
import hashlib
app = Flask(__name__, static_folder='../www')
CORS(app)
# 数据库路径
DB_PATH = os.path.join(os.path.dirname(__file__), 'data.db')
# 管理员账户(默认)
ADMIN_USERNAME = 'admin'
ADMIN_PASSWORD_HASH = hashlib.sha256('admin123'.encode()).hexdigest()
def get_db():
"""获取数据库连接"""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
"""初始化数据库"""
conn = get_db()
cursor = conn.cursor()
# 大模型接口配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS llm_configs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
provider TEXT NOT NULL,
api_url TEXT NOT NULL,
api_key TEXT NOT NULL,
model TEXT NOT NULL,
max_tokens INTEGER DEFAULT 2048,
temperature REAL DEFAULT 0.7,
is_default INTEGER DEFAULT 0,
is_active INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 智能体配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS agents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
agent_id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
avatar TEXT DEFAULT '🤖',
category TEXT NOT NULL,
description TEXT,
system_prompt TEXT NOT NULL,
llm_config_id INTEGER,
temperature REAL DEFAULT 0.7,
max_tokens INTEGER DEFAULT 2048,
enable_search INTEGER DEFAULT 0,
tags TEXT,
heat INTEGER DEFAULT 0,
is_active INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (llm_config_id) REFERENCES llm_configs(id)
)
''')
# 搜索配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS search_configs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
provider TEXT NOT NULL,
api_url TEXT NOT NULL,
api_key TEXT NOT NULL,
max_results INTEGER DEFAULT 10,
is_default INTEGER DEFAULT 0,
is_active INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 系统配置表
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_configs (
key TEXT PRIMARY KEY,
value TEXT,
description TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 统计日志表
cursor.execute('''
CREATE TABLE IF NOT EXISTS stats_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
log_type TEXT NOT NULL,
log_key TEXT NOT NULL,
log_value INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 用户表(后台管理)
cursor.execute('''
CREATE TABLE IF NOT EXISTS admin_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT DEFAULT 'admin',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 初始化默认大模型配置
cursor.execute('SELECT COUNT(*) FROM llm_configs')
if cursor.fetchone()[0] == 0:
cursor.execute('''
INSERT INTO llm_configs (name, provider, api_url, api_key, model, is_default)
VALUES ('智谱GLM', 'zhipu', 'https://open.bigmodel.cn/api/paas/v4/chat/completions',
'2259e33a1357460abe17919aaf81e73d.K44a8LPQTmFM5PKm', 'glm-4.5-air', 1)
''')
# 初始化默认搜索配置
cursor.execute('SELECT COUNT(*) FROM search_configs')
if cursor.fetchone()[0] == 0:
cursor.execute('''
INSERT INTO search_configs (name, provider, api_url, api_key, is_default)
VALUES ('Tavily', 'tavily', 'https://api.tavily.com/search',
'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', 1)
''')
# 初始化默认智能体
cursor.execute('SELECT COUNT(*) FROM agents')
if cursor.fetchone()[0] == 0:
default_agents = [
('assistant', '通用助手', '🤖', 'hot', '能回答各类问题,帮助写作、分析、解答疑惑', '你是一个智能助手,能够回答各类问题,帮助用户解决问题。', 9500),
('writer', '写作助手', '✍️', 'hot', '专注于文章写作、文案创作、内容润色', '你是一个专业的写作助手,擅长各类文章写作、文案创作和内容润色。', 8800),
('coder', '编程助手', '👨‍💻', 'hot', '精通编程语言,解答技术问题,生成代码', '你是一个专业的编程助手,精通各类编程语言,能够解答技术问题并生成高质量代码。', 8500),
('translator', '翻译助手', '🌐', 'hot', '多语言翻译,精准表达,文化适配', '你是一个专业的翻译助手,精通多语言翻译,能够精准表达并适配文化差异。', 7200),
('work', '工作助手', '💼', 'work', '职场问题解答,工作效率提升', '你是一个工作助手,帮助解决职场问题,提升工作效率。', 5000),
('ppt', 'PPT助手', '📊', 'work', 'PPT内容生成结构优化设计建议', '你是一个PPT助手擅长PPT内容生成、结构优化和设计建议。', 4500),
('excel', 'Excel助手', '📈', 'work', 'Excel公式、数据分析、表格优化', '你是一个Excel助手精通Excel公式、数据分析和表格优化。', 4000),
('teacher', '学习助手', '📚', 'study', '知识讲解,学习方法,考试辅导', '你是一个学习助手,擅长知识讲解、学习方法指导和考试辅导。', 5500),
('english', '英语助手', '🔤', 'study', '英语学习,语法纠正,口语练习', '你是一个英语助手,帮助英语学习、语法纠正和口语练习。', 5000),
('math', '数学助手', '🔢', 'study', '数学解题,公式推导,概念讲解', '你是一个数学助手,擅长数学解题、公式推导和概念讲解。', 4500),
('health', '健康助手', '🏥', 'life', '健康咨询,养生建议,运动指导', '你是一个健康助手,提供健康咨询、养生建议和运动指导。', 3500),
('travel', '旅行助手', '✈️', 'life', '旅行规划,景点推荐,美食指南', '你是一个旅行助手,擅长旅行规划、景点推荐和美食指南。', 3200),
('food', '美食助手', '🍳', 'life', '菜谱推荐,烹饪技巧,营养搭配', '你是一个美食助手,提供菜谱推荐、烹饪技巧和营养搭配建议。', 3000),
]
for agent in default_agents:
cursor.execute('''
INSERT INTO agents (agent_id, name, avatar, category, description, system_prompt, heat)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', agent)
# 初始化系统配置
cursor.execute('SELECT COUNT(*) FROM system_configs')
if cursor.fetchone()[0] == 0:
default_configs = [
('app_name', 'AI助手', '应用名称'),
('app_version', '3.5.1', '应用版本'),
('llm_provider', 'zhipu', '默认大模型提供商'),
('enable_search', 'true', '是否启用联网搜索'),
('guest_chat_sessions', '1', '游客每日对话会话限制'),
('guest_chat_messages', '20', '游客每日对话消息限制'),
('guest_agent_messages', '20', '游客每日智能体消息限制'),
('admin_password', 'admin123', '管理员密码'),
]
for key, value, desc in default_configs:
cursor.execute('INSERT INTO system_configs (key, value, description) VALUES (?, ?, ?)', (key, value, desc))
# 初始化管理员账户
cursor.execute('SELECT COUNT(*) FROM admin_users')
if cursor.fetchone()[0] == 0:
cursor.execute('INSERT INTO admin_users (username, password_hash) VALUES (?, ?',
(ADMIN_USERNAME, ADMIN_PASSWORD_HASH))
conn.commit()
conn.close()
# ==================== 前端静态文件服务 ====================
@app.route('/')
def index():
"""前端首页"""
return send_from_directory(app.static_folder, 'index.html')
@app.route('/admin')
def admin():
"""后台管理页面"""
return send_from_directory(app.static_folder, 'admin.html')
@app.route('/admin/<path:path>')
def admin_static(path):
"""后台管理静态文件"""
return send_from_directory(app.static_folder, path)
@app.route('/<path:path>')
def static_files(path):
"""前端静态文件"""
return send_from_directory(app.static_folder, path)
# ==================== 后台管理 API ====================
# 管理员登录验证
def check_admin_auth():
"""检查管理员权限"""
auth = request.authorization
if not auth:
return False
password_hash = hashlib.sha256(auth.password.encode()).hexdigest()
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM admin_users WHERE username = ? AND password_hash = ?',
(auth.username, password_hash))
user = cursor.fetchone()
conn.close()
return user is not None
@app.route('/api/admin/login', methods=['POST'])
def admin_login():
"""管理员登录"""
data = request.json
username = data.get('username')
password = data.get('password')
if not username or not password:
return jsonify({'error': '请输入用户名和密码'}), 400
password_hash = hashlib.sha256(password.encode()).hexdigest()
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM admin_users WHERE username = ? AND password_hash = ?',
(username, password_hash))
user = cursor.fetchone()
conn.close()
if user:
return jsonify({'success': True, 'message': '登录成功'})
else:
return jsonify({'error': '用户名或密码错误'}), 401
# ==================== 大模型接口管理 ====================
@app.route('/api/admin/llm', methods=['GET'])
def get_llm_configs():
"""获取所有大模型配置"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM llm_configs ORDER BY is_default DESC, created_at DESC')
configs = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(configs)
@app.route('/api/admin/llm', methods=['POST'])
def add_llm_config():
"""添加大模型配置"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO llm_configs (name, provider, api_url, api_key, model, max_tokens, temperature)
VALUES (?, ?, ?, ?, ?, ?, ?)
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7)))
conn.commit()
config_id = cursor.lastrowid
conn.close()
return jsonify({'success': True, 'id': config_id})
@app.route('/api/admin/llm/<int:id>', methods=['PUT'])
def update_llm_config(id):
"""更新大模型配置"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
UPDATE llm_configs SET name=?, provider=?, api_url=?, api_key=?, model=?,
max_tokens=?, temperature=?, updated_at=CURRENT_TIMESTAMP WHERE id=?
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
data['model'], data.get('max_tokens', 2048), data.get('temperature', 0.7), id))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/llm/<int:id>', methods=['DELETE'])
def delete_llm_config(id):
"""删除大模型配置"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('DELETE FROM llm_configs WHERE id=?', (id,))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/llm/<int:id>/default', methods=['POST'])
def set_default_llm(id):
"""设置默认大模型"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('UPDATE llm_configs SET is_default=0')
cursor.execute('UPDATE llm_configs SET is_default=1 WHERE id=?', (id,))
conn.commit()
conn.close()
return jsonify({'success': True})
# ==================== 智能体管理 ====================
@app.route('/api/admin/agents', methods=['GET'])
def get_agents():
"""获取所有智能体"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM agents ORDER BY category, heat DESC')
agents = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(agents)
@app.route('/api/admin/agents', methods=['POST'])
def add_agent():
"""添加智能体"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO agents (agent_id, name, avatar, category, description, system_prompt,
llm_config_id, temperature, max_tokens, enable_search, tags, heat)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (data['agent_id'], data['name'], data.get('avatar', '🤖'), data['category'],
data.get('description', ''), data['system_prompt'], data.get('llm_config_id'),
data.get('temperature', 0.7), data.get('max_tokens', 2048),
data.get('enable_search', 0), data.get('tags', ''), data.get('heat', 0)))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/agents/<agent_id>', methods=['PUT'])
def update_agent(agent_id):
"""更新智能体"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
UPDATE agents SET name=?, avatar=?, category=?, description=?, system_prompt=?,
llm_config_id=?, temperature=?, max_tokens=?, enable_search=?, tags=?, heat=?,
updated_at=CURRENT_TIMESTAMP WHERE agent_id=?
''', (data['name'], data.get('avatar', '🤖'), data['category'],
data.get('description', ''), data['system_prompt'], data.get('llm_config_id'),
data.get('temperature', 0.7), data.get('max_tokens', 2048),
data.get('enable_search', 0), data.get('tags', ''), data.get('heat', 0), agent_id))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/agents/<agent_id>', methods=['DELETE'])
def delete_agent(agent_id):
"""删除智能体"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('DELETE FROM agents WHERE agent_id=?', (agent_id,))
conn.commit()
conn.close()
return jsonify({'success': True})
# ==================== 搜索配置管理 ====================
@app.route('/api/admin/search', methods=['GET'])
def get_search_configs():
"""获取搜索配置"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM search_configs ORDER BY is_default DESC')
configs = [dict(row) for row in cursor.fetchall()]
conn.close()
return jsonify(configs)
@app.route('/api/admin/search', methods=['POST'])
def add_search_config():
"""添加搜索配置"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
INSERT INTO search_configs (name, provider, api_url, api_key, max_results)
VALUES (?, ?, ?, ?, ?)
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
data.get('max_results', 10)))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/search/<int:id>', methods=['PUT'])
def update_search_config(id):
"""更新搜索配置"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('''
UPDATE search_configs SET name=?, provider=?, api_url=?, api_key=?, max_results=?,
updated_at=CURRENT_TIMESTAMP WHERE id=?
''', (data['name'], data['provider'], data['api_url'], data['api_key'],
data.get('max_results', 10), id))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/search/<int:id>', methods=['DELETE'])
def delete_search_config(id):
"""删除搜索配置"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('DELETE FROM search_configs WHERE id=?', (id,))
conn.commit()
conn.close()
return jsonify({'success': True})
@app.route('/api/admin/search/<int:id>/default', methods=['POST'])
def set_default_search(id):
"""设置默认搜索"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('UPDATE search_configs SET is_default=0')
cursor.execute('UPDATE search_configs SET is_default=1 WHERE id=?', (id,))
conn.commit()
conn.close()
return jsonify({'success': True})
# ==================== 系统配置管理 ====================
@app.route('/api/admin/system', methods=['GET'])
def get_system_configs():
"""获取系统配置"""
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM system_configs')
configs = {row['key']: {'value': row['value'], 'description': row['description']}
for row in cursor.fetchall()}
conn.close()
return jsonify(configs)
@app.route('/api/admin/system', methods=['POST'])
def update_system_config():
"""更新系统配置"""
data = request.json
conn = get_db()
cursor = conn.cursor()
for key, value in data.items():
cursor.execute('''
UPDATE system_configs SET value=?, updated_at=CURRENT_TIMESTAMP WHERE key=?
''', (value, key))
conn.commit()
conn.close()
return jsonify({'success': True})
# ==================== 统计信息 ====================
@app.route('/api/admin/stats', methods=['GET'])
def get_stats():
"""获取统计信息"""
conn = get_db()
cursor = conn.cursor()
# 今日统计
today = datetime.now().strftime('%Y-%m-%d')
# 各类型统计
stats = {}
# LLM调用统计
cursor.execute('SELECT SUM(log_value) FROM stats_logs WHERE log_type="llm_call"')
stats['llm_total_calls'] = cursor.fetchone()[0] or 0
cursor.execute('SELECT SUM(log_value) FROM stats_logs WHERE log_type="llm_call" AND created_at >= ?', (today,))
stats['llm_today_calls'] = cursor.fetchone()[0] or 0
# 搜索统计
cursor.execute('SELECT SUM(log_value) FROM stats_logs WHERE log_type="search_call"')
stats['search_total_calls'] = cursor.fetchone()[0] or 0
cursor.execute('SELECT SUM(log_value) FROM stats_logs WHERE log_type="search_call" AND created_at >= ?', (today,))
stats['search_today_calls'] = cursor.fetchone()[0] or 0
# 智能体使用统计
cursor.execute('''
SELECT log_key, SUM(log_value) as count FROM stats_logs
WHERE log_type="agent_use" GROUP BY log_key ORDER BY count DESC LIMIT 10
''')
stats['agent_usage'] = [dict(row) for row in cursor.fetchall()]
# 用户统计
cursor.execute('SELECT COUNT(*) FROM stats_logs WHERE log_type="user_register"')
stats['total_users'] = cursor.fetchone()[0] or 0
# 智能体数量
cursor.execute('SELECT COUNT(*) FROM agents WHERE is_active=1')
stats['agent_count'] = cursor.fetchone()[0]
# LLM配置数量
cursor.execute('SELECT COUNT(*) FROM llm_configs WHERE is_active=1')
stats['llm_count'] = cursor.fetchone()[0]
conn.close()
return jsonify(stats)
@app.route('/api/admin/stats/log', methods=['POST'])
def log_stats():
"""记录统计日志"""
data = request.json
conn = get_db()
cursor = conn.cursor()
cursor.execute('INSERT INTO stats_logs (log_type, log_key, log_value) VALUES (?, ?, ?)',
(data['type'], data.get('key', ''), data.get('value', 1)))
conn.commit()
conn.close()
return jsonify({'success': True})
# ==================== 前端配置获取 ====================
@app.route('/api/config', methods=['GET'])
def get_frontend_config():
"""获取前端使用的配置"""
conn = get_db()
cursor = conn.cursor()
# 获取默认LLM配置
cursor.execute('SELECT * FROM llm_configs WHERE is_default=1 AND is_active=1 LIMIT 1')
llm = cursor.fetchone()
# 获取默认搜索配置
cursor.execute('SELECT * FROM search_configs WHERE is_default=1 AND is_active=1 LIMIT 1')
search = cursor.fetchone()
# 获取所有智能体
cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat FROM agents WHERE is_active=1')
agents = [dict(row) for row in cursor.fetchall()]
# 获取系统配置
cursor.execute('SELECT key, value FROM system_configs')
system = {row['key']: row['value'] for row in cursor.fetchall()}
conn.close()
config = {
'llm': dict(llm) if llm else None,
'search': dict(search) if search else None,
'agents': agents,
'system': {
'appName': system.get('app_name', 'AI助手'),
'version': system.get('app_version', '3.5.1'),
'enableSearch': system.get('enable_search', 'true') == 'true',
'guestLimits': {
'chatSessions': int(system.get('guest_chat_sessions', '1')),
'chatMessages': int(system.get('guest_chat_messages', '20')),
'agentMessages': int(system.get('guest_agent_messages', '20')),
}
}
}
return jsonify(config)
# ==================== 启动 ====================
if __name__ == '__main__':
# 初始化数据库
init_db()
# 启动服务
print('🚀 AI Chat App Backend started on http://localhost:19020')
print('📊 Admin Panel: http://localhost:19020/admin')
print('👤 Default admin: admin / admin123')
app.run(host='0.0.0.0', port=19020, debug=True)