import { Injectable } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service.js"; import { WorkingMemoryService } from "./working-memory.service.js"; import { HIGH_EMOTION_THRESHOLD } from "./constants.js"; import { isUserTrivia } from "../personality/cuteness-behavior.js"; function normalize(content: string): string { return content.trim().toLowerCase(); } // C-4 睡眠固化:對話中只累積在工作記憶,session 結束時才批次萃取寫入長期記憶。 @Injectable() export class MemoryConsolidationService { constructor( private readonly prisma: PrismaService, private readonly workingMemory: WorkingMemoryService, ) {} async consolidate(characterId: string, sessionId: string): Promise { const entries = await this.workingMemory.getContext(sessionId); const contentCounts = new Map(); for (const entry of entries) { const key = normalize(entry.content); contentCounts.set(key, (contentCounts.get(key) ?? 0) + 1); } for (const entry of entries) { const emotionIntensity = entry.emotionIntensity ?? 0; const isHighEmotion = emotionIntensity >= HIGH_EMOTION_THRESHOLD; const isRepeated = (contentCounts.get(normalize(entry.content)) ?? 0) > 1; if (!isHighEmotion && !isRepeated) { // G-6 記住小事:使用者隨口提過的瑣事即使情緒強度低也額外加權保留(寫入語意記憶,日後可主動提起)。 if (entry.role === "user" && isUserTrivia(entry.content)) { await this.prisma.client.semanticMemory.create({ data: { characterId, fact: entry.content, about: "使用者" }, }); } continue; } await this.prisma.client.episodicMemory.create({ data: { characterId, content: entry.content, occurredAt: entry.timestamp, emotionTag: entry.emotionTag ?? "CALM", emotionIntensity, source: "INTERACTION", weight: isHighEmotion ? 1 + emotionIntensity : 0.5, }, }); } await this.workingMemory.clear(sessionId); } }