evolution-memory.test.js
5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* Unit Tests — evolution-memory.js
*/
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import fs from 'fs';
import path from 'path';
const { EvolutionMemory, MEMORY_FILE, MAX_RECORDS } = require('../../src/core/evolution-memory');
describe('EvolutionMemory', () => {
const TMP = '/tmp/test-evolution-memory';
const SKILL_DIR = path.join(TMP, 'test-skill');
beforeAll(() => fs.mkdirSync(SKILL_DIR, { recursive: true }));
afterAll(() => { try { fs.rmSync(TMP, { recursive: true }); } catch {} });
beforeEach(() => {
// 清理 memory 文件
const fp = path.join(SKILL_DIR, MEMORY_FILE);
try { fs.unlinkSync(fp); } catch {}
});
describe('getHistory', () => {
it('should return empty history for new skill', () => {
const memory = new EvolutionMemory(SKILL_DIR);
const history = memory.getHistory();
expect(history.iterations).toEqual([]);
expect(history.bestScore).toBeNull();
expect(history.totalAttempts).toBe(0);
});
});
describe('record', () => {
it('should record an iteration', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({
iteration: 0,
strategy: { approach: 'full', steps: ['hunt', 'digest', 'skill_create'] },
score: 72,
weakDimensions: ['accuracy', 'robustness'],
lessonsLearned: ['需要更详细的 hunt'],
});
const history = memory.getHistory();
expect(history.iterations).toHaveLength(1);
expect(history.iterations[0].score).toBe(72);
expect(history.totalAttempts).toBe(1);
expect(history.bestScore).toBe(72);
});
it('should track bestScore across multiple records', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, strategy: { approach: 'full' }, score: 60 });
memory.record({ iteration: 1, strategy: { approach: 'refine' }, score: 85 });
memory.record({ iteration: 2, strategy: { approach: 'refine' }, score: 78 });
const history = memory.getHistory();
expect(history.bestScore).toBe(85);
expect(history.totalAttempts).toBe(3);
});
it('should persist to disk and reload', () => {
const memory1 = new EvolutionMemory(SKILL_DIR);
memory1.record({ iteration: 0, strategy: { approach: 'full' }, score: 65 });
// 新实例应从磁盘加载
const memory2 = new EvolutionMemory(SKILL_DIR);
const history = memory2.getHistory();
expect(history.iterations).toHaveLength(1);
expect(history.iterations[0].score).toBe(65);
});
});
describe('getLessons', () => {
it('should extract unique lessons from history', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, score: 50, lessonsLearned: ['教训A', '教训B'] });
memory.record({ iteration: 1, score: 60, lessonsLearned: ['教训B', '教训C'] });
const lessons = memory.getLessons();
expect(lessons).toHaveLength(3);
expect(lessons).toContain('教训A');
expect(lessons).toContain('教训B');
expect(lessons).toContain('教训C');
});
it('should return empty for no history', () => {
const memory = new EvolutionMemory(SKILL_DIR);
expect(memory.getLessons()).toEqual([]);
});
});
describe('getBestStrategy', () => {
it('should return strategy with highest score', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, strategy: { approach: 'full' }, score: 50 });
memory.record({ iteration: 1, strategy: { approach: 'refine' }, score: 88 });
memory.record({ iteration: 2, strategy: { approach: 'restructure' }, score: 75 });
const best = memory.getBestStrategy();
expect(best.approach).toBe('refine');
});
it('should return null for empty history', () => {
const memory = new EvolutionMemory(SKILL_DIR);
expect(memory.getBestStrategy()).toBeNull();
});
});
describe('getRecentWeakDimensions', () => {
it('should return weak dimensions sorted by frequency', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, score: 50, weakDimensions: ['accuracy', 'completeness'] });
memory.record({ iteration: 1, score: 60, weakDimensions: ['accuracy', 'robustness'] });
memory.record({ iteration: 2, score: 65, weakDimensions: ['accuracy'] });
const weaks = memory.getRecentWeakDimensions(3);
expect(weaks[0]).toBe('accuracy'); // 出现 3 次
});
});
describe('isStagnant', () => {
it('should detect stagnation', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, score: 70 });
memory.record({ iteration: 1, score: 71 });
memory.record({ iteration: 2, score: 71 });
expect(memory.isStagnant(3, 2)).toBe(true);
});
it('should not flag as stagnant when scores differ', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, score: 60 });
memory.record({ iteration: 1, score: 70 });
memory.record({ iteration: 2, score: 75 });
expect(memory.isStagnant(3, 2)).toBe(false);
});
it('should not flag with insufficient data', () => {
const memory = new EvolutionMemory(SKILL_DIR);
memory.record({ iteration: 0, score: 60 });
expect(memory.isStagnant(3, 2)).toBe(false);
});
});
});