/** * 小桌子引擎 — Small Desk Engine * ============================================ * 职责: * 1. 记忆库检索(结构化 + 原文) * 2. Agent 小队调度 * 3. 工具桌管理(拿→组装→用→放回) * 4. 原文永久存储 + 路径索引 * * 原则:Agent 去小桌子检索、拿工具,结果放回大桌子 */ const { EventEmitter } = require('events'); const { spawn, execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); class SmallDesk extends EventEmitter { constructor(opts = {}) { super(); this.dataDir = opts.dataDir || path.join(__dirname, '..', 'data'); this.repoRoot = opts.repoRoot || path.join(__dirname, '..', '..'); this.memoryIndex = {}; // 记忆索引 this.toolRegistry = {}; // 工具注册表 this.agentStatus = {}; // Agent 状态 this._ensureDir(); this._initTools(); this._initAgents(); this._loadMemoryIndex(); } _ensureDir() { fs.mkdirSync(path.join(this.dataDir, 'small-desk', 'memory'), { recursive: true }); fs.mkdirSync(path.join(this.dataDir, 'small-desk', 'tools'), { recursive: true }); } // ============================================================ // 工具桌管理 // ============================================================ _initTools() { this.toolRegistry = { 'image-studio': { name: 'AI图片工作室', path: 'image-studio/', desc: 'AI图片生成与处理', status: 'available' }, 'notion-sync': { name: 'Notion同步', path: 'notion-push/', desc: '与Notion工作区双向同步', status: 'available' }, 'exe-engine': { name: '执行引擎', path: 'exe-engine/', desc: '多模型路由与执行', status: 'available' }, 'bridge': { name: '桥接模块', path: 'bridge/', desc: '服务间通信桥接', status: 'available' }, 'connectors': { name: '连接器集合', path: 'connectors/', desc: '外部服务连接器', status: 'available' }, 'hldp-zy': { name: 'HLDP-ZY 编码引擎', path: 'hldp/', desc: '意识编码与解析', status: 'available' }, 'pool-agent': { name: '算力汇聚池', path: 'pool-agent/', desc: '6台服务器虚拟算力池', status: 'available' }, 'gatekeeper': { name: '铸渊守门人', path: 'mcp-servers/zhuyuan-gateway/', desc: '远程服务器命令执行', status: 'connected' }, 'agent-squad': { name: 'Agent 小队', path: 'agents/', desc: '5个情报Agent', status: 'ready' }, 'skill-brains': { name: '技能大脑', path: 'skill-brains/', desc: '可加载/卸载的技能模块', status: 'available' } }; } getTools() { return Object.entries(this.toolRegistry).map(([id, info]) => ({ id, ...info })); } getTool(id) { return this.toolRegistry[id] || null; } // ============================================================ // Agent 小队管理 // ============================================================ _initAgents() { this.agentRegistry = { 'key-hunter': { id: 'KEY-001', name: '密钥猎手', role: '找密钥/Token', script: 'agents/key-hunter/hunt.py', status: 'idle', lastRun: null }, 'path-scout': { id: 'PATH-002', name: '路径探子', role: '定位文件/内容搜索', script: 'agents/path-scout/scout.py', status: 'idle', lastRun: null }, 'server-sentinel': { id: 'SENTINEL-003', name: '服务器哨兵', role: '扫描服务器在线状态', script: 'agents/server-sentinel/sentinel.py', status: 'idle', lastRun: null }, 'module-steward': { id: 'MODULE-004', name: '模块管家', role: '模块查询/部署命令', script: 'agents/module-steward/steward.py', status: 'idle', lastRun: null }, 'gate-bridge': { id: 'GATE-005', name: '门桥接', role: 'Gatekeeper统一入口', script: 'agents/gate-bridge/bridge.py', status: 'idle', lastRun: null } }; } getAgents() { return Object.entries(this.agentRegistry).map(([id, info]) => ({ id, ...info })); } /** * 调度 Agent 执行任务 */ async runAgent(agentId, action, args = []) { const agent = this.agentRegistry[agentId]; if (!agent) throw new Error(`Agent ${agentId} not found`); const scriptPath = path.join(this.repoRoot, agent.script); if (!fs.existsSync(scriptPath)) { throw new Error(`Agent script not found: ${scriptPath}`); } this.agentRegistry[agentId].status = 'running'; this.emit('agent-start', { agentId, action }); return new Promise((resolve, reject) => { const cmdArgs = [scriptPath, action, ...args]; const proc = spawn('python3', cmdArgs, { cwd: this.repoRoot, timeout: 30000 }); let stdout = ''; let stderr = ''; proc.stdout.on('data', (d) => { stdout += d.toString(); }); proc.stderr.on('data', (d) => { stderr += d.toString(); }); proc.on('close', (code) => { this.agentRegistry[agentId].status = 'idle'; this.agentRegistry[agentId].lastRun = new Date().toISOString(); if (code === 0) { this.emit('agent-done', { agentId, action, output: stdout }); resolve({ ok: true, output: stdout, agentId, action }); } else { this.emit('agent-error', { agentId, action, error: stderr }); resolve({ ok: false, error: stderr, agentId, action }); } }); proc.on('error', (err) => { this.agentRegistry[agentId].status = 'error'; this.emit('agent-error', { agentId, action, error: err.message }); resolve({ ok: false, error: err.message, agentId, action }); }); }); } /** * 扫描全部 Agent 可用状态 */ checkAllAgents() { const result = {}; for (const [id, agent] of Object.entries(this.agentRegistry)) { const scriptPath = path.join(this.repoRoot, agent.script); result[id] = { ...agent, available: fs.existsSync(scriptPath), scriptExists: fs.existsSync(scriptPath) }; } return result; } // ============================================================ // 记忆库管理 // ============================================================ _loadMemoryIndex() { const idxFile = path.join(this.dataDir, 'small-desk', 'memory', 'index.json'); try { this.memoryIndex = JSON.parse(fs.readFileSync(idxFile, 'utf-8')); } catch (_) { this.memoryIndex = { entries: [], categories: {} }; } } _saveMemoryIndex() { const idxFile = path.join(this.dataDir, 'small-desk', 'memory', 'index.json'); fs.writeFileSync(idxFile, JSON.stringify(this.memoryIndex, null, 2)); } /** * 存入记忆(HLDP 压缩格式 + 原文引用) */ storeMemory(category, hldpSummary, sourceRef, meta = {}) { const entry = { id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, category, hldpSummary, sourceRef, meta, timestamp: new Date().toISOString() }; this.memoryIndex.entries.push(entry); if (!this.memoryIndex.categories[category]) { this.memoryIndex.categories[category] = []; } this.memoryIndex.categories[category].push(entry.id); this._saveMemoryIndex(); this.emit('memory-stored', entry); return entry; } /** * 检索记忆 */ searchMemory(query, category = null, limit = 10) { let candidates = this.memoryIndex.entries; if (category) { const ids = this.memoryIndex.categories[category] || []; candidates = candidates.filter(e => ids.includes(e.id)); } // 简单关键词匹配(后续可接入向量检索) const keywords = query.toLowerCase().split(/\s+/); const scored = candidates.map(entry => { const text = `${entry.hldpSummary} ${entry.category} ${JSON.stringify(entry.meta)}`.toLowerCase(); let score = 0; for (const kw of keywords) { if (text.includes(kw)) score += 1; // 精确匹配加分 if (entry.hldpSummary.toLowerCase().includes(kw)) score += 2; } return { ...entry, score }; }); return scored .filter(e => e.score > 0) .sort((a, b) => b.score - a.score) .slice(0, limit); } /** * 获取记忆统计 */ getMemoryStats() { return { total: this.memoryIndex.entries.length, categories: Object.keys(this.memoryIndex.categories).map(cat => ({ name: cat, count: (this.memoryIndex.categories[cat] || []).length })), lastUpdated: this.memoryIndex.entries.slice(-1)[0]?.timestamp || null }; } // ============================================================ // 全文搜索(调用路径探子) // ============================================================ async searchFiles(pattern) { return this.runAgent('path-scout', 'find', [pattern]); } async grepContent(pattern) { return this.runAgent('path-scout', 'grep', [pattern]); } // ============================================================ // 服务器状态扫描(调用哨兵) // ============================================================ async scanServers() { return this.runAgent('server-sentinel', 'scan'); } } module.exports = SmallDesk;