import { Injectable, Inject } from '@nestjs/common'; import { EmbeddingService } from '../infrastructure/embedding/embedding.service'; import { Neo4jService } from '../infrastructure/database/neo4j/neo4j.service'; import { IUserMemoryRepository, ISystemExperienceRepository, USER_MEMORY_REPOSITORY, SYSTEM_EXPERIENCE_REPOSITORY, } from '../domain/repositories/memory.repository.interface'; import { UserMemoryEntity, MemoryType } from '../domain/entities/user-memory.entity'; import { SystemExperienceEntity, ExperienceType, VerificationStatus, } from '../domain/entities/system-experience.entity'; /** * 记忆管理服务 * 管理用户长期记忆和系统经验 */ @Injectable() export class MemoryService { constructor( private embeddingService: EmbeddingService, private neo4jService: Neo4jService, @Inject(USER_MEMORY_REPOSITORY) private memoryRepo: IUserMemoryRepository, @Inject(SYSTEM_EXPERIENCE_REPOSITORY) private experienceRepo: ISystemExperienceRepository, ) {} // ========== 用户记忆 ========== /** * 保存用户记忆 */ async saveUserMemory(params: { userId: string; memoryType: MemoryType; content: string; importance?: number; sourceConversationId?: string; relatedCategory?: string; }): Promise { const memory = UserMemoryEntity.create(params); // 生成向量 const embedding = await this.embeddingService.getEmbedding(params.content); memory.setEmbedding(embedding); // 保存到PostgreSQL await this.memoryRepo.save(memory); // 同时记录到Neo4j时间线 await this.neo4jService.recordUserEvent({ userId: params.userId, eventId: memory.id, eventType: `MEMORY_${params.memoryType}`, content: params.content, metadata: { importance: params.importance, relatedCategory: params.relatedCategory, }, }); console.log(`[MemoryService] Saved memory for user ${params.userId}: ${params.memoryType}`); return memory; } /** * 获取用户的相关记忆 */ async getUserRelevantMemories( userId: string, query: string, options?: { limit?: number; memoryTypes?: MemoryType[] }, ): Promise { const embedding = await this.embeddingService.getEmbedding(query); const results = await this.memoryRepo.searchByVector(userId, embedding, { limit: options?.limit || 5, minSimilarity: 0.6, }); // 记录访问 for (const { memory } of results) { memory.recordAccess(); await this.memoryRepo.update(memory); } return results.map(r => r.memory); } /** * 获取用户最重要的记忆(用于对话上下文) */ async getUserTopMemories(userId: string, limit = 5): Promise { return this.memoryRepo.findTopMemories(userId, limit); } /** * 获取用户所有记忆 */ async getUserMemories( userId: string, options?: { memoryType?: MemoryType; includeExpired?: boolean }, ): Promise { return this.memoryRepo.findByUserId(userId, options); } /** * 更新记忆重要性 */ async updateMemoryImportance(memoryId: string, importance: number): Promise { const memory = await this.memoryRepo.findById(memoryId); if (memory) { memory.updateImportance(importance); await this.memoryRepo.update(memory); } } /** * 标记记忆为过期 */ async expireMemory(memoryId: string): Promise { const memory = await this.memoryRepo.findById(memoryId); if (memory) { memory.markAsExpired(); await this.memoryRepo.update(memory); } } /** * 清理过期记忆 */ async cleanupExpiredMemories(userId: string, olderThanDays = 180): Promise { return this.memoryRepo.markExpiredMemories(userId, olderThanDays); } /** * 删除用户所有记忆(GDPR合规) */ async deleteUserMemories(userId: string): Promise { await this.memoryRepo.deleteByUserId(userId); console.log(`[MemoryService] Deleted all memories for user ${userId}`); } // ========== 系统经验 ========== /** * 提取并保存系统经验 */ async extractAndSaveExperience(params: { experienceType: ExperienceType; content: string; scenario: string; relatedCategory?: string; sourceConversationId: string; confidence?: number; }): Promise { // 生成向量 const embedding = await this.embeddingService.getEmbedding( `${params.scenario}\n${params.content}`, ); // 查找相似经验(用于合并) const similarExperiences = await this.experienceRepo.findSimilarExperiences( embedding, 0.9, // 高阈值,只合并非常相似的 ); if (similarExperiences.length > 0) { // 合并到现有经验 const existingExperience = similarExperiences[0]; existingExperience.addSourceConversation(params.sourceConversationId); existingExperience.setEmbedding(embedding); // 可以选择更新向量 await this.experienceRepo.update(existingExperience); console.log(`[MemoryService] Merged experience into ${existingExperience.id}`); return existingExperience; } // 创建新经验 const experience = SystemExperienceEntity.create(params); experience.setEmbedding(embedding); await this.experienceRepo.save(experience); console.log(`[MemoryService] Created new experience: ${experience.id}`); return experience; } /** * 获取相关系统经验 */ async getRelevantExperiences( query: string, options?: { experienceType?: ExperienceType; category?: string; limit?: number; }, ): Promise { const embedding = await this.embeddingService.getEmbedding(query); const results = await this.experienceRepo.searchByVector(embedding, { experienceType: options?.experienceType, activeOnly: true, limit: options?.limit || 5, minSimilarity: 0.7, }); // 记录使用 for (const { experience } of results) { experience.recordUsage(); await this.experienceRepo.update(experience); } return results.map(r => r.experience); } /** * 获取待验证的经验 */ async getPendingExperiences(options?: { experienceType?: ExperienceType; page?: number; pageSize?: number; }): Promise<{ items: SystemExperienceEntity[]; total: number; }> { const page = options?.page || 1; const pageSize = options?.pageSize || 20; const items = await this.experienceRepo.findPendingExperiences({ experienceType: options?.experienceType, limit: pageSize, offset: (page - 1) * pageSize, }); // 简单计数(实际应该有专门的count方法) const stats = await this.experienceRepo.getStatistics(); const total = stats.byStatus[VerificationStatus.PENDING] || 0; return { items, total }; } /** * 审批经验 */ async approveExperience(experienceId: string, adminId: string): Promise { const experience = await this.experienceRepo.findById(experienceId); if (!experience) { throw new Error(`Experience not found: ${experienceId}`); } experience.approve(adminId); await this.experienceRepo.update(experience); console.log(`[MemoryService] Experience ${experienceId} approved by ${adminId}`); } /** * 拒绝经验 */ async rejectExperience(experienceId: string, adminId: string): Promise { const experience = await this.experienceRepo.findById(experienceId); if (!experience) { throw new Error(`Experience not found: ${experienceId}`); } experience.reject(adminId); await this.experienceRepo.update(experience); console.log(`[MemoryService] Experience ${experienceId} rejected by ${adminId}`); } /** * 记录经验反馈 */ async recordExperienceFeedback(experienceId: string, positive: boolean): Promise { const experience = await this.experienceRepo.findById(experienceId); if (experience) { experience.recordFeedback(positive); await this.experienceRepo.update(experience); } } /** * 获取经验统计 */ async getExperienceStatistics(): Promise<{ total: number; byStatus: Record; byType: Record; }> { return this.experienceRepo.getStatistics(); } // ========== 用户时间线(Neo4j) ========== /** * 获取用户时间线 */ async getUserTimeline( userId: string, options?: { limit?: number; beforeDate?: Date; eventTypes?: string[]; }, ) { return this.neo4jService.getUserTimeline(userId, options); } /** * 初始化用户节点 */ async initializeUserNode(userId: string, properties?: Record): Promise { await this.neo4jService.createUserNode(userId, properties); } }