feat: 心裡話與不重複發言、劇場模式同規則、人格匯出匯入
讓人格講話更像正常人,並讓人格可以搬家。
1. 心裡話(inner voice)
- 新增 `think` 子指令:推導/盤算寫進 state/inner.jsonl,
只印「💭 心想 N 句」,內容永不回顯給使用者。
- <persona-context> 每輪帶回最近三句,推論因此有連續性。
2. 短時間內不重複
- 說出口的話由 Stop hook 自動記進 state/said.jsonl。
- 新增 `said check|list`:事前確認這句是不是又要說一次。
- 相似度 = 字元 bigram Jaccard(0.4) + 字集合 Jaccard(0.6),
門檻 0.72;字集合權重較高才分得開「重排語序」(要擋)
與「換掉關鍵詞」(是新資訊,放行)。
3. 回話一到三句 + 劇場模式同樣適用
- 規則注入 turnContext 與 UserPromptSubmit hint。
- 劇場模式的 CLI 都帶 --quiet,警告會被丟掉,所以 `room post`
對近似重複與超過三句的發言直接拒收(--allow-repeat / --force 例外),
host 與 guest 走同一支 CLI,一視同仁。
4. 人格匯出匯入(新 skill persona-transfer)
- `export` / `import`:單一 JSON bundle(可 gzip)+ sha256 checksum,
無外部依賴。不帶載入鎖與 guest 租約,journal/ 需明確 --with-journal。
- 匯出只能匯出本 session 已載入的人格,否則等於跨人格讀取的後門;
bundle 內的 ../ 逃逸路徑一律拒收。
順帶修正:agents/persona-guest.md 的 CLI 範例都少了 --as-guest,
照著跑會被 requireMember 擋下,guest 根本無法認識自己或發言。
selftest 102 項全綠(新增 ⑪ 說話節制、⑫ 匯出匯入,含重複判定校準對照表)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
|
||||
|
||||
---
|
||||
|
||||
## 七條硬規則
|
||||
## 九條硬規則
|
||||
|
||||
| 規則 | 怎麼做到 |
|
||||
| --- | --- |
|
||||
@@ -22,6 +22,8 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
|
||||
| **5. 腳本用 Node.js** | `scripts/*.mjs`、`hooks/*.mjs`,只用 Node 內建模組(fs/path/os/crypto),無 npm 依賴 |
|
||||
| **6. 由使用者呼叫才載入並鎖定** | 人格不會自動附身:`SessionStart` hook 只列出可用人格,等使用者下 `/jsc-persona:persona-chat <slug>`;載入即取得獨占鎖並綁定該 session |
|
||||
| **7. 短期記憶轉入長期記憶有成文條件** | `R1`–`R6` 六條規則寫在程式裡(`promotionCandidates`),`candidates` 子指令會列出達標的候選與依據,hook 在達標時提醒固化 |
|
||||
| **8. 講話像人:推導藏起來、一到三句、不重複** | 推導寫進**心裡話** `think`(只回報「💭 心想 N 句」,永不回顯內容);說出口的話進 `said.jsonl`,下一輪注入「最近說過的話」提醒別重講;`room post` 直接**擋下**近似重複(字元 bigram+字集合相似度 ≥ 0.72)與超過三句的發言——劇場模式同樣適用 |
|
||||
| **9. 人格可以匯出匯入** | `export` 把身分/情緒/記憶/心智圖/關係圖打包成單一 JSON bundle(可 `--gzip`、附 sha256),`import` 還原或換名複製;**不帶**載入鎖與 guest 租約,`journal/` 要明確 `--with-journal` 才帶走 |
|
||||
|
||||
---
|
||||
|
||||
@@ -30,12 +32,13 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph P["主程序(一個 session = 一個人格)"]
|
||||
U["使用者訊息"] --> H1["UserPromptSubmit hook<br/>注入 情緒+短期記憶+命中的長期記憶+關係"]
|
||||
U["使用者訊息"] --> H1["UserPromptSubmit hook<br/>注入 情緒+短期記憶+命中的長期記憶+關係<br/>+心裡話+最近說過的話"]
|
||||
H1 --> A["語意分析:意圖/主題/實體/情感/需求"]
|
||||
A --> E["情緒評估 → 十二情緒 deltas"]
|
||||
E --> R["以人格語氣回覆"]
|
||||
A --> T["心裡話 think<br/>(推導只留在 inner.jsonl,不輸出)"]
|
||||
T --> E["情緒評估 → 十二情緒 deltas"]
|
||||
E --> R["以人格語氣回覆(1–3 句、不重複說過的話)"]
|
||||
R --> W["記憶回寫(短期)"]
|
||||
W --> H2["Stop hook:情緒衰減+續租+固化提醒"]
|
||||
W --> H2["Stop hook:情緒衰減+續租+記說過的話+固化提醒"]
|
||||
end
|
||||
subgraph G["Sub Agent(受邀人格,唯讀)"]
|
||||
GA["persona-guest"]
|
||||
@@ -63,6 +66,8 @@ flowchart TB
|
||||
│ ├── lock.json # 載入鎖(session_id + 心跳租約)
|
||||
│ ├── guests.json # guest 唯讀租約
|
||||
│ ├── emotion.json # 十二情緒 levels / baseline / 半衰期
|
||||
│ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口)
|
||||
│ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言)
|
||||
│ └── config.json
|
||||
├── memory/
|
||||
│ ├── short-term.jsonl # 短期記憶(語意分析後;上限 240 筆 / 14 天)
|
||||
@@ -105,6 +110,38 @@ flowchart TB
|
||||
沒命中任何規則的就讓它被裁掉——**遺忘是功能**。達標時 `Stop` 與 `remember` 都會提醒去跑
|
||||
`/jsc-persona:persona-memory`。
|
||||
|
||||
## 講話像人(心裡話 / 一到三句 / 不重複)
|
||||
|
||||
AI 最容易露餡的三件事:把推理過程講出來、一次講一大段、換句話說同一件事。三個對策:
|
||||
|
||||
| 機制 | 怎麼運作 |
|
||||
| --- | --- |
|
||||
| **心裡話** `think` | 語意分析、推論、盤算全寫進 `state/inner.jsonl`;這個指令**只印「💭 心想 N 句」**,內容永不回顯。下一輪 `<persona-context>` 會帶回最近三句,推論因此有連續性,但使用者只看得到狀態 |
|
||||
| **一到三句** | `<persona-context>` 每輪注入上限;`room post` 對超過三句的發言直接拒收(`--force` 例外) |
|
||||
| **不重複** | 說出口的話由 `Stop` hook 自動記進 `state/said.jsonl`;`said check` 可事前確認,`room post` 事中攔截。相似度=字元 bigram Jaccard(0.4)+字集合 Jaccard(0.6),≥ 0.72 視為同一句 |
|
||||
|
||||
字集合權重較高,是為了分開「重排語序」與「換掉關鍵詞」這兩種很像但意義完全不同的情況:
|
||||
|
||||
```
|
||||
「我等一下把報告寄給你」vs「等一下我會把報告寄給你」→ 0.779 擋下(用字幾乎相同=同一件事換句話說)
|
||||
「你今天看起來很累」 vs「你今天看起來很開心」 → 0.687 放行(換了關鍵詞=新資訊)
|
||||
```
|
||||
|
||||
視窗預設 120 分鐘、少於 8 個字的短附和(「嗯」「好啊」)不算重複。
|
||||
|
||||
## 匯出 / 匯入(人格搬家)
|
||||
|
||||
```bash
|
||||
node scripts/persona.mjs export --session <id> --out ~/backup/lumi.persona.json [--gzip] [--with-journal]
|
||||
node scripts/persona.mjs import --session <id> --file ~/backup/lumi.persona.json [--persona lumi-copy] [--load]
|
||||
```
|
||||
|
||||
- bundle = 單一 JSON(可 gzip)+ sha256 checksum,無外部工具依賴。
|
||||
- **只能匯出本 session 目前載入的人格**——否則就是跨人格外洩的後門(`guard` 會 deny)。
|
||||
- 不帶 `state/lock.json`/`guests.json`(鎖屬於那台機器的那個程序);`journal/` 預設不帶。
|
||||
- 換名匯入(`--persona <新 slug>`)可讓同一個人格並存兩份,`config.json` 會記下來歷。
|
||||
- bundle 內的 `../` 逃逸路徑一律拒收;checksum 不符要 `--force` 才吃。
|
||||
|
||||
## 劇場模式(多人格對話只顯示對話)
|
||||
|
||||
`invite` 成功即開啟(`leave` 沒有客人時自動關閉,也可 `room theater --on/--off` 手動切):
|
||||
@@ -113,6 +150,8 @@ flowchart TB
|
||||
不得出現指令、指令輸出、狀態、分析、旁白、摘要。
|
||||
- `Stop` hook 在劇場模式**完全不發系統訊息**(提醒會破壞畫面)。
|
||||
- CLI 提供 `--quiet`(成功時零輸出)與 `room script`(只有 `emoji 名字(情緒):內容` 的乾淨對話稿)。
|
||||
- 「講話像人」的三條規則在這裡一樣生效:每個人格每輪 **1–3 句**、推導走 `think`、
|
||||
近似重複的台詞被 `room post` 拒收(host 與 guest 走同一支 CLI,一視同仁)。
|
||||
|
||||
```
|
||||
🪼 Lumi(喜悅42/期待31):所以你真的一個人把那台舊鐘修好了?
|
||||
@@ -153,7 +192,7 @@ flowchart TB
|
||||
|
||||
### `persona-chat`
|
||||
|
||||
載入人格並對話:取得獨占鎖 → 每輪做語意分析 → 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆 → 寫回記憶。
|
||||
載入人格並對話:取得獨占鎖 → 每輪做語意分析(推導寫進心裡話,不說出口)→ 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆(1–3 句、不重複說過的話)→ 寫回記憶。
|
||||
|
||||
- **Claude Code / Antigravity**:`/jsc-persona:persona-chat <slug>` **Codex**:`$persona-chat`
|
||||
|
||||
@@ -163,6 +202,12 @@ flowchart TB
|
||||
|
||||
- **Claude Code / Antigravity**:`/jsc-persona:persona-invite <slug>` **Codex**:`$persona-invite`
|
||||
|
||||
### `persona-transfer`
|
||||
|
||||
人格搬家:把身分、十二情緒、短期/長期記憶、心智圖與關係圖打包成單一 bundle 檔(可 gzip、附 checksum),或從 bundle 還原/換名複製成新人格。
|
||||
|
||||
- **Claude Code / Antigravity**:`/jsc-persona:persona-transfer` **Codex**:`$persona-transfer`
|
||||
|
||||
### `persona-memory`
|
||||
|
||||
記憶固化:依 R1–R6 條件把短期記憶轉入長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。
|
||||
@@ -195,8 +240,11 @@ flowchart TB
|
||||
node scripts/persona.mjs --help
|
||||
node scripts/persona.mjs list
|
||||
node scripts/persona.mjs candidates --session <PERSONA_SESSION> # 看哪些短期記憶該固化
|
||||
node scripts/persona.mjs think --session <id> --text "<推導>" # 心裡話(只回報「心想 N 句」)
|
||||
node scripts/persona.mjs said check --session <id> --text "<話>" # 這句是不是又要說一次?
|
||||
node scripts/persona.mjs room script --session <id> --room <room> # 乾淨對話稿(劇場模式用)
|
||||
node scripts/selftest.mjs # 68 項驗證:鎖、隔離、情緒、固化條件、劇場模式、hooks
|
||||
node scripts/persona.mjs export --session <id> --out lumi.json # 人格搬家
|
||||
node scripts/selftest.mjs # 102 項驗證:鎖、隔離、情緒、固化條件、說話節制、劇場模式、匯出匯入、hooks
|
||||
```
|
||||
|
||||
檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`(CLI)、
|
||||
@@ -290,6 +338,10 @@ cp -r ~/plugins/persona/skills/* ~/.config/opencode/skills/
|
||||
- **guest 唯讀**:受邀人格不能在別人的 session 裡改自己的長期記憶(避免兩個程序同時寫),
|
||||
只能把見聞放進 `memory/inbox/`,等它自己被載入時消化。
|
||||
- **hook 是強制、SKILL 是引導**:SKILL.md 寫的規則模型可能忘記,hook 不會。
|
||||
- **「不要說出來」要靠設計而不是靠忍住**:所以心裡話有自己的指令與檔案,
|
||||
而且那個指令**印不出內容**——就算不小心把輸出貼上去,使用者也只看到「💭 心想 N 句」。
|
||||
- **重複用相似度擋、不用語意判斷**:字元層級的比對沒有模型成本、行為可預期,
|
||||
誤判時有 `--allow-repeat` 可救;漏判的代價(人格自我重複)比誤判高。
|
||||
|
||||
## 新增/修改 skill
|
||||
|
||||
|
||||
Reference in New Issue
Block a user