feat: 多智能体竞标调度系统 v1.0.0
核心组件: - Orchestrator: 意图理解、任务拆分、竞标管理、结果验证 - Worker: 竞标任务、执行交付 - TaskBoard: 状态管理、信息存储 - BidEvaluator: 竞标评估算法 - ExecutionMonitor: 执行监控、超时处理 - LLMClient: 大模型接口调用 功能特性: - 竞标机制:Agent主动竞争任务 - 动态调度:串行/并行任务智能调度 - 智能容错:超时切换、验证重试 - 质量保证:结果验证、历史追踪 Web界面:首页、请求列表、任务列表、Agent管理 API接口:请求/任务/Agent管理、测试接口 端口:19015
This commit is contained in:
213
templates/agents.html
Normal file
213
templates/agents.html
Normal file
@@ -0,0 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Agent管理 - 多智能体竞标调度系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f5f7fa; }
|
||||
.agent-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.capability-tag {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
padding: 5px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.75rem;
|
||||
margin: 2px;
|
||||
}
|
||||
.stat-item {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/"><i class="ri-robot-2-line"></i> 多智能体竞标调度系统</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="/">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/requests">请求列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tasks">任务列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="/agents">Agent管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4><i class="ri-robot-line"></i> Agent管理</h4>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
|
||||
<i class="ri-add-line"></i> 注册Agent
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="agents-list" class="row">
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="ri-loader-4-line fs-1"></i>
|
||||
<p>加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加Agent模态框 -->
|
||||
<div class="modal fade" id="addModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="ri-robot-add-line"></i> 注册新Agent</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="agent-form">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Agent ID</label>
|
||||
<input type="text" class="form-control" id="agent-id" placeholder="如: my_agent">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">名称</label>
|
||||
<input type="text" class="form-control" id="agent-name" placeholder="如: 我的助手">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">描述</label>
|
||||
<textarea class="form-control" id="agent-desc" rows="2" placeholder="Agent的功能描述"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">能力标签(逗号分隔)</label>
|
||||
<input type="text" class="form-control" id="agent-caps" placeholder="如: coding, search, writing">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">最大并发任务</label>
|
||||
<input type="number" class="form-control" id="agent-max" value="1" min="1" max="10">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="addAgent()">
|
||||
<i class="ri-check-line"></i> 注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function loadAgents() {
|
||||
fetch('/api/agents')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const list = document.getElementById('agents-list');
|
||||
|
||||
if (!data.length) {
|
||||
list.innerHTML = `
|
||||
<div class="col-12 text-center text-muted py-5">
|
||||
<i class="ri-robot-off-line fs-1"></i>
|
||||
<p>暂无注册的Agent</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = data.map(a => `
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="agent-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h5><i class="ri-robot-2-fill text-primary"></i> ${a.name}</h5>
|
||||
<span class="text-muted small">#${a.id}</span>
|
||||
</div>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="removeAgent('${a.id}')">
|
||||
<i class="ri-delete-bin-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-muted small">${a.description || '暂无描述'}</p>
|
||||
<div class="mb-3">
|
||||
${a.capabilities.map(c => `<span class="capability-tag">${c}</span>`).join('')}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="stat-item">
|
||||
<small class="text-muted">任务数</small>
|
||||
<h6>${a.total_tasks}</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="stat-item">
|
||||
<small class="text-muted">成功率</small>
|
||||
<h6>${(a.success_rate * 100).toFixed(0)}%</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="stat-item">
|
||||
<small class="text-muted">平均时间</small>
|
||||
<h6>${a.avg_completion_time.toFixed(0)}s</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
});
|
||||
}
|
||||
|
||||
function addAgent() {
|
||||
const id = document.getElementById('agent-id').value.trim();
|
||||
const name = document.getElementById('agent-name').value.trim();
|
||||
const desc = document.getElementById('agent-desc').value.trim();
|
||||
const caps = document.getElementById('agent-caps').value.split(',').map(c => c.trim()).filter(c => c);
|
||||
const max = parseInt(document.getElementById('agent-max').value) || 1;
|
||||
|
||||
if (!id) {
|
||||
alert('请填写 Agent ID');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/agent', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
id, name, description: desc,
|
||||
capabilities: caps,
|
||||
max_concurrent_tasks: max
|
||||
})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
alert('错误: ' + data.error);
|
||||
} else {
|
||||
bootstrap.Modal.getOrCreateInstance(document.getElementById('addModal')).hide();
|
||||
document.getElementById('agent-form').reset();
|
||||
loadAgents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeAgent(id) {
|
||||
if (!confirm('确定注销此Agent?')) return;
|
||||
|
||||
fetch(`/api/agent/${id}`, {method: 'DELETE'})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
loadAgents();
|
||||
});
|
||||
}
|
||||
|
||||
loadAgents();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
279
templates/index.html
Normal file
279
templates/index.html
Normal file
@@ -0,0 +1,279 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>多智能体竞标调度系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f5f7fa; }
|
||||
.hero-section {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 60px 0;
|
||||
}
|
||||
.stat-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
.feature-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.input-area {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
.result-area {
|
||||
background: #1a1a2e;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
color: #eee;
|
||||
min-height: 300px;
|
||||
}
|
||||
.agent-badge {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="ri-robot-2-line"></i> 多智能体竞标调度系统
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link active" href="/">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/requests">请求列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tasks">任务列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/agents">Agent管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="hero-section">
|
||||
<div class="container text-center">
|
||||
<h1 class="display-4"><i class="ri-robot-2-fill"></i> 多智能体竞标调度系统</h1>
|
||||
<p class="lead">任务竞标 · 动态调度 · 智能容错</p>
|
||||
<div class="mt-4">
|
||||
<span class="agent-badge me-2"><i class="ri-code-line"></i> 代码专家</span>
|
||||
<span class="agent-badge me-2"><i class="ri-search-line"></i> 搜索专家</span>
|
||||
<span class="agent-badge me-2"><i class="ri-edit-line"></i> 写作专家</span>
|
||||
<span class="agent-badge"><i class="ri-bar-chart-line"></i> 分析专家</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container py-4">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card text-center">
|
||||
<i class="ri-message-3-line fs-1 text-primary"></i>
|
||||
<h3 id="stat-requests">0</h3>
|
||||
<p class="text-muted">用户请求</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card text-center">
|
||||
<i class="ri-task-line fs-1 text-success"></i>
|
||||
<h3 id="stat-tasks">0</h3>
|
||||
<p class="text-muted">任务总数</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card text-center">
|
||||
<i class="ri-robot-line fs-1 text-warning"></i>
|
||||
<h3 id="stat-agents">4</h3>
|
||||
<p class="text-muted">注册Agent</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card text-center">
|
||||
<i class="ri-flashlight-line fs-1 text-danger"></i>
|
||||
<h3 id="stat-active">0</h3>
|
||||
<p class="text-muted">活跃执行</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="input-area">
|
||||
<h5><i class="ri-chat-3-line"></i> 发送请求</h5>
|
||||
<div class="mb-3">
|
||||
<textarea id="user-input" class="form-control" rows="5"
|
||||
placeholder="输入你的请求,例如:帮我写一个Python爬虫,爬取新闻网站..."></textarea>
|
||||
</div>
|
||||
<button id="submit-btn" class="btn btn-primary w-100">
|
||||
<i class="ri-send-plane-fill"></i> 发送请求
|
||||
</button>
|
||||
|
||||
<div class="mt-3">
|
||||
<h6>快速测试:</h6>
|
||||
<button class="btn btn-outline-secondary btn-sm me-2" onclick="quickTest('intent')">意图分析</button>
|
||||
<button class="btn btn-outline-secondary btn-sm me-2" onclick="quickTest('split')">任务拆分</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="quickTest('full')">完整流程</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="result-area">
|
||||
<h6><i class="ri-terminal-line"></i> 执行日志</h6>
|
||||
<div id="log-output" style="font-family: monospace; white-space: pre-wrap; max-height: 280px; overflow-y: auto;">
|
||||
等待输入...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功能介绍 -->
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card">
|
||||
<i class="ri-award-line fs-1 text-primary"></i>
|
||||
<h5 class="mt-3">竞标机制</h5>
|
||||
<p>Agent主动竞争任务,展示能力和方案,规划Agent综合评估选择最佳执行者。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card">
|
||||
<i class="ri-flow-chart fs-1 text-success"></i>
|
||||
<h5 class="mt-3">动态调度</h5>
|
||||
<p>串行任务严格按依赖执行,并行任务最大化并发,智能分配执行顺序。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card">
|
||||
<i class="ri-shield-check-line fs-1 text-warning"></i>
|
||||
<h5 class="mt-3">智能容错</h5>
|
||||
<p>超时自动切换备选Agent,验证失败给予重试机会,失败记录用于改进。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 加载统计
|
||||
function loadStats() {
|
||||
fetch('/api/stats')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('stat-requests').textContent = data.total_requests || 0;
|
||||
document.getElementById('stat-tasks').textContent = data.total_tasks || 0;
|
||||
document.getElementById('stat-agents').textContent = data.total_agents || 0;
|
||||
document.getElementById('stat-active').textContent = data.active_executions || 0;
|
||||
});
|
||||
}
|
||||
|
||||
// 日志输出
|
||||
function log(text, type = 'info') {
|
||||
const logArea = document.getElementById('log-output');
|
||||
const time = new Date().toLocaleTimeString();
|
||||
const color = type === 'error' ? '#ff6b6b' : type === 'success' ? '#51cf66' : '#74c0fc';
|
||||
logArea.innerHTML += `<span style="color:${color}">[${time}] ${text}</span>\n`;
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
|
||||
// 提交请求
|
||||
document.getElementById('submit-btn').addEventListener('click', async () => {
|
||||
const input = document.getElementById('user-input').value.trim();
|
||||
if (!input) {
|
||||
log('请输入请求内容', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
log('发送请求...');
|
||||
document.getElementById('submit-btn').disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/request', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({content: input})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
log(`错误: ${data.error}`, 'error');
|
||||
} else {
|
||||
log(`请求ID: ${data.id}`, 'success');
|
||||
log(`状态: ${data.status}`);
|
||||
|
||||
if (data.task_graph) {
|
||||
const tasks = Object.keys(data.task_graph.tasks);
|
||||
log(`拆分任务: ${tasks.length} 个`);
|
||||
}
|
||||
|
||||
if (data.final_result) {
|
||||
log(`结果: ${JSON.stringify(data.final_result)}`, 'success');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
log(`请求失败: ${e}`, 'error');
|
||||
}
|
||||
|
||||
document.getElementById('submit-btn').disabled = false;
|
||||
loadStats();
|
||||
});
|
||||
|
||||
// 快速测试
|
||||
async function quickTest(type) {
|
||||
const input = document.getElementById('user-input').value.trim() || '帮我写一个Python爬虫';
|
||||
log(`测试 ${type}...`);
|
||||
|
||||
try {
|
||||
const url = type === 'intent' ? '/api/test_intent' :
|
||||
type === 'split' ? '/api/test_split' : '/api/request';
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(type === 'full' ? {content: input} : {prompt: input})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
log(`错误: ${data.error}`, 'error');
|
||||
} else {
|
||||
log(`结果:`, 'success');
|
||||
log(JSON.stringify(data.intent || data.tasks || data, null, 2));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
log(`测试失败: ${e}`, 'error');
|
||||
}
|
||||
|
||||
loadStats();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
loadStats();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
113
templates/requests.html
Normal file
113
templates/requests.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>请求列表 - 多智能体竞标调度系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f5f7fa; }
|
||||
.request-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.status-badge {
|
||||
padding: 5px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.status-pending { background: #ffeaa7; color: #6c5ce7; }
|
||||
.status-processing { background: #74b9ff; color: white; }
|
||||
.status-completed { background: #00b894; color: white; }
|
||||
.status-failed { background: #e17055; color: white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/"><i class="ri-robot-2-line"></i> 多智能体竞标调度系统</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="/">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="/requests">请求列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/tasks">任务列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/agents">Agent管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4><i class="ri-message-3-line"></i> 用户请求列表</h4>
|
||||
<button class="btn btn-outline-primary btn-sm" onclick="loadRequests()">
|
||||
<i class="ri-refresh-line"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="requests-list">
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="ri-loader-4-line fs-1"></i>
|
||||
<p>加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function loadRequests() {
|
||||
fetch('/api/requests')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const list = document.getElementById('requests-list');
|
||||
|
||||
if (!data.length) {
|
||||
list.innerHTML = `
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="ri-inbox-line fs-1"></i>
|
||||
<p>暂无请求记录</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = data.map(r => `
|
||||
<div class="request-card">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="text-muted small">#${r.id}</span>
|
||||
<p class="mt-2">${r.content.substring(0, 100)}${r.content.length > 100 ? '...' : ''}</p>
|
||||
</div>
|
||||
<span class="status-badge status-${r.status}">${r.status}</span>
|
||||
</div>
|
||||
<div class="mt-3 text-muted small">
|
||||
<i class="ri-time-line"></i> ${new Date(r.created_at * 1000).toLocaleString()}
|
||||
${r.task_graph ? ` · <i class="ri-task-line"></i> ${Object.keys(r.task_graph.tasks).length} 个任务` : ''}
|
||||
${r.error ? ` · <span class="text-danger">${r.error}</span>` : ''}
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm" onclick="showDetail('${r.id}')">
|
||||
<i class="ri-eye-line"></i> 详情
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
});
|
||||
}
|
||||
|
||||
function showDetail(id) {
|
||||
fetch(`/api/request/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
alert(JSON.stringify(data, null, 2));
|
||||
});
|
||||
}
|
||||
|
||||
loadRequests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
139
templates/tasks.html
Normal file
139
templates/tasks.html
Normal file
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>任务列表 - 多智能体竞标调度系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f5f7fa; }
|
||||
.task-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.status-badge {
|
||||
padding: 5px 12px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.status-pending { background: #ffeaa7; color: #6c5ce7; }
|
||||
.status-published { background: #dfe6e9; color: #2d3436; }
|
||||
.status-bidding { background: #fdcb6e; color: white; }
|
||||
.status-assigned { background: #74b9ff; color: white; }
|
||||
.status-executing { background: #a29bfe; color: white; }
|
||||
.status-completed { background: #00b894; color: white; }
|
||||
.status-failed { background: #e17055; color: white; }
|
||||
.type-badge {
|
||||
background: #e1e5eb;
|
||||
padding: 3px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/"><i class="ri-robot-2-line"></i> 多智能体竞标调度系统</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="/">首页</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/requests">请求列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link active" href="/tasks">任务列表</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/agents">Agent管理</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4><i class="ri-task-line"></i> 任务列表</h4>
|
||||
<div>
|
||||
<select id="status-filter" class="form-select form-select-sm" style="width: auto;" onchange="loadTasks()">
|
||||
<option value="">全部状态</option>
|
||||
<option value="pending">待发布</option>
|
||||
<option value="published">已发布</option>
|
||||
<option value="bidding">竞标中</option>
|
||||
<option value="executing">执行中</option>
|
||||
<option value="completed">已完成</option>
|
||||
<option value="failed">已失败</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-primary btn-sm ms-2" onclick="loadTasks()">
|
||||
<i class="ri-refresh-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tasks-list">
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="ri-loader-4-line fs-1"></i>
|
||||
<p>加载中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function loadTasks() {
|
||||
const status = document.getElementById('status-filter').value;
|
||||
fetch('/api/tasks')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (status) {
|
||||
data = data.filter(t => t.status === status);
|
||||
}
|
||||
|
||||
const list = document.getElementById('tasks-list');
|
||||
|
||||
if (!data.length) {
|
||||
list.innerHTML = `
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="ri-inbox-line fs-1"></i>
|
||||
<p>暂无任务</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = data.map(t => `
|
||||
<div class="task-card">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="text-muted small">#${t.id}</span>
|
||||
<span class="type-badge ms-2">${t.type}</span>
|
||||
<p class="mt-2">${t.description.substring(0, 80)}${t.description.length > 80 ? '...' : ''}</p>
|
||||
</div>
|
||||
<span class="status-badge status-${t.status}">${t.status}</span>
|
||||
</div>
|
||||
<div class="mt-3 text-muted small">
|
||||
<i class="ri-time-line"></i> ${new Date(t.created_at * 1000).toLocaleString()}
|
||||
${t.dependencies.length ? ` · <i class="ri-link"></i> 依赖: ${t.dependencies.join(', ')}` : ''}
|
||||
· <i class="ri-timer-line"></i> 超时: ${t.timeout}秒
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm" onclick="showDetail('${t.id}')">
|
||||
<i class="ri-eye-line"></i> 详情
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
});
|
||||
}
|
||||
|
||||
function showDetail(id) {
|
||||
fetch(`/api/task/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
alert(JSON.stringify(data, null, 2));
|
||||
});
|
||||
}
|
||||
|
||||
loadTasks();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user