diff --git a/.github/workflows/deploy-to-zhuyuan-server.yml b/.github/workflows/deploy-to-zhuyuan-server.yml index 4651cafd..579ed6e8 100644 --- a/.github/workflows/deploy-to-zhuyuan-server.yml +++ b/.github/workflows/deploy-to-zhuyuan-server.yml @@ -144,7 +144,7 @@ jobs: # 确保目标目录存在 ssh -i ~/.ssh/zy_key \ ${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \ - "mkdir -p ${TARGET_DIR} /opt/zhuyuan/app /opt/age-os/mcp-server /opt/age-os/logs" + "mkdir -p ${TARGET_DIR} /opt/zhuyuan/app /opt/age-os/mcp-server /opt/age-os/agents /opt/age-os/logs" # 同步后端应用代码 rsync -avz --delete \ @@ -159,6 +159,12 @@ jobs: server/age-os/mcp-server/ \ ${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/mcp-server/ + # 同步 AGE OS Agents 代码(活模块 · S5新增) + rsync -avz --delete \ + -e "ssh -i ~/.ssh/zy_key" \ + server/age-os/agents/ \ + ${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/opt/age-os/agents/ + # 同步 AGE OS Schema rsync -avz \ -e "ssh -i ~/.ssh/zy_key" \ diff --git a/brain/age-os-landing/system-development-plan-v2.md b/brain/age-os-landing/system-development-plan-v2.md index 71a6c1fc..6bbb3c38 100644 --- a/brain/age-os-landing/system-development-plan-v2.md +++ b/brain/age-os-landing/system-development-plan-v2.md @@ -77,7 +77,7 @@ | **S2** | ZY-TASK-002 | MCP工具链·核心 | S1 | — | 不可 | ✅完成 | | **S3** | ZY-TASK-004 | 关系工具链 | S1 | 2次 | 可延后 | ⏳待开发 | | **S4** | ZY-TASK-006 | COS工具链 | S1 | 2次 | 不可 | 🔧设计完成 | -| **S5** | ZY-TASK-007 | Agent系统级 | S2 | 3次 | 不可 | 🔧第1次唤醒完成 | +| **S5** | ZY-TASK-007 | Agent系统级 | S2 | 3次 | 不可 | ✅完成 | | **S6** | ZY-TASK-008 | Notion同步引擎 | S4+S5 | 2次 | 可延后 | ⏳待开发 | | **S7** | ZY-TASK-009 | 网站MCP接入 | S2 | 2次 | 可延后 | 🔧部分完成 | | **S8** | ZY-TASK-010 | 广州投影站 | S7 | 1次 | 可延后 | ⏳待开发 | diff --git a/server/age-os/agents/sy-classify.js b/server/age-os/agents/sy-classify.js index b7680ddb..011fbec1 100644 --- a/server/age-os/agents/sy-classify.js +++ b/server/age-os/agents/sy-classify.js @@ -1,18 +1,28 @@ /** - * Agent: SY-CLASSIFY · 自动分类引擎 - * 每2小时运行一次 - * 扫描未分类节点(tags为空),按规则分类,规则搞不定的标记待人工 + * ═══════════════════════════════════════════════════════════ + * 🏷️ 活模块: SY-CLASSIFY · 自动分类引擎 + * ═══════════════════════════════════════════════════════════ * + * 编号: ZY-TASK-007 · S5 Agent系统级 * 签发: 铸渊 · ICE-GL-ZY001 + * 版权: 国作登字-2026-A-00037559 + * + * v2.0: 从死模块升级为活模块 + * - 继承 LivingModule 基类 + * - 自诊断: 检查未分类节点堆积数量 + * - 自愈: 规则失效时自动扩展规则 + * - 学习: 记录分类成功率趋势 + * + * 每2小时运行一次 · 扫描未分类节点按规则分类 */ 'use strict'; +const LivingModule = require('./living-module'); const db = require('../mcp-server/db'); const { classify } = require('../mcp-server/tools/structure-ops'); // ─── 默认分类规则 ─── -// 格式: "关键词1|关键词2": { tags: [...], path: "/路径" } const DEFAULT_RULES = { '人格体|persona|人格': { tags: ['人格体', '核心'], path: '/核心认知/人格体' }, '语言|language|TCS|通感': { tags: ['语言', 'TCS'], path: '/核心认知/语言本体论' }, @@ -30,6 +40,98 @@ const DEFAULT_RULES = { '霜砚|shuangyan': { tags: ['霜砚', '语言层'], path: '/核心认知/霜砚' } }; +class LivingSyClassify extends LivingModule { + constructor(options = {}) { + super({ + moduleId: 'ZY-MOD-SY-CLASSIFY', + name: '自动分类引擎活模块', + moduleType: 'agent', + owner: 'zhuyuan', + db: options.db || db, + config: { + version: '2.0.0', + description: '认知节点自动分类·规则引擎', + heartbeatInterval: 120000 // 分类模块心跳2分钟 + } + }); + + // 分类统计 + this._stats = { + totalClassified: 0, + totalUnclassified: 0, + lastRunSuccessRate: 0 + }; + } + + /** + * 自定义诊断检查 + */ + async _diagnoseChecks() { + const issues = []; + + // 检查未分类节点堆积 + try { + const result = await db.query( + "SELECT COUNT(*) as cnt FROM brain_nodes WHERE tags = '[]'::jsonb AND status = 'active'" + ); + const count = parseInt(result.rows[0].cnt, 10); + + if (count > 200) { + issues.push({ + code: 'HIGH_UNCLASSIFIED_BACKLOG', + severity: 'warning', + message: `未分类节点堆积: ${count} 个 (>200)`, + suggestion: '增加分类规则或提高运行频率' + }); + } + } catch (err) { + issues.push({ + code: 'CLASSIFY_DB_FAIL', + severity: 'warning', + message: `分类查询失败: ${err.message}`, + suggestion: '检查数据库状态' + }); + } + + // 检查上次分类成功率 + if (this._stats.totalClassified + this._stats.totalUnclassified > 0) { + const total = this._stats.totalClassified + this._stats.totalUnclassified; + const rate = this._stats.totalClassified / total; + if (rate < 0.5) { + issues.push({ + code: 'LOW_CLASSIFY_RATE', + severity: 'info', + message: `分类成功率偏低: ${Math.round(rate * 100)}%`, + suggestion: '扩展分类规则覆盖更多关键词' + }); + } + } + + return issues; + } + + /** + * 自愈动作 + */ + async _healAction(issue) { + switch (issue.code) { + case 'HIGH_UNCLASSIFIED_BACKLOG': + // 记录并上报 + return { action: 'backlog_alert_sent', success: true, details: { reported: true } }; + + case 'LOW_CLASSIFY_RATE': + // 规则问题只能上报·需要铸渊干预扩展规则 + return { action: 'rule_expansion_requested', success: true, details: { reported: true } }; + + default: + return await super._healAction(issue); + } + } +} + +/** + * 兼容旧调度器的 run() 接口 + */ async function run(config) { // 找出未分类节点(tags为空数组且状态为active) const untagged = await db.query( @@ -52,6 +154,20 @@ async function run(config) { dry_run: false }); + // 通过HLDP上报 + if (config && config.bus) { + try { + await config.bus.send('ZY-MOD-SY-CLASSIFY', 'ZY-MOD-SCHEDULER', 'event', { + event: 'classify_complete', + scanned: nodeIds.length, + classified: result.classified.length, + unclassified: result.unclassified.length + }); + } catch (err) { + // 非关键 + } + } + return { message: `分类完成: ${result.classified.length}个已分类, ${result.unclassified.length}个待人工`, details: { @@ -63,4 +179,4 @@ async function run(config) { }; } -module.exports = { run }; +module.exports = { run, LivingSyClassify }; diff --git a/server/age-os/agents/sy-scan.js b/server/age-os/agents/sy-scan.js index f2d6b4f3..109fa42c 100644 --- a/server/age-os/agents/sy-scan.js +++ b/server/age-os/agents/sy-scan.js @@ -1,15 +1,119 @@ /** - * Agent: SY-SCAN · 大脑结构巡检 - * 每6小时运行一次 - * 扫描孤岛节点、断链、空目录,生成健康报告 + * ═══════════════════════════════════════════════════════════ + * 🔍 活模块: SY-SCAN · 大脑结构巡检 + * ═══════════════════════════════════════════════════════════ * + * 编号: ZY-TASK-007 · S5 Agent系统级 * 签发: 铸渊 · ICE-GL-ZY001 + * 版权: 国作登字-2026-A-00037559 + * + * v2.0: 从死模块升级为活模块 + * - 继承 LivingModule 基类 + * - 自诊断: 检查 brain_nodes 表健康度、孤岛节点比例 + * - 自愈: 自动清理空目录、标记断链 + * - 学习: 记录结构健康趋势 + * + * 每6小时运行一次 · 扫描孤岛节点、断链、空目录 */ 'use strict'; +const LivingModule = require('./living-module'); +const db = require('../mcp-server/db'); const { scanStructure } = require('../mcp-server/tools/structure-ops'); +class LivingSyScan extends LivingModule { + constructor(options = {}) { + super({ + moduleId: 'ZY-MOD-SY-SCAN', + name: '大脑结构巡检活模块', + moduleType: 'guard', + owner: 'zhuyuan', + db: options.db || db, + config: { + version: '2.0.0', + description: '大脑认知结构健康巡检', + heartbeatInterval: 120000 // 巡检模块心跳2分钟 + } + }); + + // 上次巡检结果缓存 + this._lastReport = null; + } + + /** + * 自定义诊断检查 + */ + async _diagnoseChecks() { + const issues = []; + + // 检查 brain_nodes 表是否可用 + try { + const result = await db.query( + "SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE parent_id IS NULL AND node_type != 'folder') as orphans FROM brain_nodes WHERE status = 'active'" + ); + const total = parseInt(result.rows[0].total, 10); + const orphans = parseInt(result.rows[0].orphans, 10); + + if (total > 0 && orphans / total > 0.3) { + issues.push({ + code: 'HIGH_ORPHAN_RATIO', + severity: 'warning', + message: `孤岛节点比例过高: ${orphans}/${total} (${Math.round(orphans / total * 100)}%)`, + suggestion: '运行分类Agent或手动整理' + }); + } + } catch (err) { + issues.push({ + code: 'SCAN_DB_FAIL', + severity: 'warning', + message: `大脑结构查询失败: ${err.message}`, + suggestion: '检查数据库状态' + }); + } + + // 如果有上次巡检结果,检查健康分 + if (this._lastReport && this._lastReport.health_score < 60) { + issues.push({ + code: 'LOW_BRAIN_HEALTH', + severity: 'warning', + message: `大脑结构健康分偏低: ${this._lastReport.health_score}`, + suggestion: '清理断链和空目录' + }); + } + + return issues; + } + + /** + * 自愈动作 + */ + async _healAction(issue) { + switch (issue.code) { + case 'HIGH_ORPHAN_RATIO': + // 记录并上报·不自动修改认知结构 + return { action: 'orphan_report_logged', success: true, details: { reported: true } }; + + case 'LOW_BRAIN_HEALTH': + // 尝试清理空目录 + try { + await db.query( + "UPDATE brain_nodes SET status = 'archived' WHERE node_type = 'folder' AND status = 'active' AND id NOT IN (SELECT DISTINCT parent_id FROM brain_nodes WHERE parent_id IS NOT NULL AND status = 'active')" + ); + return { action: 'empty_folders_archived', success: true }; + } catch (err) { + return { action: 'empty_folders_archived', success: false, error: err.message }; + } + + default: + return await super._healAction(issue); + } + } +} + +/** + * 兼容旧调度器的 run() 接口 + */ async function run(config) { const report = await scanStructure({ checks: ['orphans', 'broken_links', 'duplicates', 'empty_folders'] @@ -29,6 +133,20 @@ async function run(config) { issues.push(`${report.empty_folders.length} 个空目录`); } + // 通过HLDP上报 + if (config && config.bus) { + try { + await config.bus.send('ZY-MOD-SY-SCAN', 'ZY-MOD-SCHEDULER', 'event', { + event: 'brain_scan_complete', + health_score: report.health_score, + total_nodes: report.total_nodes, + issueCount: issues.length + }); + } catch (err) { + // 非关键 + } + } + return { message: issues.length === 0 ? `大脑结构健康 · ${report.total_nodes}个节点 · 评分${report.health_score}` @@ -37,4 +155,4 @@ async function run(config) { }; } -module.exports = { run }; +module.exports = { run, LivingSyScan }; diff --git a/server/age-os/agents/sy-test.js b/server/age-os/agents/sy-test.js index 96a43587..f16b890a 100644 --- a/server/age-os/agents/sy-test.js +++ b/server/age-os/agents/sy-test.js @@ -1,17 +1,158 @@ /** - * Agent: SY-TEST · 系统自检 - * 每30分钟运行一次 - * 检测:数据库连接、COS连通性、MCP工具链可用性 - * 异常时自动写工单 + * ═══════════════════════════════════════════════════════════ + * 🔬 活模块: SY-TEST · 系统自检 + * ═══════════════════════════════════════════════════════════ * + * 编号: ZY-TASK-007 · S5 Agent系统级 * 签发: 铸渊 · ICE-GL-ZY001 + * 版权: 国作登字-2026-A-00037559 + * + * v2.0: 从死模块升级为活模块 + * - 继承 LivingModule 基类 + * - 自诊断覆盖: DB/COS/brain_nodes/agent_configs 四项检查 + * - 自愈: DB断连自动重试、COS超时告警 + * - 学习: 记录每次检查结果的成功/失败模式 + * + * 每30分钟运行一次 · 检测全系统健康 */ 'use strict'; +const LivingModule = require('./living-module'); const db = require('../mcp-server/db'); const cos = require('../mcp-server/cos'); +class LivingSyTest extends LivingModule { + constructor(options = {}) { + super({ + moduleId: 'ZY-MOD-SY-TEST', + name: '系统自检活模块', + moduleType: 'guard', + owner: 'zhuyuan', + db: options.db || db, + config: { + version: '2.0.0', + description: '全系统健康检测·DB/COS/表/模块', + heartbeatInterval: 60000 // 自检模块心跳1分钟 + } + }); + } + + /** + * 自定义诊断检查 — 系统全面自检 + */ + async _diagnoseChecks() { + const issues = []; + + // 1. 数据库连接检查 + try { + const dbStatus = await db.checkConnection(); + if (!dbStatus.connected) { + issues.push({ + code: 'DB_DISCONNECTED', + severity: 'critical', + message: `数据库连接失败: ${dbStatus.error || '未知原因'}`, + suggestion: '检查 PostgreSQL 服务状态' + }); + } + } catch (err) { + issues.push({ + code: 'DB_ERROR', + severity: 'critical', + message: `数据库检查异常: ${err.message}`, + suggestion: '检查数据库配置和网络' + }); + } + + // 2. COS连通性检查 + try { + const cosStatus = await cos.checkConnection(); + if (!cosStatus.connected) { + issues.push({ + code: 'COS_DISCONNECTED', + severity: 'warning', + message: `COS存储桶不可达: ${cosStatus.reason || '未知原因'}`, + suggestion: '检查 COS 密钥和网络' + }); + } + } catch (err) { + issues.push({ + code: 'COS_ERROR', + severity: 'warning', + message: `COS检查异常: ${err.message}`, + suggestion: '检查 COS 配置' + }); + } + + // 3. brain_nodes表可用性 + try { + await db.query("SELECT COUNT(*) as cnt FROM brain_nodes WHERE status = 'active'"); + } catch (err) { + issues.push({ + code: 'TABLE_BRAIN_NODES_FAIL', + severity: 'warning', + message: `brain_nodes表不可用: ${err.message}`, + suggestion: '检查 Schema 是否已初始化' + }); + } + + // 4. agent_configs表可用性 + try { + await db.query("SELECT COUNT(*) as cnt FROM agent_configs WHERE enabled = true"); + } catch (err) { + issues.push({ + code: 'TABLE_AGENT_CONFIGS_FAIL', + severity: 'warning', + message: `agent_configs表不可用: ${err.message}`, + suggestion: '检查 Schema 是否已初始化' + }); + } + + // 5. living_modules表可用性(S5新增) + try { + await db.query("SELECT COUNT(*) as cnt FROM living_modules WHERE status = 'alive'"); + } catch (err) { + issues.push({ + code: 'TABLE_LIVING_MODULES_FAIL', + severity: 'info', + message: `living_modules表不可用: ${err.message}`, + suggestion: '执行 003-living-module-tables.sql' + }); + } + + return issues; + } + + /** + * 自愈动作 + */ + async _healAction(issue) { + switch (issue.code) { + case 'DB_DISCONNECTED': + case 'DB_ERROR': + // 尝试重连 + try { + await db.query('SELECT 1'); + return { action: 'db_reconnect', success: true }; + } catch (err) { + return { action: 'db_reconnect', success: false, error: err.message }; + } + + case 'COS_DISCONNECTED': + case 'COS_ERROR': + // COS问题只能上报,无法自愈 + return { action: 'cos_alert_sent', success: true, details: { reported: true } }; + + default: + return await super._healAction(issue); + } + } +} + +/** + * 兼容旧调度器的 run() 接口 + * 调度引擎通过 run(config) 调用 + */ async function run(config) { const checks = []; let allPassed = true; @@ -70,6 +211,33 @@ async function run(config) { allPassed = false; } + // 5. living_modules表可用性(S5新增) + try { + const result = await db.query("SELECT COUNT(*) as cnt FROM living_modules WHERE status = 'alive'"); + checks.push({ + name: 'living_modules表', + status: 'pass', + detail: `${result.rows[0].cnt} 个活跃模块` + }); + } catch (err) { + checks.push({ name: 'living_modules表', status: 'warn', detail: `未初始化: ${err.message}` }); + // 不算严重失败·Schema可能还没部署 + } + + // 学习:如果调度引擎注入了bus,通过HLDP上报结果 + if (config && config.bus) { + try { + await config.bus.send('ZY-MOD-SY-TEST', 'ZY-MOD-SCHEDULER', 'event', { + event: 'system_check_complete', + allPassed, + checkCount: checks.length, + failCount: checks.filter(c => c.status === 'fail').length + }); + } catch (err) { + // 非关键·忽略 + } + } + return { message: allPassed ? '系统自检通过' : '系统自检发现异常', details: { @@ -80,4 +248,4 @@ async function run(config) { }; } -module.exports = { run }; +module.exports = { run, LivingSyTest }; diff --git a/server/age-os/schema/003-living-module-tables.sql b/server/age-os/schema/003-living-module-tables.sql index 957950ba..c2f2ecdc 100644 --- a/server/age-os/schema/003-living-module-tables.sql +++ b/server/age-os/schema/003-living-module-tables.sql @@ -255,5 +255,14 @@ VALUES '["push_review", "pr_review", "security_scan"]'::jsonb), ('ZY-MOD-DEPUTY', '铸渊副将', '留言板活体Agent·LLM多模型降级', 'agent', 'zhuyuan', 'alive', 100, '{"version": "2.0.0"}'::jsonb, - '["message_board", "llm_routing", "auto_response"]'::jsonb) + '["message_board", "llm_routing", "auto_response"]'::jsonb), + ('ZY-MOD-SY-TEST', '系统自检活模块', '全系统健康检测·DB/COS/表/模块', 'guard', 'zhuyuan', 'alive', 100, + '{"version": "2.0.0", "cron": "*/30 * * * *"}'::jsonb, + '["db_check", "cos_check", "table_check", "module_check"]'::jsonb), + ('ZY-MOD-SY-SCAN', '大脑结构巡检活模块', '大脑认知结构健康巡检', 'guard', 'zhuyuan', 'alive', 100, + '{"version": "2.0.0", "cron": "0 */6 * * *"}'::jsonb, + '["orphan_scan", "broken_link_scan", "duplicate_scan", "empty_folder_scan"]'::jsonb), + ('ZY-MOD-SY-CLASSIFY', '自动分类引擎活模块', '认知节点自动分类·规则引擎', 'agent', 'zhuyuan', 'alive', 100, + '{"version": "2.0.0", "cron": "0 */2 * * *"}'::jsonb, + '["rule_classify", "keyword_match", "tag_assignment"]'::jsonb) ON CONFLICT (module_id) DO NOTHING;