test-memory-phase3.js
6.9 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env node
/**
* Memory System Phase 3 — 集成测试
*
* 测试:
* 1. autoReflect: 高复杂度对话自动触发反思
* 2. consolidateMemory: 过期 short-term 合并到 long-term
* 3. generateDailyReport: 日报生成
*/
const http = require('http');
const fs = require('fs');
const path = require('path');
const BASE = 'http://localhost:3000';
const AGENT = 'ceo';
const AGENT_DIR = path.resolve(__dirname, '../../data/agents', AGENT);
const MEMORY_DIR = path.join(AGENT_DIR, 'memory');
let passed = 0, failed = 0;
function assert(condition, msg) {
if (condition) { passed++; console.log(` ✅ ${msg}`); }
else { failed++; console.log(` ❌ ${msg}`); }
}
function post(url, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body);
const u = new URL(url);
const req = http.request({ hostname: u.hostname, port: u.port, path: u.pathname, method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }
}, res => {
let buf = '';
res.on('data', c => buf += c);
res.on('end', () => { try { resolve(JSON.parse(buf)); } catch { resolve(buf); } });
});
req.on('error', reject);
req.write(data);
req.end();
});
}
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
async function main() {
console.log('\n🧪 Memory System Phase 3 — 集成测试\n');
console.log('═'.repeat(50));
// ── Test 1: 直接调用 autoReflect (模拟高复杂度对话) ──
console.log('\n📋 Test 1: autoReflect 引擎');
const { autoReflect } = require('../../server/services/memory-service');
// 模拟一个 5-tool-call 的成功对话
const mockMeta = {
status: 'completed',
stats: { toolCallCount: 6, messageCount: 3 },
createdAt: new Date(Date.now() - 120000).toISOString(),
updatedAt: new Date().toISOString(),
title: '创建新的 Marketing Agent 并配置所有技能文件',
};
const mockOutput = `创建完成!以下是步骤:
1. 创建 data/agents/marketing 目录结构
2. 编写 agent.md 和 soul.md
3. 配置 identity.md (名称: Marketing Agent)
4. 编写 SKILL.md 主技能
5. 更新 config.json
6. 验证 Agent 列表
文件已写入:
- data/agents/marketing/agent.md
- data/agents/marketing/soul.md
- data/agents/marketing/.claude/skills/main_skill/SKILL.md
Agent 已成功创建并加入系统。`;
const reflection = autoReflect(AGENT, 'test-reflect-001', mockMeta, mockOutput);
assert(reflection !== null, 'autoReflect 返回了结果');
assert(reflection.patterns.length >= 1, `提取了 ${reflection.patterns.length} 个模式`);
assert(reflection.plan !== null, '自动固化了方案到小脑');
// 验证反思日志
const reflectDir = path.join(MEMORY_DIR, 'reflections');
const reflectFiles = fs.existsSync(reflectDir)
? fs.readdirSync(reflectDir).filter(f => f.startsWith('reflect-'))
: [];
assert(reflectFiles.length >= 1, `reflections/ 有 ${reflectFiles.length} 个日志文件`);
// 验证 memory.md 更新
const memContent = fs.readFileSync(path.join(MEMORY_DIR, 'memory.md'), 'utf-8');
assert(!memContent.includes('已验证的模式\n(尚无记录)'), 'memory.md "已验证模式" 段有内容');
// 验证小脑方案
const cerebellumFiles = fs.readdirSync(path.join(MEMORY_DIR, 'cerebellum'));
assert(cerebellumFiles.length >= 3, `cerebellum/ 有 ${cerebellumFiles.length} 个方案 (含自动固化的)`);
// ── Test 2: autoReflect 对简单对话不触发 ──
console.log('\n📋 Test 2: 简单对话不触发反思');
const simpleMeta = {
status: 'completed',
stats: { toolCallCount: 1, messageCount: 1 },
createdAt: new Date(Date.now() - 10000).toISOString(),
updatedAt: new Date().toISOString(),
title: '回答一个简单问题',
};
const simpleResult = autoReflect(AGENT, 'test-simple', simpleMeta, '这是一个简单回答');
assert(simpleResult === null, '简单对话 (1 tool, 10s) 不触发反思');
// ── Test 3: autoReflect 对失败对话提取教训 ──
console.log('\n📋 Test 3: 失败对话提取教训');
const failMeta = {
status: 'failed',
stats: { toolCallCount: 3, messageCount: 2 },
createdAt: new Date(Date.now() - 90000).toISOString(),
updatedAt: new Date().toISOString(),
title: '部署服务到生产环境',
};
const failOutput = 'error: Connection refused to database. Error: ECONNREFUSED 127.0.0.1:5432';
const failReflect = autoReflect(AGENT, 'test-fail', failMeta, failOutput);
assert(failReflect !== null, '失败对话触发了反思');
assert(failReflect.lessons.length >= 1, `提取了 ${failReflect.lessons.length} 个教训`);
// ── Test 4: consolidateMemory ──
console.log('\n📋 Test 4: 记忆合并');
// 创建一些"过期"的 short-term 文件 (时间戳 = 8天前)
const shortDir = path.join(MEMORY_DIR, 'short-term');
const eightDaysAgo = Date.now() - 8 * 24 * 60 * 60 * 1000;
for (let i = 0; i < 3; i++) {
const ts = eightDaysAgo + i * 1000;
fs.writeFileSync(path.join(shortDir, `session-${ts}.md`),
`# Session 摘要\n\n- **时间**: 2026-03-17 10:${i}0:00\n\n## 任务\n测试任务${i}\n\n## 结果\n测试结果${i}\n`, 'utf-8');
}
const consolidateResult = await post(`${BASE}/api/agent/${AGENT}/memory/consolidate`, {});
assert(consolidateResult.merged === 3, `合并了 ${consolidateResult.merged} 个过期 session (期望 3)`);
// 验证 long-term 目录有文件
const longTermDir = path.join(MEMORY_DIR, 'long-term');
const ltFiles = fs.existsSync(longTermDir)
? fs.readdirSync(longTermDir).filter(f => f.startsWith('consolidate-'))
: [];
assert(ltFiles.length >= 1, `long-term/ 有 ${ltFiles.length} 个合并记录`);
// 验证过期文件已删除
const remainingShort = fs.readdirSync(shortDir).filter(f => {
const ts = parseInt(f.replace('session-', '').replace('.md', ''));
return !isNaN(ts) && ts < eightDaysAgo + 5000;
});
assert(remainingShort.length === 0, '过期的 short-term 文件已删除');
// ── Test 5: generateDailyReport ──
console.log('\n📋 Test 5: 日报生成');
const reportResult = await post(`${BASE}/api/agent/${AGENT}/memory/daily-report`, {});
assert(reportResult.success, '日报生成成功');
const today = new Date().toISOString().substring(0, 10);
const dailyFile = path.join(MEMORY_DIR, 'daily', `${today}.md`);
assert(fs.existsSync(dailyFile), `daily/${today}.md 文件存在`);
if (fs.existsSync(dailyFile)) {
const dailyContent = fs.readFileSync(dailyFile, 'utf-8');
assert(dailyContent.includes('# 日报'), '日报包含标题');
assert(dailyContent.includes('对话数'), '日报包含统计');
}
// ── 结果 ──
console.log('\n' + '═'.repeat(50));
console.log(`\n🏁 测试结果: ${passed} 通过, ${failed} 失败\n`);
if (failed > 0) process.exit(1);
}
main().catch(err => { console.error('❌ 测试崩溃:', err); process.exit(1); });