feat: 完成 C 群組 — 記憶子系統
- WorkingMemoryService:session 對話上下文緩衝,token 溢位時優先裁切最舊的低情緒段落 - JobQueue 抽象層(now/schedule(at)/every(cron))與 InProcessJobQueue 實作(node-cron), R-6 會換成 BullMQ 但呼叫端介面不變 - MemoryConsolidationService:睡眠固化,只有高情緒或重複提及的內容才寫入 EpisodicMemory - ForgettingSweepService:低情緒且長期未提取的記憶逐次降權、weight 過低後刪除, 已掛上每小時一次的 JobQueue.every 排程 - KeywordMemoryRetriever:關鍵字+時間近因+情緒權重排序,提取命中即更新提取次數/時間 - apps/api 新增 /memory/* 端點作為引擎驗證介面(H 群組會決定併入正式對話管線後的去留) - scripts/smoke/C.mjs:端到端驗證溢位裁切、只寫工作記憶、固化篩選、檢索排序、 提取即改寫、schedule(at) 準時觸發、遺忘衰減與刪除 npm run restart && npm run smoke -- C 皆通過(C-V),A/B 群組冒煙測試無回歸。
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
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";
|
||||
|
||||
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<void> {
|
||||
const entries = this.workingMemory.getContext(sessionId);
|
||||
|
||||
const contentCounts = new Map<string, number>();
|
||||
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) {
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this.workingMemory.clear(sessionId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user