feat: 智能体管理增加上线开关、标签、可使用工具
智能体新增功能: - 上线开关(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/<id>/online 切换上线状态 - 前端配置只返回上线且活跃的智能体
This commit is contained in:
@@ -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/<agent_id>/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()]
|
||||
|
||||
# 获取默认对话配置
|
||||
|
||||
120
www/admin.js
120
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 = `
|
||||
<div class="content-header">
|
||||
<h1 class="content-title">智能体管理</h1>
|
||||
@@ -382,9 +390,9 @@ async function loadAgentsPage(content) {
|
||||
<th>头像</th>
|
||||
<th>名称</th>
|
||||
<th>类别</th>
|
||||
<th>描述</th>
|
||||
<th>标签</th>
|
||||
<th>热度</th>
|
||||
<th>状态</th>
|
||||
<th>上线状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -394,9 +402,13 @@ async function loadAgentsPage(content) {
|
||||
<td style="font-size: 24px;">${a.avatar}</td>
|
||||
<td>${a.name}</td>
|
||||
<td>${categories[a.category] || a.category}</td>
|
||||
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${a.description || '-'}</td>
|
||||
<td>${a.tags ? a.tags.split(',').map(t => tagLabels[t] || t).filter(t => t).join(' ') : '-'}</td>
|
||||
<td>${a.heat || 0}</td>
|
||||
<td>${a.is_active ? '✅ 启用' : '❌ 禁用'}</td>
|
||||
<td>
|
||||
<span style="cursor: pointer; color: ${a.is_online ? '#22c55e' : '#e53e3e'};" onclick="toggleAgentOnline('${a.agent_id}', ${a.is_online})">
|
||||
${a.is_online ? '✅ 已上线' : '❌ 未上线'}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="action-btns">
|
||||
<button class="action-btn edit" onclick="showEditAgentModal('${a.agent_id}')">编辑</button>
|
||||
@@ -408,9 +420,34 @@ async function loadAgentsPage(content) {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 24px; padding: 16px; background: white; border-radius: 12px;">
|
||||
<h3 style="margin-bottom: 16px;">说明</h3>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;">
|
||||
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||
<strong>上线状态</strong>
|
||||
<p style="color: #718096; font-size: 12px; margin-top: 4px;">点击可快速切换,未上线则APP不可见</p>
|
||||
</div>
|
||||
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||
<strong>标签</strong>
|
||||
<p style="color: #718096; font-size: 12px; margin-top: 4px;">hot=热门, popular=推荐, new=新品</p>
|
||||
</div>
|
||||
<div style="padding: 12px; background: #f5f7fa; border-radius: 8px;">
|
||||
<strong>类别</strong>
|
||||
<p style="color: #718096; font-size: 12px; margin-top: 4px;">basic/work/study/life</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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('添加智能体', `
|
||||
<div class="form-group">
|
||||
@@ -432,12 +469,51 @@ function showAddAgentModal() {
|
||||
<div class="form-group">
|
||||
<label class="form-label">类别</label>
|
||||
<select class="form-select" id="agentCategory">
|
||||
<option value="hot">热门</option>
|
||||
<option value="basic">基础</option>
|
||||
<option value="work">工作</option>
|
||||
<option value="study">学习</option>
|
||||
<option value="life">生活</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">标签(逗号分隔)</label>
|
||||
<input type="text" class="form-input" id="agentTags" placeholder="如:hot,popular">
|
||||
<span style="color: #999; font-size: 12px;">可选: hot(热门), popular(推荐), new(新品)</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">描述</label>
|
||||
<input type="text" class="form-input" id="agentDescription" placeholder="智能体描述">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">System Prompt</label>
|
||||
<textarea class="form-textarea" id="agentPrompt" placeholder="系统提示词"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">LLM配置(可选)</label>
|
||||
<select class="form-select" id="agentLLM">
|
||||
<option value="">使用默认</option>
|
||||
${llmConfigs.map(c => `<option value="${c.id}">${c.name}</option>`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">可使用工具(逗号分隔)</label>
|
||||
<input type="text" class="form-input" id="agentEnableTools" placeholder="如:search,calculator">
|
||||
<span style="color: #999; font-size: 12px;">已配置工具: ${toolConfigs.map(t => t.tool_id).join(', ')}</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">热度</label>
|
||||
<input type="number" class="form-input" id="agentHeat" value="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="checkbox" id="agentIsOnline" checked> 立即上线
|
||||
</label>
|
||||
</div>
|
||||
<button class="form-submit" onclick="saveAgent()">保存</button>
|
||||
`);
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">描述</label>
|
||||
<input type="text" class="form-input" id="agentDescription" placeholder="智能体描述">
|
||||
@@ -494,12 +570,17 @@ function showEditAgentModal(agentId) {
|
||||
<div class="form-group">
|
||||
<label class="form-label">类别</label>
|
||||
<select class="form-select" id="agentCategory">
|
||||
<option value="hot" ${agent.category === 'hot' ? 'selected' : ''}>热门</option>
|
||||
<option value="basic" ${agent.category === 'basic' ? 'selected' : ''}>基础</option>
|
||||
<option value="work" ${agent.category === 'work' ? 'selected' : ''}>工作</option>
|
||||
<option value="study" ${agent.category === 'study' ? 'selected' : ''}>学习</option>
|
||||
<option value="life" ${agent.category === 'life' ? 'selected' : ''}>生活</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">标签(逗号分隔)</label>
|
||||
<input type="text" class="form-input" id="agentTags" value="${agent.tags || ''}">
|
||||
<span style="color: #999; font-size: 12px;">可选: hot(热门), popular(推荐), new(新品)</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">描述</label>
|
||||
<input type="text" class="form-input" id="agentDescription" value="${agent.description || ''}">
|
||||
@@ -509,23 +590,24 @@ function showEditAgentModal(agentId) {
|
||||
<textarea class="form-textarea" id="agentPrompt">${agent.system_prompt || ''}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">LLM配置</label>
|
||||
<label class="form-label">LLM配置(可选)</label>
|
||||
<select class="form-select" id="agentLLM">
|
||||
<option value="">使用默认</option>
|
||||
<option value="" ${!agent.llm_config_id ? 'selected' : ''}>使用默认</option>
|
||||
${llmConfigs.map(c => `<option value="${c.id}" ${c.id === agent.llm_config_id ? 'selected' : ''}>${c.name}</option>`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">可使用工具(逗号分隔)</label>
|
||||
<input type="text" class="form-input" id="agentEnableTools" value="${agent.enable_tools || ''}">
|
||||
<span style="color: #999; font-size: 12px;">已配置工具: ${toolConfigs.map(t => t.tool_id).join(', ')}</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">热度</label>
|
||||
<input type="number" class="form-input" id="agentHeat" value="${agent.heat || 0}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">标签</label>
|
||||
<input type="text" class="form-input" id="agentTags" value="${agent.tags || ''}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="checkbox" id="agentEnableSearch" ${agent.enable_search ? 'checked' : ''}> 启用联网搜索
|
||||
<input type="checkbox" id="agentIsOnline" ${agent.is_online ? 'checked' : ''}> 已上线
|
||||
</label>
|
||||
</div>
|
||||
<button class="form-submit" onclick="updateAgent('${agentId}')">保存</button>
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user