From f85991064ff3745355044427dbca9bce2d97a1ae Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Mon, 27 Apr 2026 14:15:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=99=BA=E8=83=BD=E4=BD=93=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=A2=9E=E5=8A=A0=E4=B8=8A=E7=BA=BF=E5=BC=80=E5=85=B3?= =?UTF-8?q?=E3=80=81=E6=A0=87=E7=AD=BE=E3=80=81=E5=8F=AF=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 智能体新增功能: - 上线开关(is_online):点击快速切换,未上线则APP不可见 - 标签(tags):支持多标签,逗号分隔(hot/popular/new等) - 可使用工具(enable_tools):配置智能体可用的工具列表 类别调整: - 热门改为标签,不再是类别 - 类别改为: basic/work/study/life - hot=热门标签, popular=推荐标签, new=新品标签 数据库变更: - agents表新增 is_online 字段 - agents表新增 enable_tools 字段 - 移除 enable_search 字段(改为工具) API新增: - /api/admin/agents//online 切换上线状态 - 前端配置只返回上线且活跃的智能体 --- backend/app.py | 66 +++++++++++++++++---------- www/admin.js | 120 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 145 insertions(+), 41 deletions(-) diff --git a/backend/app.py b/backend/app.py index 5c2ad10..e373873 100644 --- a/backend/app.py +++ b/backend/app.py @@ -66,9 +66,10 @@ def init_db(): llm_config_id INTEGER, temperature REAL DEFAULT 0.7, max_tokens INTEGER DEFAULT 2048, - enable_search INTEGER DEFAULT 0, + enable_tools TEXT, tags TEXT, heat INTEGER DEFAULT 0, + is_online INTEGER DEFAULT 1, is_active INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -176,24 +177,28 @@ def init_db(): 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), + # 基础类别 + ('assistant', '通用助手', '🤖', 'basic', '能回答各类问题,帮助写作、分析、解答疑惑', '你是一个智能助手,能够回答各类问题,帮助用户解决问题。', 'hot', 9500), + ('writer', '写作助手', '✍️', 'basic', '专注于文章写作、文案创作、内容润色', '你是一个专业的写作助手,擅长各类文章写作、文案创作和内容润色。', 'hot', 8800), + ('coder', '编程助手', '👨‍💻', 'basic', '精通编程语言,解答技术问题,生成代码', '你是一个专业的编程助手,精通各类编程语言,能够解答技术问题并生成高质量代码。', 'hot', 8500), + ('translator', '翻译助手', '🌐', 'basic', '多语言翻译,精准表达,文化适配', '你是一个专业的翻译助手,精通多语言翻译,能够精准表达并适配文化差异。', 'hot', 7200), + # 工作类别 + ('work', '工作助手', '💼', 'work', '职场问题解答,工作效率提升', '你是一个工作助手,帮助解决职场问题,提升工作效率。', 'popular', 5000), + ('ppt', 'PPT助手', '📊', 'work', 'PPT内容生成,结构优化,设计建议', '你是一个PPT助手,擅长PPT内容生成、结构优化和设计建议。', 'popular', 4500), + ('excel', 'Excel助手', '📈', 'work', 'Excel公式、数据分析、表格优化', '你是一个Excel助手,精通Excel公式、数据分析和表格优化。', '', 4000), + # 学习类别 + ('teacher', '学习助手', '📚', 'study', '知识讲解,学习方法,考试辅导', '你是一个学习助手,擅长知识讲解、学习方法指导和考试辅导。', 'popular', 5500), + ('english', '英语助手', '🔤', 'study', '英语学习,语法纠正,口语练习', '你是一个英语助手,帮助英语学习、语法纠正和口语练习。', '', 5000), + ('math', '数学助手', '🔢', 'study', '数学解题,公式推导,概念讲解', '你是一个数学助手,擅长数学解题、公式推导和概念讲解。', '', 4500), + # 生活类别 + ('health', '健康助手', '🏥', 'life', '健康咨询,养生建议,运动指导', '你是一个健康助手,提供健康咨询、养生建议和运动指导。', '', 3500), + ('travel', '旅行助手', '✈️', 'life', '旅行规划,景点推荐,美食指南', '你是一个旅行助手,擅长旅行规划、景点推荐和美食指南。', 'popular', 3200), + ('food', '美食助手', '🍳', 'life', '菜谱推荐,烹饪技巧,营养搭配', '你是一个美食助手,提供菜谱推荐、烹饪技巧和营养搭配建议。', '', 3000), ] for agent in default_agents: cursor.execute(''' - INSERT INTO agents (agent_id, name, avatar, category, description, system_prompt, heat) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO agents (agent_id, name, avatar, category, description, system_prompt, tags, heat, is_online) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1) ''', agent) # 初始化系统配置 @@ -380,12 +385,13 @@ def add_agent(): 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + llm_config_id, temperature, max_tokens, enable_tools, tags, heat, is_online) + 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))) + data.get('enable_tools', ''), data.get('tags', ''), data.get('heat', 0), + data.get('is_online', 1))) conn.commit() conn.close() return jsonify({'success': True}) @@ -399,12 +405,13 @@ def update_agent(agent_id): cursor = conn.cursor() cursor.execute(''' UPDATE agents SET name=?, avatar=?, category=?, description=?, system_prompt=?, - llm_config_id=?, temperature=?, max_tokens=?, enable_search=?, tags=?, heat=?, + llm_config_id=?, temperature=?, max_tokens=?, enable_tools=?, tags=?, heat=?, is_online=?, 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)) + data.get('enable_tools', ''), data.get('tags', ''), data.get('heat', 0), + data.get('is_online', 1), agent_id)) conn.commit() conn.close() return jsonify({'success': True}) @@ -421,6 +428,19 @@ def delete_agent(agent_id): return jsonify({'success': True}) +@app.route('/api/admin/agents//online', methods=['POST']) +def toggle_agent_online(agent_id): + """切换智能体上线状态""" + data = request.json + conn = get_db() + cursor = conn.cursor() + cursor.execute('UPDATE agents SET is_online=?, updated_at=CURRENT_TIMESTAMP WHERE agent_id=?', + (data.get('is_online', 1), agent_id)) + conn.commit() + conn.close() + return jsonify({'success': True}) + + # ==================== 对话配置管理 ==================== @app.route('/api/admin/chat', methods=['GET']) @@ -643,8 +663,8 @@ def get_frontend_config(): cursor.execute('SELECT * FROM tool_configs WHERE is_default=1 AND is_active=1') tools = [dict(row) for row in cursor.fetchall()] - # 获取所有智能体 - cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat, enable_search FROM agents WHERE is_active=1') + # 获取所有智能体(上线且活跃) + cursor.execute('SELECT agent_id, name, avatar, category, description, system_prompt, heat, tags, enable_tools FROM agents WHERE is_online=1 AND is_active=1') agents = [dict(row) for row in cursor.fetchall()] # 获取默认对话配置 diff --git a/www/admin.js b/www/admin.js index 068c2c1..79fd7cd 100644 --- a/www/admin.js +++ b/www/admin.js @@ -361,14 +361,22 @@ async function deleteLLM(id) { async function loadAgentsPage(content) { agents = await fetchAPI('/api/admin/agents'); llmConfigs = await fetchAPI('/api/admin/llm'); + toolConfigs = await fetchAPI('/api/admin/tools'); const categories = { - hot: '热门', + basic: '基础', work: '工作', study: '学习', life: '生活' }; + const tagLabels = { + 'hot': '🔥 热门', + 'popular': '👍 推荐', + 'new': '🆕 新品', + '': '' + }; + content.innerHTML = `

智能体管理

@@ -382,9 +390,9 @@ async function loadAgentsPage(content) { 头像 名称 类别 - 描述 + 标签 热度 - 状态 + 上线状态 操作 @@ -394,9 +402,13 @@ async function loadAgentsPage(content) { ${a.avatar} ${a.name} ${categories[a.category] || a.category} - ${a.description || '-'} + ${a.tags ? a.tags.split(',').map(t => tagLabels[t] || t).filter(t => t).join(' ') : '-'} ${a.heat || 0} - ${a.is_active ? '✅ 启用' : '❌ 禁用'} + + + ${a.is_online ? '✅ 已上线' : '❌ 未上线'} + +
@@ -408,9 +420,34 @@ async function loadAgentsPage(content) {
+ +
+

说明

+
+
+ 上线状态 +

点击可快速切换,未上线则APP不可见

+
+
+ 标签 +

hot=热门, popular=推荐, new=新品

+
+
+ 类别 +

basic/work/study/life

+
+
+
`; } +async function toggleAgentOnline(agentId, currentStatus) { + const newStatus = currentStatus ? 0 : 1; + await fetchAPI(`/api/admin/agents/${agentId}/online`, 'POST', { is_online: newStatus }); + showToast(newStatus ? '已上线' : '已下线'); + loadPage('agents'); +} + function showAddAgentModal() { showModal('添加智能体', `
@@ -432,12 +469,51 @@ function showAddAgentModal() {
+
+ + + 可选: hot(热门), popular(推荐), new(新品) +
+
+ + +
+
+ + +
+
+ + +
+
+ + + 已配置工具: ${toolConfigs.map(t => t.tool_id).join(', ')} +
+
+ + +
+
+ +
+ + `); +} + +
@@ -494,12 +570,17 @@ function showEditAgentModal(agentId) {
+
+ + + 可选: hot(热门), popular(推荐), new(新品) +
@@ -509,23 +590,24 @@ function showEditAgentModal(agentId) {
- +
+
+ + + 已配置工具: ${toolConfigs.map(t => t.tool_id).join(', ')} +
-
- - -
@@ -538,12 +620,13 @@ async function saveAgent() { name: document.getElementById('agentName').value, avatar: document.getElementById('agentAvatar').value, category: document.getElementById('agentCategory').value, + tags: document.getElementById('agentTags').value, description: document.getElementById('agentDescription').value, system_prompt: document.getElementById('agentPrompt').value, llm_config_id: document.getElementById('agentLLM').value || null, + enable_tools: document.getElementById('agentEnableTools').value, heat: parseInt(document.getElementById('agentHeat').value), - tags: document.getElementById('agentTags').value, - enable_search: document.getElementById('agentEnableSearch').checked ? 1 : 0 + is_online: document.getElementById('agentIsOnline').checked ? 1 : 0 }; if (!data.agent_id || !data.name || !data.system_prompt) { @@ -562,12 +645,13 @@ async function updateAgent(agentId) { name: document.getElementById('agentName').value, avatar: document.getElementById('agentAvatar').value, category: document.getElementById('agentCategory').value, + tags: document.getElementById('agentTags').value, description: document.getElementById('agentDescription').value, system_prompt: document.getElementById('agentPrompt').value, llm_config_id: document.getElementById('agentLLM').value || null, + enable_tools: document.getElementById('agentEnableTools').value, heat: parseInt(document.getElementById('agentHeat').value), - tags: document.getElementById('agentTags').value, - enable_search: document.getElementById('agentEnableSearch').checked ? 1 : 0 + is_online: document.getElementById('agentIsOnline').checked ? 1 : 0 }; await fetchAPI(`/api/admin/agents/${agentId}`, 'PUT', data);