test-memory.js
6.36 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
#!/usr/bin/env node
/**
* Memory System Phase 1 MVP — 集成测试
*
* 测试流程:
* 1. 清理 CEO 的 short-term 和 memory.md
* 2. 发送第一次对话 → 验证 session 摘要和 memory.md 写入
* 3. 发送第二次对话 → 验证 prompt 中包含近期记忆
*/
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 SHORT_TERM_DIR = path.join(AGENT_DIR, 'memory', 'short-term');
const MEMORY_MD = path.join(AGENT_DIR, 'memory', 'memory.md');
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)); }
function waitForCompletion(agentId, convId, timeoutMs = 120000) {
const metaPath = path.join(AGENT_DIR, '..', agentId, 'conversations', convId, 'meta.json');
const start = Date.now();
return new Promise((resolve, reject) => {
const check = () => {
if (Date.now() - start > timeoutMs) return reject(new Error('Timeout'));
try {
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
if (meta.status === 'completed' || meta.status === 'failed') return resolve(meta);
} catch {}
setTimeout(check, 2000);
};
check();
});
}
async function main() {
console.log('\n🧪 Memory System Phase 1 — 集成测试\n');
console.log('═'.repeat(50));
// ── Step 0: 清理 ──
console.log('\n📋 Step 0: 清理历史数据');
// 清理 short-term
if (fs.existsSync(SHORT_TERM_DIR)) {
const files = fs.readdirSync(SHORT_TERM_DIR).filter(f => f.startsWith('session-'));
files.forEach(f => fs.unlinkSync(path.join(SHORT_TERM_DIR, f)));
console.log(` 🗑️ 清理了 ${files.length} 个 session 文件`);
}
// 重置 memory.md
fs.writeFileSync(MEMORY_MD, '# 我的记忆\n\n## 关键事实\n(尚无记录)\n\n## 已验证的模式\n(尚无记录)\n\n## 失败教训\n(尚无记录)\n', 'utf-8');
console.log(' 🗑️ memory.md 已重置');
// ── Step 1: 第一次对话 ──
console.log('\n📋 Step 1: 发送第一次对话');
const resp1 = await post(`${BASE}/api/agent/task/dispatch`, {
from: 'master', to: AGENT,
task: '请用一句话总结BLM模型的核心思想。不需要使用任何工具,直接回答即可。',
priority: 'P2', autoExecute: true,
});
assert(resp1.success, `dispatch 成功 (taskId: ${resp1.taskId})`);
const convId1 = resp1.executionConvId;
console.log(' ⏳ 等待对话完成...');
const meta1 = await waitForCompletion(AGENT, convId1);
assert(meta1.status === 'completed', `对话完成 (status: ${meta1.status})`);
// 等待 memory 写入(异步)
await sleep(2000);
// 验证 short-term
const sessions1 = fs.existsSync(SHORT_TERM_DIR)
? fs.readdirSync(SHORT_TERM_DIR).filter(f => f.startsWith('session-'))
: [];
assert(sessions1.length === 1, `short-term 有 ${sessions1.length} 个 session 文件`);
if (sessions1.length > 0) {
const sessionContent = fs.readFileSync(path.join(SHORT_TERM_DIR, sessions1[0]), 'utf-8');
assert(sessionContent.includes('# Session 摘要'), 'session 包含标题');
assert(sessionContent.includes('BLM') || sessionContent.includes('战略'), 'session 包含任务关键词');
assert(sessionContent.includes('工具调用'), 'session 包含 toolCall 统计');
console.log(` 📄 Session 文件: ${sessions1[0]} (${sessionContent.length} 字)`);
}
// 验证 memory.md
const memContent1 = fs.readFileSync(MEMORY_MD, 'utf-8');
const hasNewFact = !memContent1.includes('(尚无记录)') || memContent1.match(/- \[202/);
assert(hasNewFact, `memory.md 有新事实记录`);
console.log(` 📝 memory.md: ${memContent1.length} 字`);
// ── Step 2: 第二次对话 ──
console.log('\n📋 Step 2: 发送第二次对话 (验证近期记忆注入)');
const resp2 = await post(`${BASE}/api/agent/task/dispatch`, {
from: 'master', to: AGENT,
task: '你还记得刚才我们讨论了什么模型吗?如果你的记忆中有相关信息,请引用它。不需要使用工具。',
priority: 'P2', autoExecute: true,
});
assert(resp2.success, `第二次 dispatch 成功 (taskId: ${resp2.taskId})`);
const convId2 = resp2.executionConvId;
console.log(' ⏳ 等待第二次对话完成...');
const meta2 = await waitForCompletion(AGENT, convId2);
assert(meta2.status === 'completed', `第二次对话完成 (status: ${meta2.status})`);
await sleep(2000);
// 验证 short-term 增长
const sessions2 = fs.readdirSync(SHORT_TERM_DIR).filter(f => f.startsWith('session-'));
assert(sessions2.length === 2, `short-term 有 ${sessions2.length} 个 session 文件 (期望 2)`);
// 验证 memory.md 条目增长
const memContent2 = fs.readFileSync(MEMORY_MD, 'utf-8');
const factCount = (memContent2.match(/- \[202/g) || []).length;
assert(factCount >= 2, `memory.md 有 ${factCount} 条事实 (期望 ≥2)`);
// 检查最后的 messages.log 是否表明 Agent "记住了"
try {
const logPath = path.join(AGENT_DIR, 'conversations', convId2, 'messages.log');
const log2 = fs.readFileSync(logPath, 'utf-8');
const mentionsBLM = log2.includes('BLM') || log2.includes('记忆') || log2.includes('之前');
assert(mentionsBLM, `Agent 在第二次对话中引用了之前的信息`);
} catch { console.log(' ⚠️ 无法检查 messages.log'); }
// ── 结果 ──
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); });