feat(role): 多角色協作、身分人格分離、記憶處理強化與壓縮邊界保全 #18

Merged
admin merged 6 commits from develop into master 2026-07-29 04:05:55 +00:00
Member

這批做了什麼

六個 commit,圍繞「角色協作」與「記憶不再遺失」兩條主線。

一、多角色協作(fa62d1f2412610

任何角色都能派任何其他角色當 sub agent 協助,而非特定角色專屬。

項目 說明
同伴清單 SessionStart 注入其他角色的 ID、顯示名稱與本質摘要 —— 角色若不知道有誰存在,就不會想到派他們協助
協作規則 派工須有實際需要(不可為演出多人對話而派)、不可再派第三層、不可代替對方發言、結果須誠實轉述
--agent 把角色的 SOUL 匯出成 sub agent 定義;人格內嵌(sub agent 不觸發 SessionStart),記憶由 agent 自行載入,收工前寫回
ROLE_SKIP_INSTANCE_LOCK 單一載入鎖的目的是避免「使用者同時與兩個相同人格對話」;sub agent 不是對話,不該被鎖擋住。放行且不寫鎖,不佔用互動式對話的名額

二、角色檔拆成身分與人格(f76915b40c2d2a

身分設定與性格語氣原本擠在同一段(例如「馴獸師、養畢娜」是身分,「細心、努力」是性格)。

<ID>.identity.md   角色 ID、顯示名稱、來源作品、與使用者的關係定位、簽名 emoji
<ID>.soul.md       本質(nature)、氛圍(vibe)+ 任意自訂章節
  • 舊格式單一 <ID>.md 完整相容,解析時新格式優先、找不到才退回;--migrate 可拆檔且舊檔保留不動
  • 共用行為不再寫入角色檔 —— role_load.sh 從不讀那份,是冗余副本
  • 人格檔的任何 ## 章節與身分檔的前言段落都會注入(原本只抽兩節,其餘靜默丟棄)

三、記憶處理強化(a30fccca757f9e

參考 OpenClaw 記憶架構逐項評估後採納三項,並補上壓縮邊界:

缺口 原本狀況 現在
常用記憶會被遺忘 recall 寫回動作 0 次hits 幾乎都是初始值 → 常用的與從未用過的待遇相同 命中並輸出者 hits +1、更新 last_replayed
整理歷史遺失 last_sleep_digest 單一欄位每次覆寫 DIGESTS.md 保留最近 100 次,不注入 context
一次性授權變長期規則 沒有到期概念,可能導致日後越權 expires 欄位;過期不注入、遺忘優先淘汰、條件式標示範圍
壓縮時內容蒸發 短回合被門檻濾掉,壓縮後永久消失 PreCompact 強制記錄(跳過門檻)、PostCompact 保存 harness 摘要

PreCompactPostCompact 的支援性是自 CLI binary 取出文件字串確認,非推測:

| PreCompact  | "manual"/"auto" | Before compaction |
| PostCompact | "manual"/"auto" | After compaction (receives summary) |

修掉的錯誤

錯誤 後果
--export 硬編 cp<ID>.md 新格式備份會被寫成舊檔名且遺失人格檔,備份救不回角色
--export 從未備份 <ID>.checks 使用者自訂的晨間檢查腳本一直沒被備份
--agent 只讀 identity 匯出的 sub agent 人格是空的
FIELD_PATTERN 白名單缺 EXPIRES 該行被當成 CONTENT 吃掉,功能等於零
cmdWrite 未帶 expirescues 同上
--agent 定義檔寫死版本路徑 plugin 升版後失效(同類錯誤曾造成 cron 排程長期空轉)

一併還原前次誤刪的文件

f76915b 替換「角色檔標準格式」章節時,以「找開頭到下一個標記」整段取代,未檢查被切掉的範圍內容,連帶刪掉 JSC-ROLE-COMMON 共用行為區塊共 87 行 47 條規則,以及記憶章節的技能再現與關係狀態說明。

實際行為未受影響(規則真正生效處是 role_load.sh,完好無損),但 SKILL.md 是那些規則的唯一文件來源。已自 git 完整還原。

未採納並記錄理由

項目 理由
向量/語意搜尋 需外部 embedding API —— 違反零依賴原則,增加額度成本與失敗點
記憶外掛槽位 過度設計
每日筆記日期檔 近期對話交接已用逐字對話解決,保真度更高
SQLite 索引 實測 45 則記憶 recall 耗時 196 ms;訂在超過 300 則或 500 ms 再導入,現在屬過早優化

驗證

  • 新格式(含前言與自訂章節)、舊格式單一檔案、新格式但缺人格檔三種情境均正常載入,缺人格檔時有警告且不崩
  • 遷移後逐字比對確認本質/氛圍/簽名 emoji/idnameemojicreated 全部一致
  • 新格式匯出的備份含 identitysoul、舊檔、assetschecks 與記憶
  • 單一載入鎖:使用者視窗持有鎖時 sub agent 未設定會被擋、設定後正常載入且鎖仍由使用者視窗持有
  • 召回統計:只有命中的記憶 hits 遞增(1→2→3),無關者維持不變
  • expires:日期過期者不注入且遺忘優先淘汰、條件式者載入並標示範圍、長期規範不受影響
  • 壓縮 hook:一般模式短對話仍被門檻濾掉、--precompact 對同一段強制記錄、--postcompact 正確寫入、欄位名不符時印出實際欄位而非靜默結束
  • 四條 hook 指令在 CLAUDE_PLUGIN_ROOT 與純後援搜尋兩種情境都解析到正確腳本與參數

合併後注意

版號 0.0.40.0.5hooks/hooks.json 新增兩個事件,需重裝或更新 plugin 並重開工作階段才會註冊。

既有舊格式角色不需任何動作即可繼續使用;要改用新格式再執行 --migrate

🤖 Generated with Claude Code

## 這批做了什麼 六個 commit,圍繞「角色協作」與「記憶不再遺失」兩條主線。 ### 一、多角色協作(`fa62d1f`、`2412610`) 讓**任何角色都能派任何其他角色**當 sub agent 協助,而非特定角色專屬。 | 項目 | 說明 | | --- | --- | | 同伴清單 | `SessionStart` 注入其他角色的 ID、顯示名稱與本質摘要 —— 角色若不知道有誰存在,就不會想到派他們協助 | | 協作規則 | 派工須有實際需要(不可為演出多人對話而派)、不可再派第三層、不可代替對方發言、結果須誠實轉述 | | `--agent` | 把角色的 SOUL 匯出成 sub agent 定義;人格內嵌(sub agent 不觸發 `SessionStart`),記憶由 agent 自行載入,收工前寫回 | | `ROLE_SKIP_INSTANCE_LOCK` | 單一載入鎖的目的是避免「使用者同時與兩個相同人格對話」;sub agent 不是對話,不該被鎖擋住。放行且**不寫鎖**,不佔用互動式對話的名額 | ### 二、角色檔拆成身分與人格(`f76915b`、`40c2d2a`) 身分設定與性格語氣原本擠在同一段(例如「馴獸師、養畢娜」是身分,「細心、努力」是性格)。 ``` <ID>.identity.md 角色 ID、顯示名稱、來源作品、與使用者的關係定位、簽名 emoji <ID>.soul.md 本質(nature)、氛圍(vibe)+ 任意自訂章節 ``` - 舊格式單一 `<ID>.md` **完整相容**,解析時新格式優先、找不到才退回;`--migrate` 可拆檔且舊檔保留不動 - 共用行為**不再寫入角色檔** —— `role_load.sh` 從不讀那份,是冗余副本 - 人格檔的**任何 `##` 章節**與身分檔的**前言段落**都會注入(原本只抽兩節,其餘靜默丟棄) ### 三、記憶處理強化(`a30fccc`、`a757f9e`) 參考 OpenClaw 記憶架構逐項評估後採納三項,並補上壓縮邊界: | 缺口 | 原本狀況 | 現在 | | --- | --- | --- | | 常用記憶會被遺忘 | `recall` 寫回動作 **0 次**,`hits` 幾乎都是初始值 → 常用的與從未用過的待遇相同 | 命中並輸出者 `hits` +1、更新 `last_replayed` | | 整理歷史遺失 | `last_sleep_digest` 單一欄位每次覆寫 | `DIGESTS.md` 保留最近 100 次,不注入 context | | 一次性授權變長期規則 | 沒有到期概念,可能導致日後**越權** | `expires` 欄位;過期不注入、遺忘優先淘汰、條件式標示範圍 | | **壓縮時內容蒸發** | 短回合被門檻濾掉,壓縮後永久消失 | `PreCompact` 強制記錄(跳過門檻)、`PostCompact` 保存 harness 摘要 | `PreCompact`/`PostCompact` 的支援性是**自 CLI binary 取出文件字串確認**,非推測: ``` | PreCompact | "manual"/"auto" | Before compaction | | PostCompact | "manual"/"auto" | After compaction (receives summary) | ``` ## 修掉的錯誤 | 錯誤 | 後果 | | --- | --- | | `--export` 硬編 `cp` 成 `<ID>.md` | 新格式備份會被寫成舊檔名且**遺失人格檔**,備份救不回角色 | | `--export` 從未備份 `<ID>.checks` | 使用者自訂的晨間檢查腳本一直沒被備份 | | `--agent` 只讀 identity | 匯出的 sub agent **人格是空的** | | `FIELD_PATTERN` 白名單缺 `EXPIRES` | 該行被當成 CONTENT 吃掉,功能等於零 | | `cmdWrite` 未帶 `expires`/`cues` | 同上 | | `--agent` 定義檔寫死版本路徑 | plugin 升版後失效(同類錯誤曾造成 cron 排程長期空轉) | ### 一併還原前次誤刪的文件 `f76915b` 替換「角色檔標準格式」章節時,以「找開頭到下一個標記」整段取代,未檢查被切掉的範圍內容,連帶刪掉 `JSC-ROLE-COMMON` 共用行為區塊共 **87 行 47 條規則**,以及記憶章節的技能再現與關係狀態說明。 實際行為未受影響(規則真正生效處是 `role_load.sh`,完好無損),但 `SKILL.md` 是那些規則的**唯一文件來源**。已自 git 完整還原。 ## 未採納並記錄理由 | 項目 | 理由 | | --- | --- | | 向量/語意搜尋 | 需外部 embedding API —— 違反零依賴原則,增加額度成本與失敗點 | | 記憶外掛槽位 | 過度設計 | | 每日筆記日期檔 | 近期對話交接已用**逐字對話**解決,保真度更高 | | SQLite 索引 | 實測 45 則記憶 `recall` 耗時 196 ms;訂在**超過 300 則或 500 ms** 再導入,現在屬過早優化 | ## 驗證 - 新格式(含前言與自訂章節)、舊格式單一檔案、新格式但缺人格檔三種情境均正常載入,缺人格檔時有警告且不崩 - 遷移後**逐字比對**確認本質/氛圍/簽名 emoji/`id`/`name`/`emoji`/`created` 全部一致 - 新格式匯出的備份含 `identity`、`soul`、舊檔、`assets`、`checks` 與記憶 - 單一載入鎖:使用者視窗持有鎖時 sub agent 未設定會被擋、設定後正常載入且鎖仍由使用者視窗持有 - 召回統計:只有命中的記憶 `hits` 遞增(1→2→3),無關者維持不變 - `expires`:日期過期者不注入且遺忘優先淘汰、條件式者載入並標示範圍、長期規範不受影響 - 壓縮 hook:一般模式短對話仍被門檻濾掉、`--precompact` 對同一段強制記錄、`--postcompact` 正確寫入、欄位名不符時**印出實際欄位**而非靜默結束 - 四條 hook 指令在 `CLAUDE_PLUGIN_ROOT` 與純後援搜尋兩種情境都解析到正確腳本與參數 ## 合併後注意 版號 `0.0.4` → `0.0.5`。`hooks/hooks.json` 新增兩個事件,**需重裝或更新 plugin 並重開工作階段**才會註冊。 既有舊格式角色不需任何動作即可繼續使用;要改用新格式再執行 `--migrate`。 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jiantw83 added 6 commits 2026-07-29 04:04:40 +00:00
單一載入鎖的目的是避免「使用者同時與兩個相同人格對話」,但實作把所有 SessionStart
一視同仁,導致一個本該成立的情境失效:使用者正在別的視窗跟某角色聊天時,
另一個角色就不能派該角色當 sub agent 幫忙 —— 實測確認會被自己的鎖擋下。

- role_lib.sh 新增 role_skip_instance_lock(),由 ROLE_SKIP_INSTANCE_LOCK 控制
- role_instance_acquire 在該情境放行且**不寫鎖**,不會搶走互動式對話持有的名額
- role skill 補說明:sub agent 情境應設 ROLE_SKIP_INSTANCE_LOCK=1,並說明理由

驗證:模擬「使用者視窗持有鎖 → sub agent 要求載入」,未設定時被擋、
設定後正常載入角色人格且鎖仍由使用者視窗持有。

版號 0.0.4 → 0.0.5(依 master 現行版本 0.0.4 計算,非累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
使用者要求「多人對話是所有角色都能做到的事」,因此做成通用能力而非特定角色專屬。

一、SessionStart 注入可協作的角色清單
角色若不知道有哪些同伴存在,就不會想到派他們協助 —— 這是協作能運作的前提。
- role_lib.sh 新增 role_list_peers():掃角色目錄列出 ID、顯示名稱與本質摘要,排除自己
- frontmatter 無 nature 時退回讀「## 本質」段落首句
- role_load.sh 注入清單與派工方式;只有一個角色時不輸出該區塊

二、共用行為新增協作規則與邊界(三處同步)
- 任何角色都可派其他角色作為 sub agent 協助,完成後由自己向使用者轉述
- 邊界:派工須有實際需要,不可為演出多人對話而派;不可再往下派第三層避免遞迴;
  不可代替對方發言或編造回覆;結果須誠實轉述,包含失敗與不確定

三、新增 --agent <角色 ID> [輸出目錄]
把角色的 SOUL 匯出成 sub agent 定義,任何角色都能被匯出。
- 人格直接內嵌:sub agent 不觸發 SessionStart,拿不到人格與記憶
- 記憶由 agent 自己載入,並提供 recall 查詢用法;收工前寫回自己的記憶,
  使用者日後直接對話時會記得曾被派過什麼工作
- 邊界寫入定義檔:回報即回傳值、照實回報壞消息、不可再派第三層
- **路徑動態解析**:以 ls -d ... | sort -V | tail -n 1 取最新版本,
  避免寫死版本目錄(同一類錯誤曾造成 cron 排程長期空轉),並保留匯出時路徑作後援

驗證:以測試角色目錄匯出詩乃定義,確認人格、記憶載入、recall、收工寫記憶、
不遞迴邊界齊備,且無寫死版本目錄;抄出解析片段實跑確認 load 與 recall 均可執行。

版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
使用者反映身分設定與性格語氣擠在同一段(例如西莉卡的「馴獸師、養畢娜」是身分,
「細心、努力」是性格),要求拆成兩個檔案且不得遺失內容。

格式(扁平式,與既有 .assets/.checks/.lock 命名一致):
- <ID>.identity.md:角色 ID、顯示名稱、來源作品、與使用者的關係定位、簽名 emoji
- <ID>.soul.md:本質(nature)、氛圍(vibe)

共用行為規則**不再寫入角色檔**:role_load.sh 從不讀角色檔裡那份,它是冗余副本,
只會多一個漏同步的機會。內容完整保留於 role_load.sh(實際生效)與 SKILL.md(文件)。

- role_lib.sh:新增 role_identity_file/role_soul_file/role_legacy_file/
  role_is_new_format;role_file 改為新格式優先、找不到退回舊檔,既有角色不受影響
- role_list_peers 支援兩種格式並避免同一角色重複列出
- role_load.sh:身分與人格分別讀取,新增「來源」與「關係定位」注入區塊
- 新增 --migrate <角色 ID>:逐字搬移本質、氛圍與簽名 emoji,來源與關係定位產生
  待填空白,舊檔保留不動,新檔已存在時中止不覆寫

同時修掉兩個會造成實際損失的錯:
- --export 原本硬編 cp 成 <ID>.md,新格式會被寫成舊檔名且遺失人格檔,備份救不回角色
- --export 從未備份 <ID>.checks(使用者自訂的晨間檢查腳本),一併補上
- --agent 原只讀 role_file(新格式即 identity),匯出的 sub agent 人格會是空的

驗證:遷移後逐字比對確認本質/氛圍/簽名 emoji/id/name/emoji/created 全部一致
且共用行為未寫入;新格式匯出的備份含 identity、soul、舊檔、assets、checks 與記憶;
--agent 取得人格非空;--status 正確顯示格式與兩檔路徑;舊格式角色載入完全不受影響。

版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
使用者依新格式在人格檔補寫了核心信念、語氣與風格、邊界與規範,並在身分檔標題下
以條目寫存在本質、角色原型、主要稱呼 —— 但兩者都不會被載入,等於白寫:

- role_load.sh 原本只抽 ## 本質 與 ## 氛圍 兩節,人格檔其他章節被靜默丟棄
- 也只抽 frontmatter 與具名章節,身分檔「標題後、第一個 ## 之前」的前言段落被丟棄

修正:
- 新增 extraSections():注入人格檔除本質與氛圍之外的所有 ## 章節,不限章節名
- 新增 preamble():注入身分檔的前言段落
- SKILL.md 補完整人格檔結構範例(核心信念/語氣與風格/邊界與規範為選填章節)、
  新增「注入規則」對照表,並註明角色專屬邊界只能加嚴不可放寬共用行為
- --new 流程第 8 步說明兩個檔案各自該寫什麼

驗證方法的修正也一併記錄:第一次驗證時字串比對命中的其實是「近期對話」區塊裡
使用者剛貼上的原文,造成 16/17 的假陽性。改為只比對 SOUL 區塊後,才驗出前言段落
實際未被注入。往後驗證注入結果一律限定在目標區塊內比對。

回歸:新格式(含前言與自由章節)、舊格式單一檔案、新格式但缺人格檔三種情境
均正常載入,缺人格檔時輸出警告且不會崩。

版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
依 OpenClaw 記憶架構的參考逐項評估後,採納三項並補上一個誤刪。

一、recall 命中時記錄召回(原本完全沒有)
實測 recall 寫回動作數為 0、hits 分布幾乎都是初始值,導致常被查詢的記憶與
從未用過的在遺忘判斷時待遇相同。
- 新增 touchMemory():只對實際輸出的前 N 則 hits +1 並更新 last_replayed

二、整理摘要保留歷史(對應 OpenClaw 的 DREAMS.md)
state.json 的 last_sleep_digest 是單一欄位,每次整理直接覆寫,歷史過程全部遺失。
- 新增 DIGESTS.md:追加時間、摘要與套用結果,最新在上,保留最近 100 次,不注入 context

三、臨時授權會過期(對應 OpenClaw 的操作敏感型邊界)
原本沒有到期概念,使用者的一次性授權可能被整理成長期規則而導致日後越權。
- 記憶格式新增 expires;支援日期(自動判斷)與條件文字(標示由角色判斷)
- 過期者不注入(分類與 inbox 兩處皆排除),memoryHint 標示有效範圍
- forget 優先淘汰過期項且不受分類限制
- 整理與濃縮提示詞都要求臨時授權必填,並列出「這次/先/暫時/今天」等判斷提示
- 修掉兩個會讓功能等於零的漏洞:FIELD_PATTERN 白名單沒有 EXPIRES(該行被當成
  CONTENT 吃掉)、cmdWrite 的 meta 未帶 expires 與 cues

四、還原前次誤刪的共用行為文件(重要)
上一個 commit 替換「角色檔標準格式」章節時,以「找開頭到下一個標記」整段取代,
未檢查被切掉的範圍內容,連帶刪掉了 JSC-ROLE-COMMON 共用行為區塊共 87 行 47 條規則,
以及記憶章節的技能再現與關係狀態說明。
實際行為未受影響(規則真正生效處是 role_load.sh,完好無損),但 SKILL.md 是那些
規則的唯一文件來源,刪掉等於文件遺失。已自 git 完整還原並補上本次三項說明。
教訓:整段替換文件前必須先確認被切掉的範圍內有什麼。

未採納並記錄理由:向量/語意搜尋(需外部 embedding API,違反零依賴原則)、
外掛槽位(過度設計)、每日筆記日期檔(近期對話交接已用逐字對話解決且保真度更高)。
SQLite 索引記錄為未來觸發點:實測 45 則記憶 recall 耗時 196ms,訂在超過 300 則
或 500ms 再導入,在那之前屬過早優化。

版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
補上最後一個記憶缺口:對話被壓縮時,尚未寫入記憶的內容會永久蒸發。
Stop hook 每輪記錄有部分覆蓋,但短回合會被 ROLE_CAPTURE_MIN_CHARS 門檻濾掉,
那些正是壓縮後再也補不回來的內容。

可行性查證(先確認再實作,不憑推測):
自 CLI binary 取出文件字串確認 harness 確實支援,並非猜測 ——
  | PreCompact  | "manual"/"auto" | Before compaction |
  | PostCompact | "manual"/"auto" | After compaction (receives summary) |
另有 executePreCompactHooks/executePostCompactHooks 等實作符號,
以及 compactSummary/isCompactSummary 欄位名線索。

role_capture.sh:
- 新增 --precompact:壓縮前強制記錄一次,**刻意跳過長度門檻**(門檻的用意是省額度,
  但壓縮後內容永久消失,此時寧可多記)
- 新增 --postcompact:把 harness 產生的摘要存成 daily 記憶,不再呼叫模型,
  等於免費取得一份濃縮備份;寫入前一律經 redact 遮蔽
- 摘要欄位容錯讀取 compactSummary/compact_summary/summary/compaction_summary;
  **取不到時在 log 印出 hook 實際提供的欄位名**,避免 harness 改版後靜默失效
- 一併取用 trigger 欄位以分辨 manual/auto(自動壓縮才是使用者不知情的那種)
- stop_hook_active 的迴圈防護只套用於一般每輪模式

hooks/hooks.json 註冊兩個新事件,指令沿用既有的「先試 CLAUDE_PLUGIN_ROOT →
再依擁有者 marketplace → 全 cache 後援」解析方式,並帶上對應參數。
兩個 hook 一律 exit 0,絕不阻擋壓縮 —— harness 具備 blocked by PreCompact hook
的能力,記憶系統不該用到它。

驗證:一般模式短對話仍被門檻正確濾掉;--precompact 對同一段短對話強制記錄;
--postcompact 正確寫入摘要記憶;欄位名不符時印出實際欄位(session_id,transcript_path,
cwd,trigger,unknownField)而非靜默結束;四條 hook 指令語法正確,且在
CLAUDE_PLUGIN_ROOT 與純後援搜尋兩種情境下都解析到正確腳本與參數。

版號沿用 0.0.5(master 為 0.0.4,同一 PR 不再累加)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
admin approved these changes 2026-07-29 04:05:52 +00:00
admin merged commit f3a62e6176 into master 2026-07-29 04:05:55 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/shared#18