144 lines
4.3 KiB
JavaScript
144 lines
4.3 KiB
JavaScript
/**
|
||
* ═══════════════════════════════════════════════════════════
|
||
* 🤖 AGE OS · Agent 调度引擎
|
||
* ═══════════════════════════════════════════════════════════
|
||
*
|
||
* 签发: 铸渊 · ICE-GL-ZY001
|
||
* 版权: 国作登字-2026-A-00037559
|
||
*
|
||
* 从 agent_configs 表读取配置,用 node-cron 调度定时任务。
|
||
* 每个Agent是一个独立的JS脚本,调度器负责按时触发并记录日志。
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
const cron = require('node-cron');
|
||
const path = require('path');
|
||
const db = require('../mcp-server/db');
|
||
|
||
const AGENT_DIR = __dirname;
|
||
const activeJobs = new Map();
|
||
|
||
/**
|
||
* 加载并启动所有启用的定时Agent
|
||
*/
|
||
async function startScheduler() {
|
||
console.log('[Scheduler] AGE OS Agent 调度引擎启动...');
|
||
|
||
try {
|
||
const result = await db.query(
|
||
"SELECT * FROM agent_configs WHERE enabled = true AND cron_schedule IS NOT NULL"
|
||
);
|
||
|
||
console.log(`[Scheduler] 发现 ${result.rows.length} 个定时Agent`);
|
||
|
||
for (const agent of result.rows) {
|
||
scheduleAgent(agent);
|
||
}
|
||
|
||
console.log('[Scheduler] 所有Agent已调度');
|
||
} catch (err) {
|
||
console.error('[Scheduler] 启动失败:', err.message);
|
||
console.log('[Scheduler] 数据库可能未初始化,等待60秒后重试...');
|
||
setTimeout(startScheduler, 60000);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调度单个Agent
|
||
*/
|
||
function scheduleAgent(agent) {
|
||
if (!cron.validate(agent.cron_schedule)) {
|
||
console.error(`[Scheduler] ${agent.agent_id} cron表达式无效: ${agent.cron_schedule}`);
|
||
return;
|
||
}
|
||
|
||
const job = cron.schedule(agent.cron_schedule, async () => {
|
||
await runAgent(agent);
|
||
}, {
|
||
timezone: 'Asia/Shanghai'
|
||
});
|
||
|
||
activeJobs.set(agent.agent_id, job);
|
||
console.log(`[Scheduler] ${agent.agent_id} (${agent.name}) 已调度: ${agent.cron_schedule}`);
|
||
}
|
||
|
||
/**
|
||
* 执行单个Agent
|
||
*/
|
||
async function runAgent(agent) {
|
||
const startTime = Date.now();
|
||
console.log(`[Agent] ${agent.agent_id} 开始执行...`);
|
||
|
||
// 更新运行状态
|
||
await db.query(
|
||
"UPDATE agent_configs SET last_run_at = NOW(), last_run_status = 'running' WHERE agent_id = $1",
|
||
[agent.agent_id]
|
||
).catch(() => {});
|
||
|
||
try {
|
||
// 动态加载Agent脚本
|
||
const scriptPath = path.resolve(AGENT_DIR, path.basename(agent.script_path));
|
||
|
||
// 清除缓存以支持热更新
|
||
delete require.cache[require.resolve(scriptPath)];
|
||
const agentModule = require(scriptPath);
|
||
|
||
// 每个Agent脚本需导出 run(config) 函数
|
||
if (typeof agentModule.run !== 'function') {
|
||
throw new Error(`Agent脚本 ${agent.script_path} 未导出 run 函数`);
|
||
}
|
||
|
||
const result = await agentModule.run({
|
||
agent_id: agent.agent_id,
|
||
model_config: agent.model_config,
|
||
allowed_tools: agent.allowed_tools
|
||
});
|
||
|
||
const duration = Date.now() - startTime;
|
||
|
||
// 记录成功日志
|
||
await db.query(
|
||
`INSERT INTO agent_logs (agent_id, status, message, details, duration_ms)
|
||
VALUES ($1, 'success', $2, $3, $4)`,
|
||
[agent.agent_id, result.message || '执行成功', JSON.stringify(result.details || {}), duration]
|
||
);
|
||
|
||
await db.query(
|
||
"UPDATE agent_configs SET last_run_status = 'success' WHERE agent_id = $1",
|
||
[agent.agent_id]
|
||
);
|
||
|
||
console.log(`[Agent] ${agent.agent_id} 完成 (${duration}ms)`);
|
||
} catch (err) {
|
||
const duration = Date.now() - startTime;
|
||
|
||
// 记录错误日志
|
||
await db.query(
|
||
`INSERT INTO agent_logs (agent_id, status, message, details, duration_ms)
|
||
VALUES ($1, 'error', $2, $3, $4)`,
|
||
[agent.agent_id, err.message, JSON.stringify({ stack: err.stack }), duration]
|
||
).catch(() => {});
|
||
|
||
await db.query(
|
||
"UPDATE agent_configs SET last_run_status = 'error' WHERE agent_id = $1",
|
||
[agent.agent_id]
|
||
).catch(() => {});
|
||
|
||
console.error(`[Agent] ${agent.agent_id} 失败 (${duration}ms):`, err.message);
|
||
}
|
||
}
|
||
|
||
// ─── 启动 ───
|
||
startScheduler();
|
||
|
||
// 优雅关闭
|
||
process.on('SIGTERM', () => {
|
||
console.log('[Scheduler] 停止所有Agent...');
|
||
for (const [id, job] of activeJobs) {
|
||
job.stop();
|
||
console.log(`[Scheduler] ${id} 已停止`);
|
||
}
|
||
process.exit(0);
|
||
});
|