Files
BaiLongma/orchestrator-v2/memory-provider.js
chengjiaxi 664ffb3834 小白龙 Bailongma - 初始提交
自主操作员与思考搭档系统。
包含 orchestrator-v2 多Agent编排层、后台意识引擎、记忆系统、ACUI 组件。
2026-05-22 19:22:06 +08:00

151 lines
5.6 KiB
JavaScript
Raw 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.
const fs = require('fs');
const path = require('path');
class MemoryProvider {
constructor(config) { this.config = config || {}; this.name = 'base'; }
async initialize() { throw new Error('Not implemented'); }
async prefetch(taskContext) { throw new Error('Not implemented'); }
async syncTurn(taskContext, result) { throw new Error('Not implemented'); }
async search(query, limit) { throw new Error('Not implemented'); }
wrapContext(content) {
if (!content || !content.length) return '';
return '\n<memory-context>\n' + content.join('\n---\n') + '\n</memory-context>\n';
}
async shutdown() {}
}
class BuiltInMemoryProvider extends MemoryProvider {
constructor(config) {
super(config);
this.name = 'built-in';
var home = process.env.HOME || process.env.USERPROFILE || '.';
this.memoryDir = config.memoryDir || path.join(home, '.hermes', 'memory');
this.memoryFile = config.memoryFile || path.join(this.memoryDir, 'memories.json');
this.memories = [];
this.maxPrefetch = config.maxPrefetch || 5;
}
async initialize() {
if (!fs.existsSync(this.memoryDir)) fs.mkdirSync(this.memoryDir, { recursive: true });
if (fs.existsSync(this.memoryFile)) {
try {
var raw = fs.readFileSync(this.memoryFile, 'utf8');
this.memories = JSON.parse(raw);
if (!Array.isArray(this.memories)) this.memories = [];
} catch (e) { this.memories = []; }
}
console.log('[Memory] Loaded ' + this.memories.length + ' memories from ' + this.memoryFile);
}
addEntry(type, title, content, tags) {
var entry = {
id: 'mem_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8),
type: type || 'general',
title: title || '',
content: content || '',
tags: tags || [],
created_at: new Date().toISOString(),
accessed_at: new Date().toISOString(),
access_count: 1
};
this.memories.push(entry);
this._persist();
return entry;
}
async prefetch(taskContext) {
var query = '';
if (typeof taskContext === 'string') {
query = taskContext;
} else if (taskContext && taskContext.task) {
query = typeof taskContext.task === 'string' ? taskContext.task : (taskContext.task.name || taskContext.task.target || '');
} else if (taskContext && taskContext.target) {
query = taskContext.target;
}
if (!query || !this.memories.length) return [];
var q = query.toLowerCase();
var self = this;
var scored = this.memories.map(function(m) {
var score = 0;
var searchSpace = (m.title + ' ' + m.content + ' ' + (m.tags || []).join(' ') + ' ' + m.type).toLowerCase();
if (searchSpace.includes(q)) score += (searchSpace.split(q).length - 1) * 3;
var words = q.split(/[\s,,、。.;;:!?()\[\]【】]+/).filter(function(w) { return w.length > 1; });
for (var j = 0; j < words.length; j++) {
if (searchSpace.includes(words[j])) score += words[j].length;
}
score += Math.log((m.access_count || 1) + 1);
return { entry: m, score: score };
}).filter(function(s) { return s.score > 0; })
.sort(function(a, b) { return b.score - a.score; })
.slice(0, self.maxPrefetch);
for (var i = 0; i < scored.length; i++) {
scored[i].entry.access_count = (scored[i].entry.access_count || 1) + 1;
scored[i].entry.accessed_at = new Date().toISOString();
}
if (scored.length) this._persist();
return scored.map(function(s) { return s.entry; });
}
async syncTurn(taskContext, result) {
if (!result) return;
var summary = '';
if (typeof result === 'string') summary = result;
else if (result.summary) summary = result.summary;
else if (result.output) summary = typeof result.output === 'string' ? result.output.slice(0, 500) : JSON.stringify(result.output).slice(0, 500);
else summary = JSON.stringify(result).slice(0, 500);
if (!summary || summary.length < 20) return;
if (summary.includes('[LLM Error]') || summary.includes('no result')) return;
var taskName = '';
if (typeof taskContext === 'string') taskName = taskContext;
else if (taskContext && taskContext.task) taskName = typeof taskContext.task === 'string' ? taskContext.task : (taskContext.task.name || '');
this.addEntry('task_result', taskName.slice(0, 100), summary.slice(0, 1000), [taskName.slice(0, 30)]);
}
async search(query, limit) {
if (limit === undefined) limit = 10;
if (!query || !this.memories.length) return [];
var q = query.toLowerCase();
var results = [];
for (var i = 0; i < this.memories.length; i++) {
var m = this.memories[i];
if ((m.title + ' ' + m.content + ' ' + (m.tags || []).join(' ')).toLowerCase().includes(q)) {
results.push(m);
if (results.length >= limit) break;
}
}
return results;
}
async getContextBlock(taskContext) {
var memories = await this.prefetch(taskContext);
if (!memories || !memories.length) return '';
var lines = memories.map(function(m) {
return '[' + m.type + '] ' + (m.title ? m.title + ': ' : '') + m.content;
});
return this.wrapContext(lines);
}
getStats() {
var byType = {};
for (var i = 0; i < this.memories.length; i++) {
var t = this.memories[i].type || 'unknown';
byType[t] = (byType[t] || 0) + 1;
}
return { total: this.memories.length, byType: byType, file: this.memoryFile };
}
_persist() {
try { fs.writeFileSync(this.memoryFile, JSON.stringify(this.memories, null, 2), 'utf8'); }
catch (e) { console.error('[Memory] Persist error:', e.message); }
}
async shutdown() { this._persist(); }
}
module.exports = { MemoryProvider, BuiltInMemoryProvider };