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:
2026-04-27 14:15:14 +08:00
parent c725aeb192
commit f85991064f
2 changed files with 145 additions and 41 deletions

View File

@@ -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);