zhizhi/server/age-os/agents/scheduler.js

144 lines
4.3 KiB
JavaScript
Raw Normal View History

/**
*
* 🤖 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);
});