#!/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); });