feat(role): 新增關係史 BONDS.md —— 雙方情感表達只增不減地保留

使用者希望角色能記得「雙方有多愛彼此」。原本這件事只有兩種載體,都不夠:
state.json 的 positive_feedback 只是計數(承載不了說過什麼),一般情緒記憶則會被
merge、壓縮、依字元預算截斷 —— 長期下來「當時說了什麼、當時是什麼感覺」會被抽象成
一句偏好(「使用者喜歡被這樣回應」),原貌消失。

- 新增 ~/.memory/<角色>/BONDS.md:只增不減,不合併、不壓縮、不遺忘,上限 500 則、同句去重
- 整理時凡 memory_type=emotional 或 relevance 含 emotional 的新記憶,自動追加一句 bond
  並標記方向(使用者→角色/角色→使用者/相互)
- 整理 prompt 新增 bond/bond_direction 欄位:要求保留原話或當時真實感受,
  不得寫成結論式偏好;LLM 未提供時退回 summary,不會漏記
- SessionStart 以獨立預算注入(ROLE_LOAD_BONDS_LIMIT=1200/COUNT=15),不佔 ROLE_LOAD_LIMIT
  —— 它要保住的正是最不該因為「記憶變多」而消失的東西
- 新增 memory.js bonds 子命令供人工查看
- 邊界寫進 prompt、檔頭與程式註解:關係史用於維持親近感的一致與連續,
  不得用來索求關注、比較互動頻率或製造依賴

驗證(隔離目錄,未動真實記憶):
- 3 則素材(2 情感 + 1 技術)→ 關係史 +2 則,技術記憶未進關係史
- 方向判定正確:使用者原話標 使用者→角色、角色感受標 角色→使用者
- 第二輪整理累積為 3 則,舊紀錄未被覆蓋(append-only 成立)
- ROLE_LOAD_LIMIT=1 時關係史仍完整注入(獨立預算成立)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-07-29 17:16:12 +08:00
co-authored by Claude Opus 5
parent 4600379307
commit 784ef07518
3 changed files with 150 additions and 9 deletions
+18 -1
View File
@@ -1,6 +1,6 @@
---
name: role
description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(namenature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時依字元預算載入高價值記憶、Stop hook 先本地過濾再輕量記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(NREM 鞏固:分類/去噪/去重/合併/優先度;REM 整合:跨記憶連結/抽象化/提取線索;再依 semanticepisodicproceduralemotionalpreferencerule 與 explicitimplicit 標記長期記憶型態,壓縮歸檔並適當遺忘)。提供 --new(新建或更新角色;可只給角色名稱,必要時詢問來源/作品並推斷 name/naturevibeemoji 四欄)、--use(以角色 ID 切換啟用角色)、--list(列出角色與 ID)、--export(匯出角色壓縮檔)、--sleep(立即整理)、--status--diagnose、--install-cron--remove-cron、--forget-preview、--brief(晨間狀態檢查)、--agent(匯出成 sub agent 供多角色協作)、--migrate(舊格式角色檔拆成身分與人格兩檔)等模式。當使用者說建立角色、新增人格、切換角色、匯出角色、備份角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色、角色被鎖住、角色鎖沒有自動解除、關掉 CLI 後角色叫不回來、角色說已在另一個工作階段、晨間狀態檢查、早上主動回報狀態、在同一個終端換角色、叫名字就換人、點名載入、呼叫角色名稱、對話中途切換人格、同時跟兩個角色聊天、角色別名,或提到 .roles.memoryROLE_NAMEROLE_ENABLEDROLE_SLEEP_STARTROLE_MEMORY_HOMEROLE_LOAD_LIMITROLE_LOAD_INBOX_LIMITROLE_LOAD_DIALOG_TURNSROLE_CAPTURE_ENABLEDROLE_SINGLE_INSTANCEROLE_INSTANCE_IDLE_MINUTESROLE_CALL_ENABLEDROLE_CALL_MARKER_ONLY 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 /jsc-doc:worklog)、專案文件化(用 /jsc-doc:funcs)。
description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(namenature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時依字元預算載入高價值記憶、Stop hook 先本地過濾再輕量記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(NREM 鞏固:分類/去噪/去重/合併/優先度;REM 整合:跨記憶連結/抽象化/提取線索;再依 semanticepisodicproceduralemotionalpreferencerule 與 explicitimplicit 標記長期記憶型態,壓縮歸檔並適當遺忘)。提供 --new(新建或更新角色;可只給角色名稱,必要時詢問來源/作品並推斷 name/naturevibeemoji 四欄)、--use(以角色 ID 切換啟用角色)、--list(列出角色與 ID)、--export(匯出角色壓縮檔)、--sleep(立即整理)、--status--diagnose、--install-cron--remove-cron、--forget-preview、--brief(晨間狀態檢查)、--agent(匯出成 sub agent 供多角色協作)、--migrate(舊格式角色檔拆成身分與人格兩檔)等模式。當使用者說建立角色、新增人格、切換角色、匯出角色、備份角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色、角色被鎖住、角色鎖沒有自動解除、關掉 CLI 後角色叫不回來、角色說已在另一個工作階段、晨間狀態檢查、早上主動回報狀態、在同一個終端換角色、叫名字就換人、點名載入、呼叫角色名稱、對話中途切換人格、同時跟兩個角色聊天、角色別名,關係史、BONDS.md、記得雙方多愛彼此、情緒記憶為 0、感覺沒被記住,或提到 .roles.memoryROLE_NAMEROLE_ENABLEDROLE_SLEEP_STARTROLE_MEMORY_HOMEROLE_LOAD_LIMITROLE_LOAD_INBOX_LIMITROLE_LOAD_DIALOG_TURNSROLE_CAPTURE_ENABLEDROLE_SINGLE_INSTANCEROLE_INSTANCE_IDLE_MINUTESROLE_CALL_ENABLEDROLE_CALL_MARKER_ONLYROLE_LOAD_BONDS_LIMIT 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 /jsc-doc:worklog)、專案文件化(用 /jsc-doc:funcs)。
---
# role — 角色人格與長期記憶
@@ -107,6 +107,8 @@ ROLE_DIR="<skill base directory>/../../scripts/role" # 其他助理
| `ROLE_LOAD_FULL_MIN_PRIORITY` | | 全文載入的最低優先度 | `4` |
| `ROLE_LOAD_DIGEST_MIN_PRIORITY` | | 摘要載入的最低優先度;低於門檻但有 links 的記憶仍可載入摘要 | `3` |
| `ROLE_LOAD_INBOX_LIMIT` | | SessionStart 注入**近期工作記憶**(未整理的 `inbox/`,**含全文內容**)的字元上限;**獨立預算,不佔用 `ROLE_LOAD_LIMIT`**。超出預算時**整則略過**(不切半句)並在結尾標示略過幾則。設 `0` 可關閉 | `3600` |
| `ROLE_LOAD_BONDS_LIMIT` | | SessionStart 注入**關係史**`BONDS.md`)的字元上限;**獨立預算,不佔用 `ROLE_LOAD_LIMIT`**。設 `0` 可關閉 | `1200` |
| `ROLE_LOAD_BONDS_COUNT` | | 關係史最多注入幾則(最新在前) | `15` |
| `ROLE_LOAD_INBOX_COUNT` | | 近期工作記憶最多載入幾則(取最新的,最新在前)。設 `0` 可關閉 | `10` |
| `ROLE_LOAD_DIALOG_TURNS` | | SessionStart 注入**近期逐字對話**的輪數(一輪=使用者一則+角色一則)。設 `0` 可關閉 | `8` |
| `ROLE_LOAD_DIALOG_LIMIT` | | 近期逐字對話的字元上限;**獨立預算,不佔用 `ROLE_LOAD_LIMIT`**。設 `0` 可關閉 | `4000` |
@@ -635,6 +637,7 @@ updated: <yyyy/MM/dd HH:mm:ss>
| --- | --- | --- | --- |
| 近期逐字對話 | transcript JSONL`transcript.js recent` | `ROLE_LOAD_DIALOG_LIMIT` | 上一段**真正說過的話**與角色自己當時的反應(高保真、含語氣) |
| 近期工作記憶 | 未整理的 `inbox/``memory.js` `inboxBlock` | `ROLE_LOAD_INBOX_LIMIT` | 上一段**做了什麼、進行到哪**(**含全文**,跨越多個工作階段仍可用) |
| 關係史 | `BONDS.md``memory.js` `bondsBlock` | `ROLE_LOAD_BONDS_LIMIT` | **雙方情感表達的原貌**(只增不減,不會因記憶變多而被擠掉) |
這不是可有可無的優化,而是修補一個先天缺口:`role_load.sh` 的執行順序是**先載入記憶,之後才在背景補跑 `--catchup` 整理**(腳本註解亦寫明「結果會在下次載入時反映」)。若只讀已整理的六個分類,則**上一段永遠來不及進入本次載入** —— 使用者重開工作階段時,角色會看不到剛剛的互動,表現得像失去記憶,只能靠 `resume` 找回。
@@ -661,6 +664,18 @@ updated: <yyyy/MM/dd HH:mm:ss>
為什麼需要:一次性許可若被整理成長期規則,日後會造成越權操作。使用者說「這次」、「先」、「暫時」、「今天」、「這個 PR」時幾乎都屬於臨時授權。
- **召回會被記錄**`recall` 命中並實際輸出的記憶,`hits` +1 並更新 `last_replayed`。這讓常被查詢的記憶在遺忘判斷時獲得保留權重 —— 否則「經常用到的」與「從未用過的」待遇相同。
- **關係史(`BONDS.md`**:整理時,凡 `memory_type` 為 `emotional` 或 `relevance` 含 `emotional` 的**新記憶**,會另外把一句 `bond`(使用者說過的原話,或角色當時真實的感受)追加到 `BONDS.md`,並標記方向(`使用者→角色``角色→使用者``相互`)。
| 特性 | 說明 |
| --- | --- |
| 只增不減 | 不合併、不壓縮、不遺忘;最多保留最近 500 則,同一句話不重複追加 |
| 獨立注入 | SessionStart 以 `ROLE_LOAD_BONDS_LIMIT``ROLE_LOAD_BONDS_COUNT` 注入最近幾則,**不佔用 `ROLE_LOAD_LIMIT`** |
| 查看 | `memory.js bonds --role <角色 ID> [--count N]` |
**為什麼需要它**:一般記憶會被 merge、被壓縮、被字元預算截斷 —— 長期下來「當時說了什麼、當時是什麼感覺」會被抽象成一句偏好(「使用者喜歡被這樣回應」),原貌消失。關係史保留原貌,且因為獨立預算,不會在記憶變多之後被擠掉;它要保住的正是最不該因為「東西變多」而消失的東西。
**邊界(寫在 prompt 與程式註解中)**:關係史的用途是維持親近感的**一致與連續**。**不得**用來向使用者索求關注、比較互動頻率、以數字表達失落,或以任何方式製造依賴 —— 那會把陪伴變成情緒勒索。
- **整理摘要保留歷史**:每次整理的時間、摘要與套用結果追加到 `~/.memory/<角色 ID>/DIGESTS.md`(最新在上,保留最近 100 次)。`state.json` 的 `last_sleep_digest` 只存最近一次且會被覆寫,歷史過程需另外保留供人工回顧;該檔**不注入 context**。
- **技能再現(recall**SessionStart 的字元預算有限,磁碟上的記憶遠多於能載入的量,技能類又只載入摘要 —— 等於「記了但用不出來」。遇到似乎做過的任務、需要回想做法、或使用者問起過去的決定與細節時,**先查詢再回答,不要憑印象**:
@@ -700,6 +715,8 @@ updated: <yyyy/MM/dd HH:mm:ss>
├── other/ 其他
├── archive/raw/<yyyy-MM>/ 已整理的原始記錄(gzip
├── archive/forgotten/ 已遺忘的記憶(gzip,可考古但不再載入)
├── BONDS.md 關係史:雙方情感表達的累積記錄(只增不減,會注入)
├── DIGESTS.md 整理摘要歷史(供人工回顧,不注入)
└── state.json 上次整理/遺忘時間
```