初始提交:多智能体任务管理系统
- 完整的 RESTful API(智能体注册、任务管理) - Web 管理界面(实时统计、任务列表) - SQLite 数据库存储 - Python 客户端示例 - 完整的 API 文档
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
*.db
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,429 @@
|
|||||||
|
# 多智能体任务管理系统
|
||||||
|
|
||||||
|
一个用于多智能体协作的任务发布、领取和状态管理服务。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- ✅ **智能体注册与管理** - 智能体可自主注册并维持心跳
|
||||||
|
- ✅ **任务发布系统** - 人类和智能体都可以发布任务
|
||||||
|
- ✅ **任务领取机制** - 智能体可领取待处理的任务
|
||||||
|
- ✅ **状态实时更新** - 跟踪任务和智能体的状态变化
|
||||||
|
- ✅ **Web 管理界面** - 可视化查看和管理任务
|
||||||
|
- ✅ **RESTful API** - 完整的 HTTP API 接口
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 启动服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /data/openclaw/workspace/agent-task-service
|
||||||
|
node server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
服务将在 `http://localhost:3000` 启动。
|
||||||
|
|
||||||
|
### 2. 访问 Web 界面
|
||||||
|
|
||||||
|
在浏览器中打开: `http://localhost:3000`
|
||||||
|
|
||||||
|
## API 文档
|
||||||
|
|
||||||
|
### 智能体相关
|
||||||
|
|
||||||
|
#### 注册智能体
|
||||||
|
```http
|
||||||
|
POST /api/agents/register
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "智能体名称",
|
||||||
|
"type": "general", // 可选: general, search, coding, etc.
|
||||||
|
"capabilities": ["搜索", "编程", "分析"] // 可选
|
||||||
|
}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"name": "智能体名称",
|
||||||
|
"type": "general",
|
||||||
|
"status": "idle",
|
||||||
|
"message": "智能体注册成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取所有智能体
|
||||||
|
```http
|
||||||
|
GET /api/agents
|
||||||
|
|
||||||
|
响应:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"name": "智能体1",
|
||||||
|
"type": "general",
|
||||||
|
"capabilities": ["搜索", "编程"],
|
||||||
|
"status": "idle",
|
||||||
|
"registered_at": 1234567890,
|
||||||
|
"last_heartbeat": 1234567890
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 更新心跳(保活)
|
||||||
|
```http
|
||||||
|
POST /api/agents/:id/heartbeat
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"status": "idle" // idle | busy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 任务相关
|
||||||
|
|
||||||
|
#### 创建任务
|
||||||
|
```http
|
||||||
|
POST /api/tasks
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "任务标题",
|
||||||
|
"description": "任务详细描述",
|
||||||
|
"priority": "normal", // low | normal | high
|
||||||
|
"created_by": "human" // human | agent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"title": "任务标题",
|
||||||
|
"status": "pending",
|
||||||
|
"message": "任务创建成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取所有任务
|
||||||
|
```http
|
||||||
|
GET /api/tasks
|
||||||
|
GET /api/tasks?status=pending // 可选过滤
|
||||||
|
|
||||||
|
响应:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"title": "任务标题",
|
||||||
|
"description": "任务描述",
|
||||||
|
"priority": "normal",
|
||||||
|
"status": "pending",
|
||||||
|
"created_by": "human",
|
||||||
|
"assigned_to": null,
|
||||||
|
"created_at": 1234567890,
|
||||||
|
"updated_at": 1234567890
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 领取任务
|
||||||
|
```http
|
||||||
|
POST /api/tasks/:id/claim
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"agent_id": "智能体ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"message": "任务领取成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 更新任务状态
|
||||||
|
```http
|
||||||
|
PUT /api/tasks/:id
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"status": "completed", // pending | in_progress | completed
|
||||||
|
"result": "任务执行结果" // 可选
|
||||||
|
}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"message": "任务状态更新成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取任务详情
|
||||||
|
```http
|
||||||
|
GET /api/tasks/:id
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"title": "任务标题",
|
||||||
|
"description": "任务描述",
|
||||||
|
"priority": "normal",
|
||||||
|
"status": "completed",
|
||||||
|
"created_by": "human",
|
||||||
|
"assigned_to": "agent_id",
|
||||||
|
"created_at": 1234567890,
|
||||||
|
"updated_at": 1234567890,
|
||||||
|
"completed_at": 1234567890,
|
||||||
|
"result": "任务执行结果"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取统计信息
|
||||||
|
```http
|
||||||
|
GET /api/stats
|
||||||
|
|
||||||
|
响应:
|
||||||
|
{
|
||||||
|
"total_agents": 5,
|
||||||
|
"active_agents": 2,
|
||||||
|
"total_tasks": 20,
|
||||||
|
"pending_tasks": 3,
|
||||||
|
"in_progress_tasks": 2,
|
||||||
|
"completed_tasks": 15
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 智能体集成示例
|
||||||
|
|
||||||
|
### Python 客户端示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
class TaskAgent:
|
||||||
|
def __init__(self, name, base_url="http://localhost:3000"):
|
||||||
|
self.name = name
|
||||||
|
self.base_url = base_url
|
||||||
|
self.agent_id = None
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
"""注册智能体"""
|
||||||
|
response = requests.post(f"{self.base_url}/api/agents/register", json={
|
||||||
|
"name": self.name,
|
||||||
|
"type": "general",
|
||||||
|
"capabilities": ["general_task"]
|
||||||
|
})
|
||||||
|
data = response.json()
|
||||||
|
self.agent_id = data["id"]
|
||||||
|
print(f"✅ 智能体已注册: {self.agent_id}")
|
||||||
|
|
||||||
|
def heartbeat(self, status="idle"):
|
||||||
|
"""发送心跳"""
|
||||||
|
requests.post(
|
||||||
|
f"{self.base_url}/api/agents/{self.agent_id}/heartbeat",
|
||||||
|
json={"status": status}
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_pending_tasks(self):
|
||||||
|
"""获取待处理任务"""
|
||||||
|
response = requests.get(f"{self.base_url}/api/tasks?status=pending")
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def claim_task(self, task_id):
|
||||||
|
"""领取任务"""
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.base_url}/api/tasks/{task_id}/claim",
|
||||||
|
json={"agent_id": self.agent_id}
|
||||||
|
)
|
||||||
|
return response.ok
|
||||||
|
|
||||||
|
def update_task(self, task_id, status, result=None):
|
||||||
|
"""更新任务状态"""
|
||||||
|
payload = {"status": status}
|
||||||
|
if result:
|
||||||
|
payload["result"] = result
|
||||||
|
requests.put(f"{self.base_url}/api/tasks/{task_id}", json=payload)
|
||||||
|
|
||||||
|
def execute_task(self, task):
|
||||||
|
"""执行任务(示例)"""
|
||||||
|
print(f"🔄 正在执行任务: {task['title']}")
|
||||||
|
time.sleep(2) # 模拟任务执行
|
||||||
|
return f"任务 '{task['title']}' 已完成"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""主循环"""
|
||||||
|
self.register()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# 发送心跳
|
||||||
|
self.heartbeat("idle")
|
||||||
|
|
||||||
|
# 获取待处理任务
|
||||||
|
tasks = self.get_pending_tasks()
|
||||||
|
|
||||||
|
if tasks:
|
||||||
|
task = tasks[0]
|
||||||
|
print(f"📋 发现任务: {task['title']}")
|
||||||
|
|
||||||
|
# 领取任务
|
||||||
|
if self.claim_task(task['id']):
|
||||||
|
self.heartbeat("busy")
|
||||||
|
|
||||||
|
# 执行任务
|
||||||
|
result = self.execute_task(task)
|
||||||
|
|
||||||
|
# 更新任务状态
|
||||||
|
self.update_task(task['id'], "completed", result)
|
||||||
|
print(f"✅ 任务完成: {task['id']}")
|
||||||
|
|
||||||
|
self.heartbeat("idle")
|
||||||
|
|
||||||
|
time.sleep(5) # 每5秒检查一次
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("👋 智能体退出")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 错误: {e}")
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# 使用示例
|
||||||
|
if __name__ == "__main__":
|
||||||
|
agent = TaskAgent("示例智能体")
|
||||||
|
agent.run()
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript/Node.js 客户端示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
class TaskAgent {
|
||||||
|
constructor(name, baseUrl = 'http://localhost:3000') {
|
||||||
|
this.name = name;
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
this.agentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async register() {
|
||||||
|
const response = await axios.post(`${this.baseUrl}/api/agents/register`, {
|
||||||
|
name: this.name,
|
||||||
|
type: 'general',
|
||||||
|
capabilities: ['general_task']
|
||||||
|
});
|
||||||
|
this.agentId = response.data.id;
|
||||||
|
console.log(`✅ 智能体已注册: ${this.agentId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async heartbeat(status = 'idle') {
|
||||||
|
await axios.post(
|
||||||
|
`${this.baseUrl}/api/agents/${this.agentId}/heartbeat`,
|
||||||
|
{ status }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPendingTasks() {
|
||||||
|
const response = await axios.get(`${this.baseUrl}/api/tasks?status=pending`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async claimTask(taskId) {
|
||||||
|
try {
|
||||||
|
await axios.post(`${this.baseUrl}/api/tasks/${taskId}/claim`, {
|
||||||
|
agent_id: this.agentId
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateTask(taskId, status, result = null) {
|
||||||
|
const payload = { status };
|
||||||
|
if (result) payload.result = result;
|
||||||
|
await axios.put(`${this.baseUrl}/api/tasks/${taskId}`, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeTask(task) {
|
||||||
|
console.log(`🔄 正在执行任务: ${task.title}`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
return `任务 '${task.title}' 已完成`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
await this.register();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await this.heartbeat('idle');
|
||||||
|
|
||||||
|
const tasks = await this.getPendingTasks();
|
||||||
|
|
||||||
|
if (tasks.length > 0) {
|
||||||
|
const task = tasks[0];
|
||||||
|
console.log(`📋 发现任务: ${task.title}`);
|
||||||
|
|
||||||
|
if (await this.claimTask(task.id)) {
|
||||||
|
await this.heartbeat('busy');
|
||||||
|
|
||||||
|
const result = await this.executeTask(task);
|
||||||
|
|
||||||
|
await this.updateTask(task.id, 'completed', result);
|
||||||
|
console.log(`✅ 任务完成: ${task.id}`);
|
||||||
|
|
||||||
|
await this.heartbeat('idle');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 错误:', error.message);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const agent = new TaskAgent('示例智能体');
|
||||||
|
agent.run().catch(console.error);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据库结构
|
||||||
|
|
||||||
|
系统使用 SQLite 数据库,包含以下表:
|
||||||
|
|
||||||
|
### agents 表
|
||||||
|
- `id` - 智能体唯一标识
|
||||||
|
- `name` - 智能体名称
|
||||||
|
- `type` - 智能体类型
|
||||||
|
- `capabilities` - 能力列表(JSON)
|
||||||
|
- `status` - 状态(idle/busy)
|
||||||
|
- `registered_at` - 注册时间
|
||||||
|
- `last_heartbeat` - 最后心跳时间
|
||||||
|
|
||||||
|
### tasks 表
|
||||||
|
- `id` - 任务唯一标识
|
||||||
|
- `title` - 任务标题
|
||||||
|
- `description` - 任务描述
|
||||||
|
- `priority` - 优先级(low/normal/high)
|
||||||
|
- `status` - 状态(pending/in_progress/completed)
|
||||||
|
- `created_by` - 创建者
|
||||||
|
- `assigned_to` - 分配给的智能体
|
||||||
|
- `created_at` - 创建时间
|
||||||
|
- `updated_at` - 更新时间
|
||||||
|
- `completed_at` - 完成时间
|
||||||
|
- `result` - 任务结果
|
||||||
|
|
||||||
|
## 扩展建议
|
||||||
|
|
||||||
|
1. **认证授权** - 添加 API Key 或 JWT 认证
|
||||||
|
2. **任务队列** - 基于优先级的任务队列
|
||||||
|
3. **任务依赖** - 支持任务之间的依赖关系
|
||||||
|
4. **消息推送** - WebSocket 实时通知
|
||||||
|
5. **日志记录** - 详细的操作日志
|
||||||
|
6. **性能监控** - 任务执行时间和成功率统计
|
||||||
|
7. **分布式部署** - 支持多节点部署
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT
|
||||||
Executable
+157
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
多智能体任务系统 - Python 客户端示例
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class TaskAgent:
|
||||||
|
def __init__(self, name, base_url="http://localhost:3000"):
|
||||||
|
self.name = name
|
||||||
|
self.base_url = base_url
|
||||||
|
self.agent_id = None
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
"""注册智能体"""
|
||||||
|
try:
|
||||||
|
response = requests.post(f"{self.base_url}/api/agents/register", json={
|
||||||
|
"name": self.name,
|
||||||
|
"type": "general",
|
||||||
|
"capabilities": ["general_task", "data_processing"]
|
||||||
|
})
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
self.agent_id = data["id"]
|
||||||
|
print(f"✅ 智能体已注册: {self.agent_id}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 注册失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def heartbeat(self, status="idle"):
|
||||||
|
"""发送心跳"""
|
||||||
|
try:
|
||||||
|
requests.post(
|
||||||
|
f"{self.base_url}/api/agents/{self.agent_id}/heartbeat",
|
||||||
|
json={"status": status},
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 心跳发送失败: {e}")
|
||||||
|
|
||||||
|
def get_pending_tasks(self):
|
||||||
|
"""获取待处理任务"""
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
f"{self.base_url}/api/tasks?status=pending",
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 获取任务失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def claim_task(self, task_id):
|
||||||
|
"""领取任务"""
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.base_url}/api/tasks/{task_id}/claim",
|
||||||
|
json={"agent_id": self.agent_id},
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 领取任务失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_task(self, task_id, status, result=None):
|
||||||
|
"""更新任务状态"""
|
||||||
|
try:
|
||||||
|
payload = {"status": status}
|
||||||
|
if result:
|
||||||
|
payload["result"] = result
|
||||||
|
response = requests.put(
|
||||||
|
f"{self.base_url}/api/tasks/{task_id}",
|
||||||
|
json=payload,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 更新任务失败: {e}")
|
||||||
|
|
||||||
|
def execute_task(self, task):
|
||||||
|
"""执行任务(示例实现)"""
|
||||||
|
print(f"🔄 正在执行任务: {task['title']}")
|
||||||
|
print(f" 描述: {task.get('description', '无')}")
|
||||||
|
print(f" 优先级: {task['priority']}")
|
||||||
|
|
||||||
|
# 模拟任务执行
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
return f"任务 '{task['title']}' 已由 {self.name} 成功完成"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""主循环"""
|
||||||
|
if not self.register():
|
||||||
|
print("❌ 无法注册智能体,退出")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"🤖 智能体 '{self.name}' 开始运行...")
|
||||||
|
print("按 Ctrl+C 退出\n")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# 发送心跳
|
||||||
|
self.heartbeat("idle")
|
||||||
|
|
||||||
|
# 获取待处理任务
|
||||||
|
tasks = self.get_pending_tasks()
|
||||||
|
|
||||||
|
if tasks:
|
||||||
|
task = tasks[0]
|
||||||
|
print(f"\n📋 发现新任务: {task['title']} (ID: {task['id'][:8]}...)")
|
||||||
|
|
||||||
|
# 领取任务
|
||||||
|
if self.claim_task(task['id']):
|
||||||
|
print("✓ 任务已领取")
|
||||||
|
self.heartbeat("busy")
|
||||||
|
|
||||||
|
# 执行任务
|
||||||
|
result = self.execute_task(task)
|
||||||
|
|
||||||
|
# 更新任务状态
|
||||||
|
self.update_task(task['id'], "completed", result)
|
||||||
|
print(f"✅ 任务完成: {task['id'][:8]}...")
|
||||||
|
print(f" 结果: {result}\n")
|
||||||
|
|
||||||
|
self.heartbeat("idle")
|
||||||
|
else:
|
||||||
|
print("⚠️ 任务可能已被其他智能体领取\n")
|
||||||
|
|
||||||
|
# 等待一段时间再检查
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\n👋 收到退出信号,智能体停止运行")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 运行时错误: {e}")
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
agent_name = sys.argv[1]
|
||||||
|
else:
|
||||||
|
agent_name = f"Python智能体-{int(time.time()) % 10000}"
|
||||||
|
|
||||||
|
base_url = sys.argv[2] if len(sys.argv) > 2 else "http://localhost:3000"
|
||||||
|
|
||||||
|
agent = TaskAgent(agent_name, base_url)
|
||||||
|
agent.run()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Generated
+2256
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "agent-task-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^2.2.2",
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"sqlite3": "^5.1.7",
|
||||||
|
"uuid": "^13.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,459 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>多智能体任务管理系统</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card h3 {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .number {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel h2 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-item, .task-item {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-left: 4px solid #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-item.busy {
|
||||||
|
border-left-color: #f39c12;
|
||||||
|
background: #fff9e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-item.offline {
|
||||||
|
border-left-color: #95a5a6;
|
||||||
|
background: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item.pending {
|
||||||
|
border-left-color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item.in_progress {
|
||||||
|
border-left-color: #f39c12;
|
||||||
|
background: #fff9e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item.completed {
|
||||||
|
border-left-color: #27ae60;
|
||||||
|
background: #e8f8f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.idle {
|
||||||
|
background: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.busy {
|
||||||
|
background: #f39c12;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.pending {
|
||||||
|
background: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.in_progress {
|
||||||
|
background: #f39c12;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.completed {
|
||||||
|
background: #27ae60;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.high {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.normal {
|
||||||
|
background: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.low {
|
||||||
|
background: #95a5a6;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #764ba2;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input, .form-group textarea, .form-group select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-meta {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#createTaskForm {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
float: right;
|
||||||
|
padding: 5px 15px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<h1>🤖 多智能体任务管理系统</h1>
|
||||||
|
<p>Multi-Agent Task Management System</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="stats" id="stats">
|
||||||
|
<!-- 统计数据将动态加载 -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="panel">
|
||||||
|
<h2>
|
||||||
|
智能体列表
|
||||||
|
<button class="refresh-btn" onclick="loadAgents()">刷新</button>
|
||||||
|
</h2>
|
||||||
|
<div id="agentList">
|
||||||
|
<div class="empty-state">加载中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel">
|
||||||
|
<h2>
|
||||||
|
任务列表
|
||||||
|
<button class="refresh-btn" onclick="loadTasks()">刷新</button>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div id="taskList">
|
||||||
|
<div class="empty-state">加载中...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="createTaskForm">
|
||||||
|
<h3 style="margin-top: 30px; margin-bottom: 15px;">创建新任务</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>任务标题 *</label>
|
||||||
|
<input type="text" id="taskTitle" required placeholder="输入任务标题">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>任务描述</label>
|
||||||
|
<textarea id="taskDescription" placeholder="详细描述任务内容"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>优先级</label>
|
||||||
|
<select id="taskPriority">
|
||||||
|
<option value="low">低</option>
|
||||||
|
<option value="normal" selected>普通</option>
|
||||||
|
<option value="high">高</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit">创建任务</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const API_BASE = '';
|
||||||
|
|
||||||
|
// 加载统计数据
|
||||||
|
async function loadStats() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/stats`);
|
||||||
|
const stats = await response.json();
|
||||||
|
|
||||||
|
document.getElementById('stats').innerHTML = `
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>智能体总数</h3>
|
||||||
|
<div class="number">${stats.total_agents || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>活跃智能体</h3>
|
||||||
|
<div class="number">${stats.active_agents || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>待处理任务</h3>
|
||||||
|
<div class="number">${stats.pending_tasks || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>进行中任务</h3>
|
||||||
|
<div class="number">${stats.in_progress_tasks || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>已完成任务</h3>
|
||||||
|
<div class="number">${stats.completed_tasks || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>任务总数</h3>
|
||||||
|
<div class="number">${stats.total_tasks || 0}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载统计数据失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载智能体列表
|
||||||
|
async function loadAgents() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/agents`);
|
||||||
|
const agents = await response.json();
|
||||||
|
|
||||||
|
const agentList = document.getElementById('agentList');
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
agentList.innerHTML = '<div class="empty-state">暂无智能体注册</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
agentList.innerHTML = agents.map(agent => {
|
||||||
|
const statusClass = agent.status === 'busy' ? 'busy' : 'idle';
|
||||||
|
const lastHeartbeat = new Date(agent.last_heartbeat).toLocaleString('zh-CN');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="agent-item ${statusClass}">
|
||||||
|
<strong>${agent.name}</strong>
|
||||||
|
<span class="badge ${agent.status}">${agent.status === 'idle' ? '空闲' : '忙碌'}</span>
|
||||||
|
<div class="task-meta">
|
||||||
|
类型: ${agent.type || '通用'} |
|
||||||
|
注册时间: ${new Date(agent.registered_at).toLocaleString('zh-CN')} |
|
||||||
|
最后心跳: ${lastHeartbeat}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载智能体列表失败:', error);
|
||||||
|
document.getElementById('agentList').innerHTML =
|
||||||
|
'<div class="empty-state">加载失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载任务列表
|
||||||
|
async function loadTasks() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/tasks`);
|
||||||
|
const tasks = await response.json();
|
||||||
|
|
||||||
|
const taskList = document.getElementById('taskList');
|
||||||
|
|
||||||
|
if (tasks.length === 0) {
|
||||||
|
taskList.innerHTML = '<div class="empty-state">暂无任务</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskList.innerHTML = tasks.map(task => {
|
||||||
|
const statusText = {
|
||||||
|
'pending': '待处理',
|
||||||
|
'in_progress': '进行中',
|
||||||
|
'completed': '已完成'
|
||||||
|
}[task.status] || task.status;
|
||||||
|
|
||||||
|
const priorityText = {
|
||||||
|
'low': '低',
|
||||||
|
'normal': '普通',
|
||||||
|
'high': '高'
|
||||||
|
}[task.priority] || task.priority;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="task-item ${task.status}">
|
||||||
|
<strong>${task.title}</strong>
|
||||||
|
<span class="badge ${task.status}">${statusText}</span>
|
||||||
|
<span class="badge ${task.priority}">${priorityText}</span>
|
||||||
|
${task.description ? `<div style="margin-top: 8px; color: #666;">${task.description}</div>` : ''}
|
||||||
|
<div class="task-meta">
|
||||||
|
创建时间: ${new Date(task.created_at).toLocaleString('zh-CN')} |
|
||||||
|
${task.assigned_to ? `执行者: ${task.assigned_to}` : '未分配'} |
|
||||||
|
${task.completed_at ? `完成时间: ${new Date(task.completed_at).toLocaleString('zh-CN')}` : ''}
|
||||||
|
</div>
|
||||||
|
${task.result ? `<div style="margin-top: 8px; padding: 8px; background: #e8f4f8; border-radius: 4px;"><strong>结果:</strong> ${task.result}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载任务列表失败:', error);
|
||||||
|
document.getElementById('taskList').innerHTML =
|
||||||
|
'<div class="empty-state">加载失败</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建任务表单提交
|
||||||
|
document.getElementById('createTaskForm').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const title = document.getElementById('taskTitle').value;
|
||||||
|
const description = document.getElementById('taskDescription').value;
|
||||||
|
const priority = document.getElementById('taskPriority').value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/tasks`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
priority,
|
||||||
|
created_by: 'human'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
alert('✅ 任务创建成功!');
|
||||||
|
document.getElementById('createTaskForm').reset();
|
||||||
|
loadTasks();
|
||||||
|
loadStats();
|
||||||
|
} else {
|
||||||
|
alert('❌ 创建失败: ' + result.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('❌ 创建失败: ' + error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
|
loadStats();
|
||||||
|
loadAgents();
|
||||||
|
loadTasks();
|
||||||
|
|
||||||
|
// 自动刷新(每10秒)
|
||||||
|
setInterval(() => {
|
||||||
|
loadStats();
|
||||||
|
loadAgents();
|
||||||
|
loadTasks();
|
||||||
|
}, 10000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const sqlite3 = require('sqlite3').verbose();
|
||||||
|
const cors = require('cors');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// 中间件
|
||||||
|
app.use(cors());
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// 初始化数据库
|
||||||
|
const db = new sqlite3.Database('./agent_tasks.db', (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('数据库连接失败:', err.message);
|
||||||
|
} else {
|
||||||
|
console.log('✅ 已连接到 SQLite 数据库');
|
||||||
|
initDatabase();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建表结构
|
||||||
|
function initDatabase() {
|
||||||
|
db.serialize(() => {
|
||||||
|
// 智能体表
|
||||||
|
db.run(`CREATE TABLE IF NOT EXISTS agents (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT,
|
||||||
|
capabilities TEXT,
|
||||||
|
status TEXT DEFAULT 'idle',
|
||||||
|
registered_at INTEGER,
|
||||||
|
last_heartbeat INTEGER
|
||||||
|
)`);
|
||||||
|
|
||||||
|
// 任务表
|
||||||
|
db.run(`CREATE TABLE IF NOT EXISTS tasks (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
priority TEXT DEFAULT 'normal',
|
||||||
|
status TEXT DEFAULT 'pending',
|
||||||
|
created_by TEXT,
|
||||||
|
assigned_to TEXT,
|
||||||
|
created_at INTEGER,
|
||||||
|
updated_at INTEGER,
|
||||||
|
completed_at INTEGER,
|
||||||
|
result TEXT
|
||||||
|
)`);
|
||||||
|
|
||||||
|
console.log('✅ 数据库表已初始化');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== API 路由 ====================
|
||||||
|
|
||||||
|
// 1. 智能体注册
|
||||||
|
app.post('/api/agents/register', (req, res) => {
|
||||||
|
const { name, type, capabilities } = req.body;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
return res.status(400).json({ error: '智能体名称不能为空' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = uuidv4();
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`INSERT INTO agents (id, name, type, capabilities, status, registered_at, last_heartbeat)
|
||||||
|
VALUES (?, ?, ?, ?, 'idle', ?, ?)`,
|
||||||
|
[id, name, type || 'general', JSON.stringify(capabilities || []), now, now],
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type: type || 'general',
|
||||||
|
status: 'idle',
|
||||||
|
message: '智能体注册成功'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 获取所有智能体
|
||||||
|
app.get('/api/agents', (req, res) => {
|
||||||
|
db.all('SELECT * FROM agents ORDER BY registered_at DESC', [], (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json(rows.map(row => ({
|
||||||
|
...row,
|
||||||
|
capabilities: JSON.parse(row.capabilities || '[]')
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. 更新智能体状态(心跳)
|
||||||
|
app.post('/api/agents/:id/heartbeat', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { status } = req.body;
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
'UPDATE agents SET status = ?, last_heartbeat = ? WHERE id = ?',
|
||||||
|
[status || 'idle', Date.now(), id],
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
if (this.changes === 0) {
|
||||||
|
return res.status(404).json({ error: '智能体不存在' });
|
||||||
|
}
|
||||||
|
res.json({ message: '心跳更新成功' });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 创建任务
|
||||||
|
app.post('/api/tasks', (req, res) => {
|
||||||
|
const { title, description, priority, created_by } = req.body;
|
||||||
|
|
||||||
|
if (!title) {
|
||||||
|
return res.status(400).json({ error: '任务标题不能为空' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = uuidv4();
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`INSERT INTO tasks (id, title, description, priority, status, created_by, created_at, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, 'pending', ?, ?, ?)`,
|
||||||
|
[id, title, description || '', priority || 'normal', created_by || 'human', now, now],
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
status: 'pending',
|
||||||
|
message: '任务创建成功'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. 获取所有任务
|
||||||
|
app.get('/api/tasks', (req, res) => {
|
||||||
|
const { status } = req.query;
|
||||||
|
|
||||||
|
let query = 'SELECT * FROM tasks';
|
||||||
|
let params = [];
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
query += ' WHERE status = ?';
|
||||||
|
params.push(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
query += ' ORDER BY created_at DESC';
|
||||||
|
|
||||||
|
db.all(query, params, (err, rows) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. 领取任务
|
||||||
|
app.post('/api/tasks/:id/claim', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { agent_id } = req.body;
|
||||||
|
|
||||||
|
if (!agent_id) {
|
||||||
|
return res.status(400).json({ error: '需要提供智能体ID' });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.get('SELECT status FROM tasks WHERE id = ?', [id], (err, task) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
if (!task) {
|
||||||
|
return res.status(404).json({ error: '任务不存在' });
|
||||||
|
}
|
||||||
|
if (task.status !== 'pending') {
|
||||||
|
return res.status(400).json({ error: '任务已被领取或已完成' });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
'UPDATE tasks SET status = ?, assigned_to = ?, updated_at = ? WHERE id = ?',
|
||||||
|
['in_progress', agent_id, Date.now(), id],
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时更新智能体状态
|
||||||
|
db.run('UPDATE agents SET status = ? WHERE id = ?', ['busy', agent_id]);
|
||||||
|
|
||||||
|
res.json({ message: '任务领取成功' });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. 更新任务状态
|
||||||
|
app.put('/api/tasks/:id', (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const { status, result } = req.body;
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const updates = [];
|
||||||
|
const params = [];
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
updates.push('status = ?');
|
||||||
|
params.push(status);
|
||||||
|
}
|
||||||
|
if (result !== undefined) {
|
||||||
|
updates.push('result = ?');
|
||||||
|
params.push(result);
|
||||||
|
}
|
||||||
|
if (status === 'completed') {
|
||||||
|
updates.push('completed_at = ?');
|
||||||
|
params.push(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.push('updated_at = ?');
|
||||||
|
params.push(now);
|
||||||
|
params.push(id);
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`UPDATE tasks SET ${updates.join(', ')} WHERE id = ?`,
|
||||||
|
params,
|
||||||
|
function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
if (this.changes === 0) {
|
||||||
|
return res.status(404).json({ error: '任务不存在' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果任务完成,将智能体状态改为 idle
|
||||||
|
if (status === 'completed') {
|
||||||
|
db.get('SELECT assigned_to FROM tasks WHERE id = ?', [id], (err, task) => {
|
||||||
|
if (task && task.assigned_to) {
|
||||||
|
db.run('UPDATE agents SET status = ? WHERE id = ?', ['idle', task.assigned_to]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ message: '任务状态更新成功' });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 8. 获取单个任务详情
|
||||||
|
app.get('/api/tasks/:id', (req, res) => {
|
||||||
|
db.get('SELECT * FROM tasks WHERE id = ?', [req.params.id], (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
if (!row) {
|
||||||
|
return res.status(404).json({ error: '任务不存在' });
|
||||||
|
}
|
||||||
|
res.json(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 9. 统计信息
|
||||||
|
app.get('/api/stats', (req, res) => {
|
||||||
|
const stats = {
|
||||||
|
total_agents: 0,
|
||||||
|
active_agents: 0,
|
||||||
|
total_tasks: 0,
|
||||||
|
pending_tasks: 0,
|
||||||
|
in_progress_tasks: 0,
|
||||||
|
completed_tasks: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as total FROM agents', [], (err, row) => {
|
||||||
|
if (!err && row) stats.total_agents = row.total;
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as active FROM agents WHERE status = "busy"', [], (err, row) => {
|
||||||
|
if (!err && row) stats.active_agents = row.active;
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as total FROM tasks', [], (err, row) => {
|
||||||
|
if (!err && row) stats.total_tasks = row.total;
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as pending FROM tasks WHERE status = "pending"', [], (err, row) => {
|
||||||
|
if (!err && row) stats.pending_tasks = row.pending;
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as in_progress FROM tasks WHERE status = "in_progress"', [], (err, row) => {
|
||||||
|
if (!err && row) stats.in_progress_tasks = row.in_progress;
|
||||||
|
|
||||||
|
db.get('SELECT COUNT(*) as completed FROM tasks WHERE status = "completed"', [], (err, row) => {
|
||||||
|
if (!err && row) stats.completed_tasks = row.completed;
|
||||||
|
res.json(stats);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启动服务器
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`🚀 多智能体任务服务已启动: http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 优雅关闭
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
db.close((err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err.message);
|
||||||
|
}
|
||||||
|
console.log('数据库连接已关闭');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user