# jsc-persona — AI 人格化記憶聊天 一套讓 AI **成為某個人**、而不只是回答問題的 plugin。 以 **OpenClaw 相同的人格描述**(`IDENTITY.md` 五欄位 + `SOUL.md` 四段落)建立人格, 再用 **hook 強制**的人格鎖與跨人格隔離,把 **十二情緒**、**語意分析**、 **短期/長期記憶**、**心智圖/思維導圖**、**人際關係圖** 綁在一起。 可同時安裝於 **Claude Code、Codex、Antigravity、OpenCode**; skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有 Claude Code 支援**(見「跨助理支援度」)。 --- ## 四條硬規則 | 規則 | 怎麼做到 | | --- | --- | | **1. 用與 OpenClaw 相同的描述建立人格** | `persona-create` 逐項索取 `Name` / `Creature` / `Vibe` / `Emoji` / `Avatar`(連括號提示文字都照 OpenClaw 原文),`SOUL.md` 沿用 `Core Truths` / `Boundaries` / `Vibe` / `Continuity` 段落結構 | | **2. 同一個人格只能被一個程序載入(Sub Agent 不限)** | `state/lock.json` 以 **session_id** 為主鍵、15 分鐘心跳租約;同一 session 的 sub agent 沿用同一把鎖,跨 session 搶佔會被拒;租約過期才可接手(並強制回報) | | **3. 禁止跨人格讀取資料** | `PreToolUse` hook 對 Read/Write/Edit/Glob/Grep/Bash 做路徑判定(含 `../`、symlink、`$PERSONA_HOME` 繞路),非當前人格一律 deny;CLI 也驗 `--session` 防止冒用身分 | | **4. 可以邀請人格透過 Sub Agent 一起聊天** | `persona-invite` 建聊天室 + guest 唯讀租約,用 `persona-guest` sub agent 啟動對方;兩邊只能透過聊天室交換「說出口的話」,記憶與情緒互不可見 | --- ## 架構 ```mermaid flowchart TB subgraph P["主程序(一個 session = 一個人格)"] U["使用者訊息"] --> H1["UserPromptSubmit hook
注入 情緒+短期記憶+命中的長期記憶+關係"] H1 --> A["語意分析:意圖/主題/實體/情感/需求"] A --> E["情緒評估 → 十二情緒 deltas"] E --> R["以人格語氣回覆"] R --> W["記憶回寫(短期)"] W --> H2["Stop hook:情緒衰減+續租+固化提醒"] end subgraph G["Sub Agent(受邀人格,唯讀)"] GA["persona-guest"] end subgraph S["人格倉庫 ~/.claude/personas"] PA["alpha/|IDENTITY SOUL 記憶 情緒 心智圖 關係圖"] PB["beta/|…"] RM[".rooms/room/transcript.jsonl"] end P -->|"只能碰自己"| PA GA -->|"只能碰自己"| PB P <-->|"唯一合法交流管道"| RM GA <--> RM ``` ## 人格倉庫(預設 `~/.claude/personas//`,可用 `PERSONA_HOME` 覆寫) ``` / ├── IDENTITY.md # 身分卡:Name / Creature / Vibe / Emoji / Avatar(OpenClaw 同欄位) ├── SOUL.md # 靈魂:Core Truths / Boundaries / Vibe / Continuity + 情緒傾向 ├── AGENTS.md # 操作規則(與個性分離) ├── USER.md # 對使用者的畫像(事實/推測分開) ├── state/ │ ├── lock.json # 載入鎖(session_id + 心跳租約) │ ├── guests.json # guest 唯讀租約 │ ├── emotion.json # 十二情緒 levels / baseline / 半衰期 │ └── config.json ├── memory/ │ ├── short-term.jsonl # 短期記憶(語意分析後;上限 240 筆 / 14 天) │ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter) │ ├── INDEX.md # 長期記憶索引(自動產生) │ └── inbox/room-*.jsonl # 當 guest 時留下的見聞,待本體消化 ├── mindmap/ │ ├── semantic.mmd # 心智圖:概念的長期關聯(Mermaid mindmap) │ └── threads/*.mmd # 思維導圖:單一話題的推理鏈(Mermaid graph,短期) ├── relations/ │ ├── graph.json # 人際關係圖(親近度/信任度/連線) │ └── graph.mmd # Mermaid 呈現(自動產生) └── journal/YYYY-MM.jsonl # 原始逐字 + 情緒史(hook 自動寫) ``` ## 十二情緒 | 六正向 | 六負向 | | --- | --- | | 喜悅 `joy`、信任 `trust`、期待 `anticipation`、感激 `gratitude`、平靜 `serenity`、驚喜 `delight` | 憤怒 `anger`、悲傷 `sadness`、恐懼 `fear`、厭惡 `disgust`、羞愧 `shame`、焦慮 `anxiety` | - 每種 0–100,各有**不同半衰期**(驚喜 60 分最快、信任 720 分最慢),每輪自動朝 `baseline` 指數衰減。 - 由十二情緒推導 `valence`(正向/中性/負向)與 `arousal`(高張/平穩/低張),決定語氣與句長。 - **主導情緒**取「超出基線最多」的前三名,所以個性底色不會永遠霸榜。 - 觸發規則與事件→delta 對照表:`skills/persona-chat/reference/emotions.md`。 ## Hooks(Claude Code) | Hook | 做什麼 | | --- | --- | | `SessionStart` | 清死鎖、接續人格、把 `PERSONA_SESSION=` 與規則注入上下文 | | `UserPromptSubmit` | 注入 ``:身分、情緒、短期記憶、關鍵詞命中的長期記憶、相關人際關係;並記原始逐字 | | `PreToolUse` | **人格隔離與鎖驗證的唯一強制點**(deny 帶原因) | | `Stop` | 情緒隨時間衰減、續租、記錄回覆、短期記憶過多時提醒固化 | | `SubagentStop` | 解除 guest sub agent 的 pin | | `SessionEnd` | 釋放鎖與 guest 租約,人格才能被下一個程序載入 | > `session_id` 只有 hook 拿得到 → 注入上下文 → skills 呼叫 CLI 時必須帶 `--session`, > hook 會驗證是否相符。**這是「一人格一程序」與「跨人格隔離」不能被繞過的關鍵**。 --- ## Skills 目錄 ### `persona-create` 建立人格:以 OpenClaw 相同的五個身分欄位與 SOUL 段落訪談使用者,初始化情緒基線、記憶、心智圖與關係圖,並立即取得載入鎖。 - **Claude Code / Antigravity**:`/jsc-persona:persona-create` **Codex**:`$persona-create` ### `persona-chat` 載入人格並對話:取得獨占鎖 → 每輪做語意分析 → 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆 → 寫回記憶。 - **Claude Code / Antigravity**:`/jsc-persona:persona-chat ` **Codex**:`$persona-chat` ### `persona-invite` 邀請另一個人格透過 `persona-guest` sub agent 加入聊天室,形成多人格對話;結束後讓它離場並把見聞留在它自己的 inbox。 - **Claude Code / Antigravity**:`/jsc-persona:persona-invite ` **Codex**:`$persona-invite` ### `persona-memory` 記憶固化:短期 → 長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。 - **Claude Code / Antigravity**:`/jsc-persona:persona-memory` **Codex**:`$persona-memory` ### `persona-relation` 人際關係圖維護:節點/連線、親近度與信任度調整、輸出 Mermaid 關係圖。 - **Claude Code / Antigravity**:`/jsc-persona:persona-relation` **Codex**:`$persona-relation` ### `persona-status` 載入狀態與鎖管理:誰被哪個程序鎖住、guest 租約、釋放、接手死鎖、清理殘留。 - **Claude Code / Antigravity**:`/jsc-persona:persona-status` **Codex**:`$persona-status` ### Agents - `persona-guest`(`agents/persona-guest.md`)— 受邀人格的 sub agent,唯讀、被綁死在自己的人格目錄。 ### CLI 與自我測試 所有狀態變更都經過 `scripts/persona.py`(純標準庫,無外部依賴): ```bash python3 scripts/persona.py --help python3 scripts/persona.py list python3 scripts/selftest.py # 51 項驗證:鎖、隔離、情緒、記憶、聊天室、hooks ``` --- ## 跨助理支援度 | 助理 | skills | hooks(鎖/隔離強制) | 受邀人格 sub agent | | --- | --- | --- | --- | | Claude Code | ✅ `/jsc-persona:` | ✅ 完整 | ✅ `jsc-persona:persona-guest` | | Codex | ✅ `$` | ❌ | ⚠ 需自行以子任務模擬 | | Antigravity | ✅ `/jsc-persona:` | ❌ | ⚠ | | OpenCode | ✅ 依描述自動觸發 | ❌ | ⚠ | > 沒有 hook 的助理仍會遵守 CLI 層的檢查(`--session` 綁定、`require_owner`/`require_member`、 > guest 唯讀),但那是**自律**而非強制:真正的 deny 只有 Claude Code 的 `PreToolUse` 做得到。 --- ## 安裝 / 更新 / 移除 > 指令中的 repo 網址換成你的:`https://gitea.jsc.idv.tw/plugins/persona.git` > **Claude / Codex 從 git URL 安裝(會 clone 遠端),請先把本 repo `push` 到 gitea。** ### Claude Code ```bash claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/persona.git claude plugin install jsc-persona@jsc-plugins # 更新 claude plugin marketplace update jsc-plugins claude plugin update jsc-persona@jsc-plugins # 移除 claude plugin uninstall jsc-persona@jsc-plugins ``` - 工作階段內 slash 版(等價):把 `claude plugin` 換成 `/plugin`。 - 本機開發(免 push):`claude plugin marketplace add /home/coder/plugins/persona` 後再 install。 - 安裝後**重啟工作階段**讓 hooks 生效;用 `/hooks` 確認六個 hook 都在。 ### Codex ```bash codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/persona.git codex plugin add jsc-persona@jsc-plugins codex plugin marketplace upgrade jsc-plugins # 更新 codex plugin remove jsc-persona@jsc-plugins # 移除 ``` ### Antigravity(`agy`) > `agy plugin install ` 目前只支援 github.com;gitea 請 clone 後用本地路徑。 ```bash git clone https://gitea.jsc.idv.tw/plugins/persona.git ~/plugins/persona agy plugin install ~/plugins/persona # 更新:git -C ~/plugins/persona pull && agy plugin uninstall jsc-persona && agy plugin install ~/plugins/persona ``` ### OpenCode ```bash git clone https://gitea.jsc.idv.tw/plugins/persona.git ~/plugins/persona mkdir -p ~/.config/opencode/skills cp -r ~/plugins/persona/skills/* ~/.config/opencode/skills/ ``` > **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`~` → `$HOME`。 ### headless 一次性執行 | 助理 | 指令 | | --- | --- | | Claude Code | `claude -p "/jsc-persona:persona-chat lumi"` | | Codex | `codex exec '$persona-chat lumi'` | | Antigravity | `agy -p "/jsc-persona:persona-chat lumi"` | | OpenCode | `opencode run "用 lumi 這個人格跟我聊聊"` | --- ## 設計取捨(讀之前先知道) - **記憶是被策展的,不是全存**:逐字稿進 `journal/`,但只有經過語意分析、有顯著度的內容才進短期記憶, 再由 `persona-memory` 決定什麼值得成為長期記憶。**遺忘是功能**。 - **事實與推測分離**:推論走思維導圖(`mindmap/threads/`),驗證後才升格長期記憶。 - **鎖的擁有者是 session 不是 process**:CLI 跑完就結束,所以租約判定只看心跳。 - **guest 唯讀**:受邀人格不能在別人的 session 裡改自己的長期記憶(避免兩個程序同時寫), 只能把見聞放進 `memory/inbox/`,等它自己被載入時消化。 - **hook 是強制、SKILL 是引導**:SKILL.md 寫的規則模型可能忘記,hook 不會。 ## 新增/修改 skill 1. 複製一個現有 skill 目錄,改 `SKILL.md` 的 `name` 與 `description`(描述要寫清楚何時用、何時不用)。 2. 需要新的狀態操作 → 加到 `scripts/persona.py` 的子指令,並在 `scripts/selftest.py` 補測試。 3. 動到隔離規則 → 一定要在 `selftest.py` 的第 ③/⑦ 區加對應案例,跑到全綠。 4. 把 skill 補進上方「Skills 目錄」區塊。 5. bump `.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、`plugin.json` 三處 `version`,commit 後 push。