小白龙 Bailongma - 初始提交
自主操作员与思考搭档系统。 包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
This commit is contained in:
141
orchestrator-v2/curator.js
Normal file
141
orchestrator-v2/curator.js
Normal file
@@ -0,0 +1,141 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Curator — background skill maintenance orchestrator
|
||||
// Periodically reviews agent-created skills and memories for
|
||||
// consolidation, staleness, and merge opportunities.
|
||||
class Curator {
|
||||
constructor(config) {
|
||||
this.config = config || {};
|
||||
this.skillsDir = config.skillsDir || path.join(process.env.HOME || process.env.USERPROFILE || '.', '.hermes', 'skills');
|
||||
this.reportDir = config.reportDir || path.join(__dirname, 'reports');
|
||||
this.staleDays = config.staleDays || 30;
|
||||
this.archiveDays = config.archiveDays || 90;
|
||||
this.enabled = config.enabled !== false;
|
||||
}
|
||||
|
||||
async run(memoryProvider) {
|
||||
if (!this.enabled) return { status: 'disabled' };
|
||||
|
||||
var report = {
|
||||
timestamp: new Date().toISOString(),
|
||||
staleSkills: [],
|
||||
archivedSkills: [],
|
||||
mergedSuggestions: [],
|
||||
memoryAnalysis: null
|
||||
};
|
||||
|
||||
// 1. Check skill staleness
|
||||
try {
|
||||
var skills = this._scanSkills();
|
||||
report.staleSkills = this._checkStaleness(skills);
|
||||
report.archivedSkills = this._checkArchive(skills);
|
||||
} catch (e) {
|
||||
report.skillError = e.message;
|
||||
}
|
||||
|
||||
// 2. Analyze memories for consolidation opportunities
|
||||
if (memoryProvider) {
|
||||
try {
|
||||
report.memoryAnalysis = this._analyzeMemories(memoryProvider);
|
||||
} catch (e) {
|
||||
report.memoryError = e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Generate report
|
||||
this._saveReport(report);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
_scanSkills() {
|
||||
if (!fs.existsSync(this.skillsDir)) return [];
|
||||
var entries = [];
|
||||
try {
|
||||
var items = fs.readdirSync(this.skillsDir, { withFileTypes: true });
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].isDirectory() || items[i].name.endsWith('.md')) {
|
||||
var fullPath = path.join(this.skillsDir, items[i].name);
|
||||
var stat = fs.statSync(fullPath);
|
||||
entries.push({
|
||||
name: items[i].name,
|
||||
path: fullPath,
|
||||
isDir: items[i].isDirectory(),
|
||||
created: stat.birthtime,
|
||||
modified: stat.mtime,
|
||||
ageDays: (Date.now() - stat.mtime.getTime()) / 86400000
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return entries;
|
||||
}
|
||||
|
||||
_checkStaleness(skills) {
|
||||
return skills.filter(function(s) {
|
||||
return s.ageDays > this.staleDays && s.ageDays < this.archiveDays;
|
||||
}.bind(this)).map(function(s) {
|
||||
return { name: s.name, ageDays: Math.round(s.ageDays), action: 'mark-stale' };
|
||||
});
|
||||
}
|
||||
|
||||
_checkArchive(skills) {
|
||||
return skills.filter(function(s) {
|
||||
return s.ageDays > this.archiveDays;
|
||||
}.bind(this)).map(function(s) {
|
||||
return { name: s.name, ageDays: Math.round(s.ageDays), action: 'archive' };
|
||||
});
|
||||
}
|
||||
|
||||
_analyzeMemories(memoryProvider) {
|
||||
var stats = memoryProvider.getStats();
|
||||
var suggestions = [];
|
||||
|
||||
// Check for memory type balance
|
||||
if (stats.byType) {
|
||||
var total = stats.total || 0;
|
||||
if (total > 50) {
|
||||
suggestions.push('记忆总数超过50条,建议整理合并相似条目');
|
||||
}
|
||||
var taskResults = stats.byType.task_result || 0;
|
||||
if (taskResults > 20) {
|
||||
suggestions.push('task_result 类型记忆过多(' + taskResults + '条),建议按项目归类');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalMemories: stats.total || 0,
|
||||
byType: stats.byType || {},
|
||||
suggestions: suggestions
|
||||
};
|
||||
}
|
||||
|
||||
_saveReport(report) {
|
||||
if (!fs.existsSync(this.reportDir)) fs.mkdirSync(this.reportDir, { recursive: true });
|
||||
var ts = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
var reportPath = path.join(this.reportDir, 'curator-' + ts + '.json');
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
getReportHistory(limit) {
|
||||
if (limit === undefined) limit = 5;
|
||||
if (!fs.existsSync(this.reportDir)) return [];
|
||||
try {
|
||||
var files = fs.readdirSync(this.reportDir)
|
||||
.filter(function(f) { return f.startsWith('curator-') && f.endsWith('.json'); })
|
||||
.sort()
|
||||
.reverse()
|
||||
.slice(0, limit);
|
||||
var reports = [];
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
try {
|
||||
reports.push(JSON.parse(fs.readFileSync(path.join(this.reportDir, files[i]), 'utf8')));
|
||||
} catch (e) {}
|
||||
}
|
||||
return reports;
|
||||
} catch (e) { return []; }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Curator;
|
||||
Reference in New Issue
Block a user