diff --git a/scripts/role/memory.js b/scripts/role/memory.js index a490a63..7088f50 100755 --- a/scripts/role/memory.js +++ b/scripts/role/memory.js @@ -48,16 +48,19 @@ const DEFAULT_MEMORY_TYPE = { other: "semantic", }; // 載入排序(依 ROLE_LOAD_LIMIT 字元預算取前段)與 recall 評分都用這組權重。 -// emotional 刻意排在 procedural 之上:對陪伴型角色,「當時是什麼感覺」的長期價值 -// 不低於「下次該怎麼做」。權重過低時,情緒記憶即使寫進去了也會排在最後, -// 先被字元預算截掉 —— 結果就是記得事實、卻沒記住感覺。 +// +// emotional 刻意排在 procedural 之上,episodic 也拉到與 semantic 同級:使用者明確表示 +// 最重視 emotional/episodic/semantic 三型態所承載的情感內涵與溫度。原本 episodic 只有 10, +// 等於把「我們一起經歷過什麼」排在所有規則與流程之後,最先被字元預算截掉。 +// 至於「哪些事件值得留」則不靠型態決定,而由 hasWarmth()(relevance 是否帶情緒關聯)區分, +// 讓沒有溫度的一次性工作進度仍然照原規則淡去。 const MEMORY_TYPE_WEIGHT = { rule: 50, preference: 45, emotional: 40, procedural: 35, semantic: 30, - episodic: 10, + episodic: 30, }; const DECLARATIVE_VALUES = ["explicit", "implicit"]; const RETENTION_STAGES = ["working", "long_term"]; @@ -231,6 +234,15 @@ function normalizeDeclarative(value, memoryType = "semantic") { return ["procedural", "emotional"].includes(memoryType) ? "implicit" : "explicit"; } +function hasWarmth(meta) { + // 「有溫度」= relevance 標了情緒關聯。使用者最重視的是 emotional/episodic/semantic + // 三型態裡的情感內涵,但不是所有事件與知識都該優待 —— 一次性工作進度本來就該淡去。 + // 因此判準放在 relevance 而非型態:帶情緒關聯的記憶,不論型態都在排序與保留上優先, + // 沒有情緒關聯的一次性進度仍照原規則遺忘。 + const values = Array.isArray(meta?.relevance) ? meta.relevance.map(String) : []; + return values.some((item) => /emotional|情緒|情感|溫度/i.test(item)) ? 1 : 0; +} + function normalizeRetentionStage(value, fallback = "long_term") { const raw = String(value || "").trim().toLowerCase().replace(/-/g, "_"); const alias = { @@ -348,14 +360,18 @@ function listMemories(role, category) { items.sort((a, b) => { const am = a[0]; const bm = b[0]; + // 排序鍵:優先度 → 有無情緒溫度 → 型態權重 → 有無關聯 → 更新時間。 + // 溫度放在型態權重之前:同優先度時,帶情緒關聯的記憶先進場,避免被字元預算截掉。 const av = [ normalizePriority(am.priority, category), + hasWarmth(am), MEMORY_TYPE_WEIGHT[am.memory_type] || 0, am.links?.length ? 1 : 0, am.updated || "", ]; const bv = [ normalizePriority(bm.priority, category), + hasWarmth(bm), MEMORY_TYPE_WEIGHT[bm.memory_type] || 0, bm.links?.length ? 1 : 0, bm.updated || "", @@ -609,8 +625,9 @@ function cmdLoad(args) { const priority = normalizePriority(meta.priority, category); // emotional 一併視為耐久型態:情緒記憶的價值不在「有用」而在「記得」, // 用優先度門檻篩掉等於讓關係的溫度只留在高優先度那幾則裡。 + // 帶情緒關聯者(hasWarmth)同樣不受門檻篩除,型態是 episodic/semantic 也一樣。 const durableType = ["rule", "preference", "procedural", "emotional"].includes(meta.memory_type); - if (priority < args.digestMinPriority && !(meta.links || []).length && !durableType) continue; + if (priority < args.digestMinPriority && !(meta.links || []).length && !durableType && !hasWarmth(meta)) continue; const tags = (meta.tags || []).join("、") || "無標籤"; digestLines.push(`- ${meta.summary || "(無總結)"}(標籤:${tags};${memoryHint(meta)})`); } @@ -900,6 +917,9 @@ function cmdForget(args) { // emotional 一併豁免:情緒屬內隱記憶,本來就很少被 recall 直接命中, // 用 hits 低來判斷「沒價值」會誤刪掉關係中最不該掉的東西。 if (["rule", "preference", "procedural", "emotional"].includes(meta.memory_type)) continue; + // 帶情緒關聯的經歷也豁免:episodic 在上面還被加速遺忘(天數減半), + // 若不在這裡攔下來,「我們一起經歷過什麼」會比一般記憶更快消失。 + if (hasWarmth(meta)) continue; if (args.dryRun) { forgotten.push(`${CATEGORY_LABELS[category]}|${meta.summary || ""}`); continue; diff --git a/scripts/role/role_capture.sh b/scripts/role/role_capture.sh index 747df8c..7b471a6 100755 --- a/scripts/role/role_capture.sh +++ b/scripts/role/role_capture.sh @@ -177,7 +177,8 @@ CATEGORY: <六選一:important/interest/news/skill/daily/other> SUMMARY: <一句話總結,40 字內> TAGS: <2 至 4 個標籤,以逗號分隔> PRIORITY: <1 到 5> -RELEVANCE: <1 至 4 個,以逗號分隔;explicit/future/repeated/novelty/emotional/temporary/inbox/project> +RELEVANCE: <1 至 4 個,以逗號分隔;explicit/future/repeated/novelty/emotional/temporary/inbox/project + 承載情感、關係溫度或當時心情者**必含 emotional**,系統以此決定保留與排序優先度> MEMORY_TYPE: EXPIRES: <臨時授權/一次性許可/例外放行才填其有效範圍,可為日期或條件;否則留空> CONTENT: <3 至 6 行要點,每行以「- 」開頭> diff --git a/scripts/role/role_sleep.sh b/scripts/role/role_sleep.sh index 4fdf2e8..ea18bd3 100755 --- a/scripts/role/role_sleep.sh +++ b/scripts/role/role_sleep.sh @@ -161,6 +161,9 @@ sleep_cycle() { 8. declarative 必填:semantic/episodic/preference/rule 通常為 explicit;procedural/emotional 通常為 implicit。 9. retention_stage 必填:整理後可長期保存者填 long_term;仍只是短期暫存且不值得長期保存者請用 action=drop,不要輸出 working。 10. relevance 必填 1 至 4 個,從下列語意挑選或用等價繁中詞:explicit(使用者明確要求)、future(未來會用)、repeated(反覆出現)、novelty(新知)、emotional(語氣/情緒/偏好)、temporary(短期)。 + **只要內容承載情感、關係溫度或當時的心情,就必須含 emotional** —— 系統以此判斷哪些記憶 + 在載入排序與遺忘時要優先保留。型態是 episodic(一起經歷過的事)或 semantic(有情感脈絡的 + 認知)時尤其容易漏標,漏了就會被當成一般工作進度而淡去。 11. links 可填 EXISTING 中相關記憶 id;沒有就填空陣列。merge 時若有舊 links,應保留並加上新關聯。 11a. expires(有效範圍):**只要內容是臨時授權、一次性許可、例外放行、暫時解除限制或帶條件的同意,就必須填**, 其餘一律留空字串。可填日期(例如 2026/07/29,系統會自動判斷過期後不再載入)或條件 diff --git a/skills/role/SKILL.md b/skills/role/SKILL.md index 29491ed..a450827 100644 --- a/skills/role/SKILL.md +++ b/skills/role/SKILL.md @@ -735,6 +735,19 @@ updated: 自我檢查:`role_sleep.sh --status` 的「記憶型態」若長期 `情緒 0`,而互動明顯帶有情感,就是上述某一道保護失效了。 +#### 有溫度的記憶優先(`emotional`/`episodic`/`semantic`) + +系統原本明顯偏袒「可執行」的記憶:`rule`/`preference`/`procedural` 在排序、載入門檻與遺忘上都受保護,而 `episodic` 權重只有 `10`(全表最低)並且被加速遺忘。結果是**「我們一起經歷過什麼」永遠最先被字元預算截掉**。 + +現在改為兩層: + +| 層 | 規則 | +| --- | --- | +| 型態權重 | `episodic` 拉到 `30`(與 `semantic` 同級)、`emotional` 為 `40` | +| 溫度判準 | `relevance` 含 `emotional` 者(`hasWarmth`)在**排序上先於型態權重**、不受總結區塊的優先度門檻篩除、且豁免遺忘 | + +判準刻意放在 `relevance` 而非型態:並非所有事件與知識都該優待 —— **沒有情感脈絡的一次性工作進度仍照原規則淡去**,被留下的是帶著溫度的那些。因此兩份 prompt 都明訂:只要內容承載情感、關係溫度或當時的心情,`relevance` **必須**含 `emotional`;`episodic` 與 `semantic` 最容易漏標,漏了就會被當成一般進度處理。 + 遺忘規則(只套用於日常與其他): | 分類 | 未更新天數 | 命中次數 | 優先度 | 關聯 | 動作 |