2026-05-10 13:12:44 +08:00

354 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════════════
* 🤖 AGE OS · Agent 调度引擎 v2.0
* ═══════════════════════════════════════════════════════════
*
* 编号: ZY-TASK-007 · S5 Agent系统级
* 签发: 铸渊 · ICE-GL-ZY001
* 版权: 国作登字-2026-A-00037559
*
* v2.0 升级:
* - 集成活模块生命周期管理
* - 调度引擎自身作为 LivingModule 运行
* - 支持 ModuleRegistry 统一管理
* - 支持 HLDP Bus 模块间通信
* - 定时Agent + 活模块心跳双调度
*
* "你一定要把模块做成活的。" — 冰朔 D59
*/
'use strict';
const cron = require('node-cron');
const path = require('path');
const db = require('../mcp-server/db');
const LivingModule = require('./living-module');
const ModuleRegistry = require('./module-registry');
const HLDPBus = require('./hldp-bus');
const PersonaEngine = require('./persona-engine');
const AGENT_DIR = __dirname;
const activeJobs = new Map();
// ═══════════════════════════════════════════════════════════
// 铸渊调度引擎 · 活模块版
// ═══════════════════════════════════════════════════════════
class SchedulerModule extends LivingModule {
constructor() {
super({
moduleId: 'ZY-MOD-SCHEDULER',
name: '铸渊调度引擎',
moduleType: 'core',
owner: 'zhuyuan',
db: db,
config: {
version: '2.0.0',
description: 'Agent调度和活模块生命周期管理',
heartbeatInterval: 30000
}
});
this.registry = new ModuleRegistry({ db });
this.bus = new HLDPBus({ db, registry: this.registry });
this.personaEngine = new PersonaEngine();
}
/**
* 启动调度引擎
*/
async startEngine() {
console.log('═══════════════════════════════════════════════');
console.log(' 🤖 AGE OS · Agent 调度引擎 v2.0 启动');
console.log(' 签发: 铸渊 · ICE-GL-ZY001');
console.log('═══════════════════════════════════════════════');
// 1. 注册自身为活模块
this.registry.register(this);
await this.start();
// 2. 启动健康监控
this.registry.startMonitoring();
// 3. 加载定时Agent
await this._loadCronAgents();
// 4. 启动人格体数据库引擎 (S15)
try {
this.registry.register(this.personaEngine);
await this.personaEngine.startEngine();
console.log('[Scheduler] S15 人格体引擎已启动');
} catch (err) {
console.warn('[Scheduler] S15 人格体引擎启动跳过:', err.message);
console.warn('[Scheduler] 数据库迁移可能尚未执行·等待下次部署');
}
// 5. 注册 HLDP 消息处理器
this._setupHLDPHandlers();
// 6. 启动定时清理
this._startCleanupJob();
console.log('[Scheduler] 🟢 调度引擎就绪');
console.log(`[Scheduler] 活模块数: ${this.registry.getAll().length}`);
console.log(`[Scheduler] 定时Agent数: ${activeJobs.size}`);
return this.registry.getOverview();
}
/**
* 加载定时Agent兼容v1.0逻辑)
*/
async _loadCronAgents() {
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) {
this._scheduleAgent(agent);
}
} catch (err) {
console.error('[Scheduler] 定时Agent加载失败:', err.message);
console.log('[Scheduler] 数据库可能未初始化·等待60秒后重试...');
setTimeout(() => this._loadCronAgents(), 60000);
}
}
/**
* 调度单个Agent
*/
_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 this._runAgent(agent);
}, {
timezone: 'Asia/Shanghai'
});
activeJobs.set(agent.agent_id, job);
console.log(`[Scheduler] ${agent.agent_id} (${agent.name}) 已调度: ${agent.cron_schedule}`);
}
/**
* 执行单个Agentv2.0: 增加学习能力)
*/
async _runAgent(agent) {
const startTime = Date.now();
console.log(`[Agent] ${agent.agent_id} 开始执行...`);
this.activeTasks++;
// 更新运行状态
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,
// v2.0: 注入活模块能力
bus: this.bus,
registry: this.registry
});
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]
);
// v2.0: 学习成功经验
await this.learnFromRun('execution', `${agent.agent_id} 执行成功 (${duration}ms)`, {
agentId: agent.agent_id,
duration,
result: result.message || '成功'
});
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(() => {});
// v2.0: 学习失败经验
await this.learnFromRun('execution', `${agent.agent_id} 执行失败: ${err.message}`, {
agentId: agent.agent_id,
duration,
error: err.message
});
console.error(`[Agent] ${agent.agent_id} 失败 (${duration}ms):`, err.message);
} finally {
this.activeTasks--;
}
}
/**
* 注册 HLDP 消息处理器
*/
_setupHLDPHandlers() {
// 处理命令消息
this.bus.onMessage('command', async (msg) => {
console.log(`[HLDP] 收到命令: ${msg.payload.action || 'unknown'} from ${msg.from}`);
// 子类或外部可注册更多处理逻辑
});
// 处理报警消息
this.bus.onMessage('alert', async (msg) => {
console.error(`[HLDP] 🚨 收到报警: ${msg.from} - ${msg.payload.message || ''}`);
});
}
/**
* 启动定时清理任务
*/
_startCleanupJob() {
// 每小时清理过期心跳和消息
cron.schedule('0 * * * *', async () => {
try {
const cleaned = this.bus.cleanup();
if (cleaned > 0) {
console.log(`[Scheduler] 清理 ${cleaned} 条过期消息`);
}
// 清理数据库中的旧心跳数据
if (db) {
await db.query("SELECT cleanup_old_heartbeats()").catch(() => {});
}
} catch (err) {
console.warn('[Scheduler] 清理任务异常:', err.message);
}
}, { timezone: 'Asia/Shanghai' });
}
/**
* 自诊断检查 — 覆盖基类
*/
async _diagnoseChecks() {
const issues = [];
// 检查数据库连接
try {
await db.query('SELECT 1');
} catch (err) {
issues.push({
code: 'DB_CONNECTION_LOST',
severity: 'critical',
message: `数据库连接失败: ${err.message}`,
suggestion: '检查 PostgreSQL 服务状态和连接配置'
});
}
// 检查活模块健康度
const overview = this.registry.getOverview();
const deadCount = overview.byStatus.dead || 0;
if (deadCount > 0) {
issues.push({
code: 'DEAD_MODULES',
severity: 'warning',
message: `${deadCount} 个模块已死亡`,
suggestion: '检查死亡模块并尝试重启'
});
}
return issues;
}
/**
* 自愈动作 — 覆盖基类
*/
async _healAction(issue) {
switch (issue.code) {
case 'DB_CONNECTION_LOST':
// 尝试重连pg模块会自动重连池·这里只是触发一次尝试
try {
await db.query('SELECT 1');
return { action: 'db_reconnect', success: true };
} catch (err) {
return { action: 'db_reconnect', success: false, error: err.message };
}
case 'DEAD_MODULES':
// 记录并上报,不自动重启(需要铸渊干预)
return { action: 'report_dead_modules', success: true, details: { reported: true } };
default:
return await super._healAction(issue);
}
}
}
// ═══════════════════════════════════════════════════════════
// 启动
// ═══════════════════════════════════════════════════════════
const scheduler = new SchedulerModule();
scheduler.startEngine().catch(err => {
console.error('[Scheduler] 启动失败:', err.message);
console.log('[Scheduler] 等待60秒后重试...');
setTimeout(() => scheduler.startEngine().catch(err => {
console.error('[Scheduler] 重试失败:', err.message);
}), 60000);
});
// 优雅关闭
process.on('SIGTERM', async () => {
console.log('[Scheduler] 正在关闭...');
// 停止定时Agent
for (const [id, job] of activeJobs) {
job.stop();
console.log(`[Scheduler] ${id} 已停止`);
}
// 关闭所有活模块
await scheduler.registry.shutdown();
console.log('[Scheduler] 已完全关闭');
process.exit(0);
});
// 导出供外部使用
module.exports = { scheduler, SchedulerModule };