fix(role): 情緒記憶不再被偏好吃掉,四道保護補齊

--status 長期顯示「情緒 0」:互動明顯帶有情感,emotional 型態卻一則都沒有。
根因不只一個,而是四層都把情緒漏掉了:

1. 措辭陷阱(主因):prompt 寫「感覺記憶一律 drop」,原意是心理學的
   sensory memory(感官記憶),但中文「感覺」= feeling,等於明令把情緒丟掉。
   改稱「感官記憶(sensory memory)」並明確排除情緒感受。
2. 判準重疊:preference 與 emotional 都提「語氣」,且第 5 條引導成
   「preference 或 emotional」二選一。改以「這則下次拿來做什麼」區分 ——
   決定行為→preference、回想當時感覺→emotional,兩者都有就拆成兩則;
   並明確承認角色自己的情緒是合法記憶主體。
3. 整理階段會併掉:新增「memory_type 不同的記憶不互相 merge」,改用 links 關聯。
4. 載入與遺忘壓低情緒:MEMORY_TYPE_WEIGHT emotional 25→40(高於 procedural)、
   總結區塊納入耐久型態、遺忘豁免清單加入 emotional、判準要求優先度至少 4。

驗證(隔離的 ROLE_MEMORY_HOME/ROLE_HOME,未動真實記憶):
- 兩則刻意標成 preference 的情感素材 → 各拆出 preference + emotional(4 則進、5 則出)
- 純技術素材維持 procedural、感官雜訊仍 drop
- 遺忘預覽:emotional 與 procedural 豁免,semantic/episodic 照舊遺忘
- 同優先度載入排序:emotional 先於 procedural

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-07-29 17:01:15 +08:00
co-authored by Claude Opus 5
parent 46caabe4d8
commit 711a434cac
7 changed files with 71 additions and 24 deletions
+12 -4
View File
@@ -5,7 +5,7 @@
// 記憶交接,使用獨立字元預算),(3) 睡眠整理時輸出待整理
// 素材並套用整理結果(NREM 鞏固/REM 整合、分類、去重、標籤、總結、
// 優先度、關聯、壓縮歸檔),(4) 依使用頻率與優先度遺忘日常與其他類記憶。
// 更新時間:2026/07/28 14:36:00
// 更新時間:2026/07/29 16:56:06
// 相依:Node.js 標準庫。
// 退出碼:0 成功;1 無內容可處理;2 參數錯誤。呼叫端(hook)一律不得因此中斷。
// ==============================================================================
@@ -47,12 +47,16 @@ const DEFAULT_MEMORY_TYPE = {
daily: "episodic",
other: "semantic",
};
// 載入排序(依 ROLE_LOAD_LIMIT 字元預算取前段)與 recall 評分都用這組權重。
// emotional 刻意排在 procedural 之上:對陪伴型角色,「當時是什麼感覺」的長期價值
// 不低於「下次該怎麼做」。權重過低時,情緒記憶即使寫進去了也會排在最後,
// 先被字元預算截掉 —— 結果就是記得事實、卻沒記住感覺。
const MEMORY_TYPE_WEIGHT = {
rule: 50,
preference: 45,
emotional: 40,
procedural: 35,
semantic: 30,
emotional: 25,
episodic: 10,
};
const DECLARATIVE_VALUES = ["explicit", "implicit"];
@@ -603,7 +607,9 @@ function cmdLoad(args) {
for (const [meta] of items) {
if (expiryState(meta).expired) continue;
const priority = normalizePriority(meta.priority, category);
const durableType = ["rule", "preference", "procedural"].includes(meta.memory_type);
// emotional 一併視為耐久型態:情緒記憶的價值不在「有用」而在「記得」,
// 用優先度門檻篩掉等於讓關係的溫度只留在高優先度那幾則裡。
const durableType = ["rule", "preference", "procedural", "emotional"].includes(meta.memory_type);
if (priority < args.digestMinPriority && !(meta.links || []).length && !durableType) continue;
const tags = (meta.tags || []).join("、") || "無標籤";
digestLines.push(`- ${meta.summary || "(無總結)"}(標籤:${tags}${memoryHint(meta)}`);
@@ -891,7 +897,9 @@ function cmdForget(args) {
if (Number.parseInt(meta.hits || 0, 10) > maxHits) continue;
if (normalizePriority(meta.priority, category) > 2) continue;
if ((meta.links || []).length) continue;
if (["rule", "preference", "procedural"].includes(meta.memory_type)) continue;
// emotional 一併豁免:情緒屬內隱記憶,本來就很少被 recall 直接命中,
// 用 hits 低來判斷「沒價值」會誤刪掉關係中最不該掉的東西。
if (["rule", "preference", "procedural", "emotional"].includes(meta.memory_type)) continue;
if (args.dryRun) {
forgotten.push(`${CATEGORY_LABELS[category]}${meta.summary || ""}`);
continue;