/** * EvolutionMemory — Skill 进化历史记忆模块 * * 记录每个 Skill 的进化迭代历史,包括策略、分数、教训。 * 为 RL Loop 的 Reason & Plan 阶段提供经验参考。 * * 存储位置: {skillDir}/evolution_memory.json */ const fs = require('fs'); const path = require('path'); const MEMORY_FILE = 'evolution_memory.json'; const MAX_RECORDS = 50; // 保留最近 50 条迭代记录 class EvolutionMemory { /** * @param {string} skillDir - Skill 目录绝对路径 */ constructor(skillDir) { this.skillDir = skillDir; this.filePath = path.join(skillDir, MEMORY_FILE); this._data = null; } /** * 读取完整历史 * @returns {{ iterations: Array, bestScore: number|null, totalAttempts: number }} */ getHistory() { const data = this._load(); return { iterations: data.iterations || [], bestScore: data.bestScore || null, totalAttempts: data.totalAttempts || 0, }; } /** * 从历史失败中提取教训 * @returns {string[]} 教训列表 */ getLessons() { const data = this._load(); const lessons = []; for (const iter of (data.iterations || [])) { if (iter.lessonsLearned && Array.isArray(iter.lessonsLearned)) { lessons.push(...iter.lessonsLearned); } } // 去重 return [...new Set(lessons)]; } /** * 获取历史最高分时的策略 * @returns {object|null} */ getBestStrategy() { const data = this._load(); if (!data.iterations || data.iterations.length === 0) return null; let best = null; for (const iter of data.iterations) { if (!best || (iter.score && iter.score > (best.score || 0))) { best = iter; } } return best ? best.strategy : null; } /** * 获取最近 N 轮的弱项维度 (用于聚焦) * @param {number} n * @returns {string[]} */ getRecentWeakDimensions(n = 3) { const data = this._load(); const iterations = data.iterations || []; const recent = iterations.slice(-n); const dims = []; for (const iter of recent) { if (iter.weakDimensions && Array.isArray(iter.weakDimensions)) { dims.push(...iter.weakDimensions); } } // 按频次排序 const freq = {}; for (const d of dims) { freq[d] = (freq[d] || 0) + 1; } return Object.entries(freq) .sort((a, b) => b[1] - a[1]) .map(e => e[0]); } /** * 记录一轮进化结果 * @param {object} record * @param {number} record.iteration - 迭代轮次 * @param {object} record.strategy - 使用的策略 * @param {number} record.score - 本轮分数 * @param {string[]} [record.weakDimensions] - 弱项维度 * @param {string[]} [record.strongDimensions] - 强项维度 * @param {object[]} [record.adjustments] - 调整记录 * @param {string[]} [record.lessonsLearned] - 教训 */ record(record) { const data = this._load(); const entry = { ...record, timestamp: new Date().toISOString(), }; data.iterations = data.iterations || []; data.iterations.push(entry); // 更新聚合指标 data.totalAttempts = (data.totalAttempts || 0) + 1; if (!data.bestScore || (record.score && record.score > data.bestScore)) { data.bestScore = record.score; } // 裁剪历史 if (data.iterations.length > MAX_RECORDS) { data.iterations = data.iterations.slice(-MAX_RECORDS); } this._save(data); } /** * 检测是否处于停滞 (连续 N 轮分数变化 < threshold) * @param {number} rounds - 检查的轮次数 * @param {number} threshold - 分数变化阈值 * @returns {boolean} */ isStagnant(rounds = 3, threshold = 2) { const data = this._load(); const iterations = data.iterations || []; if (iterations.length < rounds) return false; const recent = iterations.slice(-rounds); const scores = recent.map(r => r.score).filter(s => typeof s === 'number'); if (scores.length < rounds) return false; for (let i = 1; i < scores.length; i++) { if (Math.abs(scores[i] - scores[i - 1]) >= threshold) { return false; } } return true; } // ── 内部方法 ── _load() { if (this._data) return this._data; try { if (fs.existsSync(this.filePath)) { this._data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8')); return this._data; } } catch (err) { console.warn(`[EvolutionMemory] 读取失败: ${err.message}`); } this._data = { iterations: [], bestScore: null, totalAttempts: 0 }; return this._data; } _save(data) { try { const dir = path.dirname(this.filePath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), 'utf-8'); this._data = data; } catch (err) { console.error(`[EvolutionMemory] 保存失败: ${err.message}`); } } } module.exports = { EvolutionMemory, MEMORY_FILE, MAX_RECORDS };