/** * Memory Manager — 记忆管理器 * * 基于 SQLite(better-sqlite3)的三层记忆系统。 * 表结构由 DatabaseService 统一创建,此处不再重复。 * * @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第六章 * @see standard/开发规范.md — 使用 better-sqlite3(禁止自写数据库层) */ import { nanoid } from 'nanoid'; import type Database from 'better-sqlite3'; import log from 'electron-log'; export type MemoryType = 'episodic' | 'semantic' | 'working'; export type MemorySource = 'user_input' | 'tool_result' | 'agent_thought' | 'imported'; export interface MemoryItem { id: string; type: MemoryType; content: string; summary?: string; source: MemorySource; importance: number; sessionId?: string; createdAt: number; expiresAt?: number; } export interface SearchResult extends MemoryItem { score: number; } interface MemorySearchOptions { topK?: number; sessionId?: string; type?: MemoryType; minImportance?: number; } /** * 记忆管理器 */ export class MemoryManager { constructor(private getDB: () => Database.Database) {} /** * 初始化(表结构由 DatabaseService 创建) */ initialize(): void { log.info('MemoryManager initialized'); } /** * 存储记忆 */ store(item: Omit): string { const db = this.getDB(); const id = `mem_${nanoid(12)}`; const now = Date.now(); const importance = item.importance ?? this.calculateImportance(item); switch (item.type) { case 'episodic': db.prepare(` INSERT INTO episodic_memories (id, session_id, content, summary, source, importance, created_at) VALUES (?, ?, ?, ?, ?, ?, ?) `).run(id, item.sessionId ?? null, item.content, item.summary ?? null, item.source, importance, now); break; case 'semantic': db.prepare(` INSERT OR REPLACE INTO semantic_memories (id, key, value, category, confidence, source_session, created_at, updated_at, access_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0) `).run(id, id, item.content, 'general', importance, item.sessionId ?? null, now, now); break; case 'working': db.prepare(` INSERT OR REPLACE INTO working_memories (id, session_id, task_id, key, value, updated_at) VALUES (?, ?, ?, ?, ?, ?) `).run(id, item.sessionId ?? 'default', 'default', 'default', item.content, now); break; } log.debug(`Memory stored: ${id} (${item.type})`); return id; } /** * 检索记忆(关键词搜索) */ search(query: string, options: MemorySearchOptions = {}): SearchResult[] { const db = this.getDB(); const { topK = 5, type, minImportance = 0 } = options; if (!query) return []; const pattern = `%${query}%`; const results: SearchResult[] = []; // 搜索情节记忆 if (!type || type === 'episodic') { const rows = db.prepare(` SELECT * FROM episodic_memories WHERE (content LIKE ? OR summary LIKE ?) AND importance >= ? ORDER BY importance DESC, created_at DESC LIMIT ? `).all(pattern, pattern, minImportance, topK) as Array<{ id: string; session_id: string | null; content: string; summary: string | null; source: string; importance: number; created_at: number; expires_at: number | null; }>; for (const row of rows) { results.push({ id: row.id, type: 'episodic', content: row.content, summary: row.summary ?? undefined, source: row.source as MemorySource, importance: row.importance, sessionId: row.session_id ?? undefined, createdAt: row.created_at, expiresAt: row.expires_at ?? undefined, score: row.importance, }); } } // 搜索语义记忆 if (!type || type === 'semantic') { const rows = db.prepare(` SELECT * FROM semantic_memories WHERE (key LIKE ? OR value LIKE ?) AND confidence >= ? ORDER BY confidence DESC, access_count DESC LIMIT ? `).all(pattern, pattern, minImportance, Math.ceil(topK / 2)) as Array<{ id: string; key: string; value: string; category: string | null; confidence: number; source_session: string | null; created_at: number; }>; for (const row of rows) { results.push({ id: row.id, type: 'semantic', content: row.value, source: 'imported', importance: row.confidence, sessionId: row.source_session ?? undefined, createdAt: row.created_at, score: row.confidence, }); } } // 搜索工作记忆 if (type === 'working') { const rows = db.prepare(` SELECT * FROM working_memories WHERE (key LIKE ? OR value LIKE ?) ORDER BY updated_at DESC LIMIT ? `).all(pattern, pattern, topK) as Array<{ id: string; session_id: string; task_id: string; key: string; value: string; updated_at: number; }>; for (const row of rows) { results.push({ id: row.id, type: 'working', content: row.value, source: 'agent_thought', importance: 0.5, sessionId: row.session_id, createdAt: row.updated_at, score: 0.3, // 工作记忆权重较低 }); } } return results.sort((a, b) => b.score - a.score).slice(0, topK); } /** * 获取工作记忆 */ getWorkingMemory(sessionId: string, taskId: string = 'default'): Map { const db = this.getDB(); const rows = db.prepare(` SELECT key, value FROM working_memories WHERE session_id = ? AND task_id = ? `).all(sessionId, taskId) as Array<{ key: string; value: string }>; return new Map(rows.map((r) => [r.key, r.value])); } /** * 更新工作记忆 */ setWorkingMemory(sessionId: string, taskId: string, key: string, value: string): void { const db = this.getDB(); db.prepare(` INSERT OR REPLACE INTO working_memories (id, session_id, task_id, key, value, updated_at) VALUES (?, ?, ?, ?, ?, ?) `).run(`wm_${nanoid(8)}`, sessionId, taskId, key, value, Date.now()); } /** * 清除工作记忆 */ clearWorkingMemory(sessionId: string, taskId?: string): void { const db = this.getDB(); if (taskId) { db.prepare('DELETE FROM working_memories WHERE session_id = ? AND task_id = ?').run(sessionId, taskId); } else { db.prepare('DELETE FROM working_memories WHERE session_id = ?').run(sessionId); } } /** * 清理过期记忆 */ cleanupExpired(): number { const db = this.getDB(); const result = db.prepare('DELETE FROM episodic_memories WHERE expires_at IS NOT NULL AND expires_at < ?').run(Date.now()); return result.changes; } private calculateImportance(item: Omit): number { let score = 0.5; if (item.source === 'user_input') score += 0.2; if (item.source === 'tool_result') score += 0.1; if (item.content.length > 200) score += 0.1; return Math.min(1, Math.max(0, score)); } }