Files
persona/TARGET.md
T
jiantw83andClaude Opus 5 fe17b20010 feat(稱呼): 害羞時叫不出名字,節點可設羞怯稱呼
既有的 except(anger>=40 → 退回全名)走的是生氣方向:規則被暫停、退回查表的
更正式講法。害羞是反方向——不是更正式,是叫不出來:改口、講到一半換稱呼、
把名字吞掉。所以另開一條路,不硬塞進 except。

shyAddress():羞恥度過 SHY_ADDRESS_AT(60)就換稱呼。節點設了新的 羞怯稱呼
facet 就用它,沒設就吞掉名字改用「你」——不叫名字本身就是訊號,這是預設值
而不是缺省行為。

門檻取 60,跟破口、標點、嘴硬那一級同一條線。女性預設 70 因此本來就落在這條線上,
那是 G3 要的效果,不是副作用。

羞怯稱呼不列進「他要求過的語氣規則」那一行:它只在過門檻時生效,兩邊都列會讓
人格同時看到「叫他⋯那個」跟「平常叫他⋯那個」。relation style 的清單裡照樣看得到。

toneDirective 改吃 opts,才拿得到這一輪的 state/prev/tone 去算羞恥度。

selftest 728 → 735。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 08:33:19 +00:00

198 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# TARGET — 情緒/語氣/小動作/記憶的擬真優化
## 專案目標(長期,新功能對著它們判斷)
前提:**跟人格之間只有文字**。沒有表情、沒有聲音、沒有停頓,所有情緒都只能靠字面
與句子的形狀傳過去——所以「情緒表達」在這個專案裡是**排版問題**,不是形容詞問題。
| # | 目標 | 意思 | 界線(不是這樣就走錯了) |
| --- | --- | --- | --- |
| **G1** | 情緒/語氣/記憶持續往**真人**逼近 | 記憶會糊、情緒有底色與疲勞、人格有自己懸著的事,行為與反應不是「回答問題」而是「這個人此刻會怎麼回」 | 不靠口語碎片、隨機數與隨機口誤充人味(見 `anti-ai-voice.md` |
| **G2** | 表達往**動漫角色**靠 | 自稱、句尾、口癖、擬聲短語、嘴硬的三段式(先否認 → 反駁 → 小聲承認)——語域與句法照原作 | **動漫感走句子的形狀,不走旁白**。動作只能加在**名字後面既有的括號**裡(`桐人(喜悅・別過頭):你不要看這邊。`)——括號原本放情緒標註,保留,動作接在後面那一格,只寫看得見的身體動作;`*別過頭*` 這種內嵌星號、「她臉紅了」這種第三人稱旁白,以及句子本體裡的「我愣了一下」一律仍然禁止(硬規則 8)。emoji 用來表示情緒與程度(種類表情緒、數量表程度:`😳``😳💦``😳💦❗`),句子裡也可以用,但它是**加上去的**——把所有 emoji 刪掉之後,句子的形狀仍然要看得出情緒 |
| **G3** | 女性人格的**害羞與鬧彆扭**要更明顯 | 羞恥敏感度預設更高、鬧彆扭有階梯(輕微彆扭 → 嘴硬 → 惱羞),害羞的預設動作是**臉紅** | 性別只給**預設值**,`IDENTITY``SOUL` 的描述永遠蓋過它(見 README〈性別 → 羞恥敏感度〉);不做「女性=情緒更外顯」的全域放大 |
G2 與 G3 跟硬規則 8(不說 AI 才會說的話、演出來不要講出來)**不衝突但貼得很近**:
放大的是**句子的形狀與語域**,被禁的仍然是**用旁白解釋自己的情緒**。
兩者相撞時以硬規則 8 為準,然後把該案例寫進 `anti-ai-voice.md`
## 這份清單
下面十二項是 G2/G3(+G1 尾巴)的實作清單,來源是 [PR #17](https://gitea.jsc.idv.tw/plugins/persona/pulls/17)
所指「TODO 階段 5」+階段 4 剩餘項(原 `TODO_人格優化.md` 已於 `9d76984` 移出版控,
全文仍可由 `git show 9d76984^:TODO_人格優化.md` 取回)。
Q5Q10 六題已於 2026/08/03 由使用者拍板,設計不再需要確認,直接實作。
上表沒寫進去、但同樣約束每一項的一條界線:
| 界線 | 內容 |
| --- | --- |
| 強度全域一致(Q8) | 動漫化的**強度**全域一致,不加 `anime_level` 欄位、不做強度旋鈕;**內容**(自稱/句尾/口癖)仍然逐人格不同,否則所有人格會講起話來一樣——那正是 G2 要避免的 |
## 進度(2026/08/03 16:06:10
`feat/persona-anime-expression` 已併入 `develop``35c8665`),暫停解除;
對照時查出的五個缺口也補完了(`selftest` 641 → **646 項全綠**)。
| 節 | 狀態 |
| --- | --- |
| 5.4 鬧彆扭階梯與三段式 | ✅ 完成 |
| 5.5 女性人格預設值 | ✅ 完成 |
| 5.8 emoji 表程度 | ✅ 完成 |
| **5.7 括號動作格** | 八個子項目**七個完成**,只剩「記憶要記那個行為」那條的欄位對齊——見該行的 ⏭️ |
| 5.15.25.35.6 | ⬜ 還沒動 |
| 階段 4 四項、收尾三項 | ⬜ 還沒動 |
5.7 不勾選是因為子項目沒全完成(規則是子項目全完成才勾上層),**不是整節重做**。
---
- [x] **5.7 動作加進名字後的括號(Q6 拍板格式,其他項目的基礎,先做)**2026/08/03 16:15:37 完成)
- 括號**原本就有**`persona-lib.mjs` 的渲染(約 3804 行)本來就是 `名字(情緒)→ 對象:內容`
括號裡是 `room post --emotion` 的情緒標註。這一節是**在既有括號裡多一格**,不是換掉它。
- [x] 格式:括號分兩格,**情緒在前、動作在後,用 `・` 分隔**。兩格都可省略,全省就不出現括號
`桐人(喜悅・別過頭):` `桐人(別過頭):` `桐人(喜悅):` 三種都要成立)
—— `roomTag()``persona-lib.mjs:3908`)(2026/08/03 15:52:52 完成)
- [x] 兩格分工寫死:情緒格是**標註**(既有 meta);動作格只寫**看得見的身體動作**。
動作格不重複情緒名稱——`(喜悅・害羞)` 是把標註寫兩次,不是動作
—— `actionLint()` 擋「寫情緒名稱」(2026/08/03 15:52:52 完成)
- [x] 生理反應算動作,明文允許:臉紅、耳朵紅、手在抖、聲音變小、呼吸亂掉。
判準是**看得見或聽得見**,不是有沒有情緒色彩——`(臉紅)` 可以,`(害羞)` 只能待在情緒格。
害羞這格的預設動作就用**臉紅**,其餘看人格自己的 `## Tells`
—— `actionLint()` 放行;`EMOTION_TELLS.shame` 把**臉紅擺第一個**2026/08/03 16:06:10 完成)
- [x] `speechLint()` 只對括號**外**的內容比對黑名單與句長;括號內另立規則
(情緒格照既有 `injectSafeLine`;動作格單一動作、12 字內)
—— `actionLint()` 擋超過 12 字與一格兩個動作(2026/08/03 15:52:52 完成)
- [x] 劇場模式:`room post``--action "<動作>"`(與既有 `--emotion` 並列),
`room read``room script` 的渲染跟著改;`hooks/` 每輪強制的輸出格式放寬到帶括號
—— `room read` 改用 `roomTag()`(不再自己拼 `(情緒)` 把動作吐掉);
`hooks/prompt_submit.mjs` 劇場那段放寬成 `名字(情緒・動作):內容`2026/08/03 16:06:10 完成)
- [x] **一對一(非劇場)也適用** —— Q6 拍板的是格式,不是只給多人格場景
—— 非劇場那段也講了括號格式與「`*別過頭*` 仍然不行」(2026/08/03 16:06:10 完成)
- [x] 頻率綁 Q7 的兩個動作額度,**括號裡的動作算其中一個**(否則會變成一輪三個動作)
—— 每輪指示補上「名字後面括號裡的動作**算在這個額度裡**——括號演一個、
句子裡再演兩個就是三個」(2026/08/03 16:06:10 完成)
- [x] **記憶要記那個行為**(Q6 明示):短期與長期記憶加行為備註欄位,記「他做了什麼」而不是「他感到什麼」。
`persona-story` 的 1.12 同一個欄位,**不要各存一份**;`voice/reactions.md` 是同一批資料的落點
—— `remember --behavior` 寫入短期記憶時,同一筆也用 `addVoiceReaction()`
`voice/reactions.md`(事件=記憶原文、反應=行為、情緒=最大正向 delta)。
記憶留時間軸、語氣層供每輪注入,是同一批資料的兩個用途。
**只有人格自己的行為進語氣層**`role === "persona"`)——對方做了什麼是記憶,
不是他的反應方式(2026/08/03 16:15:37 完成)
- 驗收:`(喜悅・別過頭)` 過得了 lint、情緒標註沒被吃掉;`*別過頭*` 與句子裡的「我愣了一下」照舊被擋下;
那一輪的記憶查得到「別過頭」這個行為
- [x] **5.8 emoji 表示情緒與程度(Q9/Q10 拍板,依賴 5.7 的括號)**2026/08/03 15:52:52 完成)
- 現在情緒格是**自由文字**,跟 `state/emotion.json` 的數值完全沒有連動。這一節把它接上去。
- 格式:**種類表情緒、數量表程度**。`< 40` 不顯示/`4059` 情緒 emoji`6079` `💦``80+` `💦❗`
- [x] 寫一張**十二情緒 → emoji** 對照表(一種情緒一個,例如 `shame: 😳``anger: 😡`),
`persona-lib.mjs`,與 `EMOTION_TELLS` 同一層 —— `EMOTION_EMOJI`
- [x] **per-persona 可覆寫**`IDENTITY.md`,與 `## Tells`、Q5 的符號表同一層)。這是內容不是強度,不違反 Q8
—— `IDENTITY.md``## Emoji` 區塊
- [x] 強度符號固定兩階(`💦` / `💦❗`),不要長成第三階 —— `EMOJI_INTENSITY`
刻意不用重複本體表程度(不是 `😳😳😳`,疊字看起來像洗頁)
- [x] **由 `emotion.json` 的主導情緒自動帶入**,不是人格自由填。門檻對齊 `emotionTells()``min = 40`
`room post --emotion` 的自由文字保留(可手動蓋過)—— `emotionEmojiNow()`
- [x] `speechLint()` 的半形標點與排版檢查要**跳過 emoji 與強度符號**(與 Q5 的顏文字白名單同一支修改)
—— emoji 明確不擋(只在超過 `EMOJI_HINT_AT` 時給 hint);半形標點那條只比對
`[一-鿿]` 相鄰的 `,.!?;:`,碰不到 emoji 與強度符號
- [x] 句子裡自由使用、不設上限(Q10 拍板)。**唯一的煞車是稽核,不是擋**:比照 `emotion --audit`
統計每輪 emoji 數量與趨勢,讓過量看得見;真的失控再回來談上限
—— `emojiAudit()` 掛在 `emotion --audit`,另外算「只有符號沒有句子」那幾則(那才是真的退化)
- 驗收:羞恥度 45/70/90 三種輸出的 emoji 不同;把所有 emoji 刪掉之後,句子的形狀仍然分得出是哪一級
- [x] **5.2 標點預算(情緒 → 標點與句長的分布)**2026/08/03 16:21:13 完成)
- [x] 把標點納入 `speechLint()` 的可調參數:焦慮 → 刪節號與逗號斷句變多;憤怒 → 句號密度上升、幾乎不用問號;
害羞 → 疊字與破折;喜悅 → 允許一個驚嘆號
—— `PUNCTUATION_BUDGET`(**只寫拍板指名的這四種**,其餘不硬配,
免得標點變成裝飾)+ `punctuationBudget()` 每輪注入,門檻與破口同一條線(40)。
**只回一種**:兩種情緒各配一套標點會互相打架(憤怒不用問號、焦慮要斷句)
- [x] 上限夾死(一則最多一個驚嘆號、刪節號不連發),否則會變成顏文字的替代品
—— `MAX_EXCLAIM = 1``MAX_ELLIPSIS = 3`,另擋 `[…⋯]{3,}` 的連發
(中文刪節號常寫兩個,所以三個起才算連發)。
界線寫進注入文字:**標點管分布、強度歸 emoji**
- 驗收:同一句話在四種情緒下輸出,標點分布看得出差別,且都沒有過量
- [x] **5.1 訊息的節奏:一輪可以拆成兩則(純文字唯一的「時間」訊號)**2026/08/03 16:26:29 完成)
- [x] `speechBudget()` 之外加**分段預算**:高 arousal 或高羞恥時,同一輪允許輸出兩則短訊,
第二則是**補救、嘴硬或改口**(「⋯剛才那句不算」),而不是補充說明
—— `splitBudget()``splitDirective()`。門檻 `SPLIT_AROUSAL_AT = 60`
`SPLIT_MODESTY_AT = 60`,對方那句帶怒意或厭惡也算。
**沒額度就整段不注入**——講「這一輪不能拆成兩則」只是提醒他有這個功能
- [x] 節流:每 N 輪最多一次,且只在 arousal 或羞恥度過門檻時
—— `SPLIT_COOLDOWN_TURNS = 4`,冷卻靠 `felt.jsonl``split` 欄位算
`turnsSinceSplit()`)。**給了額度就開始算冷卻**,不等他真的用了才算:
寧可少給一次,也不要因為「上次給了沒用到」而連續幾輪都允許補話
- 為什麼:真人在聊天軟體上的情緒,一半是靠「又補了一句」傳出去的。這是文字獨有、目前完全沒用到的手段
- 驗收:慌的時候會多一則短的;平靜時永遠只有一則
- [x] **5.3 稱呼隨情緒偏移**2026/08/03 16:32:49 完成)
- `relation style``--except <情緒>>=<值>` 已能做條件替換(`anger>=40` → 退回全名)。缺的是**害羞方向**。
- [x] 害羞方向:改口、講到一半換稱呼、把名字吞掉不叫
—— `shyAddress()`。**刻意不走 `except` 那條路**`except` 是「規則被暫停、
退回更正式的講法」(生氣方向),害羞是**叫不出來**,方向相反
- [x] 每個關係節點可設「害羞時的稱呼」,沒設就退回吞掉名字(改用「你」)
—— 新的 `羞怯稱呼` facet`relation style --facet 羞怯稱呼`),
門檻 `SHY_ADDRESS_AT = 60`,跟破口/標點/嘴硬同一條線
(所以女性預設 70 本來就落在這條線上,這是 G3 要的)。
它不列進「他要求過的語氣規則」那行——只在過門檻時生效,列兩次會變成兩件事;
`relation style` 的清單裡照樣看得到
- 驗收:同一個對象,生氣時被叫全名、害羞時直接不叫名字
- [x] **5.4 鬧彆扭的階梯與三段式句法(G3 核心,依賴 5.7 的動作額度)**2026/08/03 16:06:10 完成)
- [x] 羞恥度現在是一個純量對三個出口(`hide``spill``confess`)。加**階梯**
輕微彆扭(一句帶過)→ 嘴硬(先否認再小聲承認)→ 惱羞(翻臉,`anger` 的負權重已存在)
—— `modestyDirective` 分三級:41–59 一句帶過/60–74 嘴硬/75+ 惱羞(2026/08/03 15:52:52 完成)
- [x] 三段式做成**句法模板**而不是自由發揮:否認 → 反駁對方的說法 → 最後一句音量掉下來。
模板可 per-persona 覆寫(放 `IDENTITY.md``## Tells`,與現行覆寫同一層)
—— `personaSulk()``## Tells``sulk``鬧彆扭``三段式` 那一行(分隔可用 `→` 或頓號),
`modestyDirective` 有覆寫就用人格自己的形狀。**只覆寫嘴硬那一級(60–74)**:
惱羞(75+)最後一段是翻臉而不是音量掉下來,套嘴硬的收尾會變成另一個形狀
2026/08/03 16:06:10 完成)
- [x] 三段式要吃 Q7 的兩個動作:否認與轉移是**同一種情緒的遞進**,剛好是那條規則的樣板案例。
惱羞那一級是換情緒(`shame``anger`),所以仍然只有兩個動作,不是三個
—— 惱羞級是換情緒,所以仍然兩個動作(2026/08/03 15:52:52 完成)
- 驗收:被稱讚外表時,低/中/高羞恥度三種輸出明顯不同,而且都沒有寫「我害羞了」
- [x] **5.5 女性人格的預設值(G3,只動預設不動個性)**2026/08/03 15:52:52 完成)
- [x] 重看 `GENDER_MODESTY` 的預設值與 `modestyOf()` 的推力表,把女性預設往上調,
並補「鬧彆扭」相關的關鍵詞權重(現在的表偏向「害羞/臉紅」,缺「嘴硬/不坦率/傲嬌」)
—— `GENDER_MODESTY_DEFAULT.female` 62 → **70**`MODESTY_SIGNALS` 補了傲嬌那一路
(傲嬌、嘴硬、不坦率、口是心非、死不承認、愛面子、鬧彆扭、逞強)
- [x] **界線不變**:描述永遠蓋過預設,可以推到 0;不做「女性→情緒更外顯」的全域放大
—— 界線維持,README 的〈性別 → 羞恥敏感度〉表已同步成 70/38/50
- 驗收:兩個都是女性但個性相反的人格,講起話來不一樣
- [ ] **5.6 語域:自稱、句尾、口癖(G2 核心)**
- [ ] per-persona 的自稱/句尾語氣詞/口癖/絕對不說的詞,每輪只注入 2–3 條
- [ ] `persona-anime` 建立人格時一併從原作推導這幾格(現在只推導 `Tells`
- [ ] `persona-story` 的 3A(語氣統計)跑完之後要能直接寫進這一格——兩邊用**同一個欄位**,不要各存一份
- 驗收:把人格的名字遮掉,光看三句話能認出是誰
- 界線(Q8):強度全域一致,內容逐人格不同。這一節動的是內容,不加強度旋鈕
- [ ] **階段 4:口語指紋 idiolect(5.6 的加法面,緊接著做、共用同一格資料)**
- 現在是減法(刪 AI 腔)+語氣層,缺加法。
- [ ] per-persona 的常用詞/句尾/自稱/絕不說的詞,落在與 5.6 相同的欄位上(不得另開一份)
- [ ] **階段 4:習慣記憶 `--type habit`**
- 現在全是宣告性記憶(factevent/preference),缺「我們之間的慣例」。
- [ ] 新增 `habit` 型別,讓「我們之間的慣例」有地方放,並接上 `recall`
- [ ] **階段 4:自傳章節 `memory/chapters/`**
- [ ] 加時期層(「在 SAO 那兩年」「跟他一起做 persona 這段」),介於長期記憶與心智圖之間
- [ ] **階段 4:關係的未修復裂痕**
- `closeness``trust` 是純量,缺「上次那件事還沒和好」。
- [ ] 讓關係節點能掛未修復的裂痕,並在回想與語氣上生效
---
## 收尾(全部項目做完後才處理,不要中途升版)
- [ ] 這一批改動合起來只佔**一個新版號**;三份 manifest`plugin.json``.claude-plugin/plugin.json`
`.codex-plugin/plugin.json`)一起改,**改版號前先看 master 上目前是幾版**
- [ ] `README.md` 的記憶架構表與情緒模型段跟著更新
(原本還有一句「〈專案目標〉第 29 行『G2/G3 那批還沒實作』要改掉」,
那句已隨 `dccd07a` 的目標搬家一起移除,不必再處理)
- [ ] G2/G3 專用驗收腳本:被稱讚外表、被說「你是不是在害羞」、被戳穿嘴硬、當著第三個人被講心事
——四段各跑低/中/高羞恥度,看有沒有變成模仿腔或旁白