Files
persona/README.md
T
2026-07-29 14:37:20 +00:00

256 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<br/>注入 情緒+短期記憶+命中的長期記憶+關係"]
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/<slug>/`,可用 `PERSONA_HOME` 覆寫)
```
<slug>/
├── IDENTITY.md # 身分卡:Name / Creature / Vibe / Emoji / AvatarOpenClaw 同欄位)
├── 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`
## HooksClaude Code
| Hook | 做什麼 |
| --- | --- |
| `SessionStart` | 清死鎖、接續人格、把 `PERSONA_SESSION=<session_id>` 與規則注入上下文 |
| `UserPromptSubmit` | 注入 `<persona-context>`:身分、情緒、短期記憶、關鍵詞命中的長期記憶、相關人際關係;並記原始逐字 |
| `PreToolUse` | **人格隔離與鎖驗證的唯一強制點**deny 帶原因) |
| `Stop` | 情緒隨時間衰減、續租、記錄回覆、短期記憶過多時提醒固化 |
| `SubagentStop` | 解除 guest sub agent 的 pin |
| `SessionEnd` | 釋放鎖與 guest 租約,人格才能被下一個程序載入 |
> `session_id` 只有 hook 拿得到 → 注入上下文 → skills 呼叫 CLI 時必須帶 `--session`
> hook 會驗證是否相符。**這是「一人格一程序」與「跨人格隔離」不能被繞過的關鍵**。
---
## Skills 目錄
<!-- JSC-SKILLS:START -->
### `persona-create`
建立人格:以 OpenClaw 相同的五個身分欄位與 SOUL 段落訪談使用者,初始化情緒基線、記憶、心智圖與關係圖,並立即取得載入鎖。
- **Claude Code / Antigravity**`/jsc-persona:persona-create` **Codex**`$persona-create`
### `persona-chat`
載入人格並對話:取得獨占鎖 → 每輪做語意分析 → 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆 → 寫回記憶。
- **Claude Code / Antigravity**`/jsc-persona:persona-chat <slug>` **Codex**`$persona-chat`
### `persona-invite`
邀請另一個人格透過 `persona-guest` sub agent 加入聊天室,形成多人格對話;結束後讓它離場並把見聞留在它自己的 inbox。
- **Claude Code / Antigravity**`/jsc-persona:persona-invite <slug>` **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`
<!-- JSC-SKILLS:END -->
### 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:<name>` | ✅ 完整 | ✅ `jsc-persona:persona-guest` |
| Codex | ✅ `$<name>` | ❌ | ⚠ 需自行以子任務模擬 |
| Antigravity | ✅ `/jsc-persona:<name>` | ❌ | ⚠ |
| 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 <url>` 目前只支援 github.comgitea 請 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。