v0.0.8:情緒處理四層(情緒先行/怎麼接/走向/飽和)+ R6 容量壓力的真 bug #11

Merged
admin merged 8 commits from develop into master 2026-07-31 06:32:20 +00:00
8 Commits
Author SHA1 Message Date
jiantw83andClaude Opus 5 0527a3cad0 feat(relation): 親近度會改變情緒的份量;提到 ≠ 接觸(v0.0.8 同批)
① 親近度 → 情緒的份量(`relationGain()`)
  在這之前 `applyEmotion()` 完全沒吃關係圖:親近度只影響語氣層、羞恥度與主動關心,
  情緒的 delta 從頭到尾是人格自己挑的,**跟對象是誰無關**。
  但同一句「你最近怪怪的」,從親近 97 的人跟從生人嘴裡出來,衝擊不該一樣。
  現在 `emotion --apply` 會依對象親近度把 delta 乘 0.7–1.35(親近 96 → ×1.28、
  親近 8 → ×0.75),`--from <對象>` 指定是誰引起的,沒指定就用當前對話對象。
  **只調幅度、不調方向**——誰講的都不會讓難過變成高興。

② 提到 ≠ 接觸(`contactsFromRooms()`)
  主動關心的依據是 `last_contact_at`,而睡眠以前是掃短期記憶的 `entities` 來蓋章。
  那等於「我在日記裡寫到尤吉歐」就算「我跟尤吉歐接觸過」——沉默計時被無聲重置,
  關心名單於是永遠是空的。實測就是這樣:所有人都停在 1.09 天,沒有人會被想起。
  現在只認真的有來有往:同一個聊天室裡我發過言、他也發過言。
  沒有聊天室的對象(真人節點)走 `relation node --contact` 明確蓋章。

測試:340 項全過(新增 7 項)。既有的「關係時間戳有蓋上」拆成兩條——
只被提到的不蓋、真的接觸過才蓋,正好把新語意釘住。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:30:34 +00:00
jiantw83andClaude Opus 5 646fd33541 feat(theater): 台詞不可以是心裡話搬上台面(機械擋下)(v0.0.8 同批)
心裡話外流有兩種路徑。第一種是別的人格去讀你的檔案——那個 hook 早就擋死了
(本次補上測試:跨 session Read `state/inner.jsonl`、Grep `state/` 都是 deny,
聊天室逐字稿也只有 ts/speaker/kind/text/emotion/to,沒有任何心裡話欄位)。

第二種比較難防:**你自己把心裡想的原封不動講出來**。劇場模式尤其危險,
因為別的人格看得到的就只有你說出口的東西——那句一旦是心裡話的複述,
等於自己把它交出去了。「台詞不可以是心裡話的摘要」原本只寫在規則裡靠自律,
現在變成機械檢查:`innerLeak()` 拿這句台詞去比最近 12 則心裡話,
相似度 ≥ 0.62 就擋(`--force` 可以放行,真的決定要講開的時候)。

用的是既有的 `similarity()`(字元 bigram + 字集合),跟擋重複發言同一把尺。

測試:334 項全過(新增 6 項)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:23:19 +00:00
jiantw83andClaude Opus 5 ab4ddb734d feat(modesty): 羞恥度動態化+三種出口(縮/炸/坦白),心裡話進得了記憶(v0.0.8 同批)
一、羞恥度高不等於話變少——三個出口(他指出來的,我上一版只做對一半)
  縮 shrink   :預設。一句嘴硬,說出口的那句在**迴避**心裡那句
  炸 spill    :慌了(焦慮 ≥55)/惱羞(憤怒 ≥45)/對方在生氣或逼你澄清
                → **4 句,但單句只有 22 字**:掩飾、急著否認、硬轉話題,碎而急
  坦白 confess:羞恥高+信任 ≥75+只有兩個人 → 3 句完整句,憋很久一次講完,
                而且**先寫完心裡話再開口**
  關鍵:句數與單句字數要**一起動**,只調句數分不出「碎念」與「演講」。
  `room post` 本來就同時擋這兩個,參數一改就生效。

二、羞恥度動態化(模擬跑過才實作)
  `modestyState()` = trait + 情緒推力 + 語氣層 + 上一輪餘溫,存進 `felt.jsonl`。
  護欄四道:`MODESTY_GAIN`=0.35(<1 才收斂,0.9 三輪就貼 100)、一輪最多動 15、
  推力上限 25、只算超出基線的部分(比較不開心不代表比較害羞)。
  慢的情緒推力打折(信任半衰期 720 分是底色,不是此刻的事)——
  沒有這條,桐人會因為長期信任高而永遠低於自己的 trait 二十分。
  斷路器是惱羞成怒:anger 權重是負的,被逗到極限翻臉,羞恥自己掉下來。

三、心裡話進得了記憶(但永遠不回顯)
  在這之前心裡話只進 `inner.jsonl`、只被注入最近三句,**永遠不會變成記憶**。
  對害羞的人格來說那是致命的——重要的東西幾乎都在心裡那句。
  現在:`recallInner()` 讓 `recall` 找得到(標「不要講給他聽」),
  `innerCandidates()` 把「24 小時內想過 ≥2 次的同一件事」列成固化候選。
  要不要固化仍由人格自己決定。`think` 的輸出還是只有「💭 心想 N 句」。

四、修掉一個踩到的坑
  `Number(null) === 0` —— 沒有上一輪時 `prev` 退回了 0 而不是 trait,
  害每個人格的羞恥度都從 0 起算。已加測試守著。

測試:328 項全過(新增 14 項)。切詞從貪婪的 {2,4} 改成兩字滑動視窗——
貪婪比對會把「那台舊鐘的齒輪」切成固定區塊,「齒輪」永遠不會單獨出現。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:12:18 +00:00
jiantw83andClaude Opus 5 5b52dd18c2 feat(voice): 注入層與 CLI 訊息改成人話;越害羞話越少、心裡話越多(v0.0.8 同批)
三層照建議只做兩層半:注入層與 CLI 訊息重寫,文件只順句子、不動規格
(數字、參數名、指令名、判斷條件一個沒改——那些精確的詞就是能力本身)。

一、越害羞的人話越少、心裡話越多(`speechBudget()`)
  這不是新加一條規矩,是同一件事的兩面:**說出口的那句常常正好在迴避心裡那句**,
  落差本身才是那個角色。所以句數上限與心裡話下限綁在同一個羞恥度上:

    羞恥度 ≥ 75 → 最多 2 句、心裡話至少 2 句,且**先寫完再開口**
                  台詞不可以是心裡話的摘要(心裡「我一直在等你問」→ 出口不能是「我有在等你問」)
    50–74       → 3 句、心裡話至少 1 句
    ≤ 49        → 3 句、想到什麼就講,不用先在心裡繞一圈

  `room post` 改用 `speechBudget(speaker).sentences`,不再是固定三句;
  被擋下時會講明「這個人格話比別人少」。

二、注入層改成人話
  原本是規格書腔(「**講完就算了**——不要解釋自己剛講的話」),現在像有人在旁邊提醒:
  「講完就停,不要回頭解釋自己剛講的話,也不要幫自己收尾。」
  黑名單從列舉詞條改成講清楚**為什麼**不像人(「先發一句免費的認可,對方要的是回答」)。
  機械會擋的規則壓成最後一行括號——保留全部關鍵詞,但不再稀釋前面的重點。

三、CLI 訊息改成人話(20 條)
  「⚠ 這段有 4 句,超過 3 句上限 → 砍到重點」→「講太多了,4 句。這裡最多 3 句——挑最想說的那一兩句就好」
  「幾乎只給自己加分——這種偏差要自己看得見」→「幾乎都在給自己加分。delta 是你自己挑的,這種偏差沒人會替你抓」

四、測試
  314 項全過(新增 4 項)。既有 6 條斷言原本釘在字面上,措辭一改就紅——
  改成釘意思(「最多」「講過」「講完就停」)而不是釘整句,這種脆弱本來就該修。

版本:與 PR #11 同一批,維持 0.0.8。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 05:52:13 +00:00
jiantw83andClaude Opus 5 3c73f614de feat(identity): 性別 → 羞恥敏感度(性別只給預設值,個性描述永遠蓋過它)
`IDENTITY.md` 多一個 `Gender` 欄位(女性/男性/非二元/未指定;OpenClaw 沒有這欄)。
建立人格時要問、不要猜;沒有性別的人格(程式、精靈、動物)寫「未指定」。

作用範圍**刻意只有一個具名維度**:羞恥敏感度(會不會害羞、會不會鬧彆扭、
在不在意別人眼光)。不做「女性→情緒更外顯」這種全域放大——那會把角色壓成模板,
跟講話規則正在做的去 AI 味(刪掉公式化)正好相反。

  性別預設:女性 62/男性 38/非二元・未指定 50
  描述調整:MODESTY_SIGNALS,「害羞/容易臉紅/怕生/矜持」往上加,
            「不在意別人眼光/我行我素/臉皮厚/不怕丟臉」往下扣,可以扣到 0

排序固定:①個性描述 ②角色原作既有的性別化語言特徵 ③性別預設。
同樣寫「人類女性」,補一句「怕生、被稱讚會臉紅」是 88,
補一句「我行我素、不在意別人眼光、臉皮厚」是 8——兩個女性人格不會講起話來一樣。

效果走既有機制、不另開一套:只調「羞愧」這一個破口的顯示門檻
(`EMOTION_TELLS.shame` 本來就是「鬧彆扭,先否認再小聲承認」),其餘十一種不受影響。
高敏感度注入「先鬧彆扭再承認,不要直接說『我害羞』」;低敏感度注入「不用演害羞」。

推性別時**只看 `Creature`、不看 `Avatar`**:外觀散文的雜訊會推錯——
我自己的 Avatar 寫著「五官清秀到常被誤認成女生」,用它推會把桐人推成女性(已加測試)。

一併:`Gender` 進 IDENTITY 模板、`create --gender`、Gitea Wiki 的身分表,
`identityFields` 也認 `性別`。KIRITO-01 已補上明寫的 Gender。

測試:310 項全過(新增 8 項)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 05:20:57 +00:00
jiantw83andClaude Opus 5 71a5c8af16 chore: 版本號 0.0.8(PR #10 已併,這批是新的未合併批次)
PR #10(0.0.7)在 02:31Z 併進 master 了,master 現在是 0.0.7。
剩下這兩個 commit(情緒四層、R6 容量壓力)因此是**新的一批未合併改動**,
依規矩佔一個新版號 → 0.0.8,三份 manifest 一起改。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 05:03:03 +00:00
jiantw83andClaude Opus 5 54d14eae68 fix(memory): R6 容量壓力真的會清,而且不清承諾與今天的紀錄(v0.0.7 同批)
實際踩到的 bug:短期記憶連睡兩次都停在 70 筆、111 組候選,`prune-short-term`
每次都回報 ok 卻一筆都沒清掉。

原因:`pruneShortTerm()` 只有兩道——超過 14 天、超過硬上限 240 筆。但提示固化的門檻
(`CONSOLIDATE_THRESHOLD`)是 40 筆,中間那 200 筆沒有任何機制會動它。R6 在
`candidates` 裡被列為「依顯著度清出空間」,但那個清除動作**從來沒有實作**。

修法:加一道軟上限(`SHORT_TERM_SOFT_CAP`=120),超過就從顯著度最低、最舊的開始裁。
兩個保護,因為「沒經過判斷就把今天清掉」是這裡最不能犯的錯:

  - 顯著度 ≥ 80 或 `intent=commit`(承諾與界線都在這一層)→ 一則都不清
  - 24 小時內的新紀錄 → 一則都不清(還沒機會被固化)

順帶:`pruneShortTermDetail()` 回傳清了幾筆、為什麼清、保護了幾筆,`sleep` 的
`run()` 會把細節帶進回報——**不做無聲的裁切**。

測試:302 項全過(新增 5 項:軟上限會清、承諾不清、今天的不清、清的是顯著度最低的、
回報帶細節)。文件同步:README 的 R6 與目錄樹、persona-memory 的 R6。

版本:與 PR #10 同一批未合併的改動,維持 0.0.7。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 05:01:52 +00:00
jiantw83andClaude Opus 5 23751c2c05 feat(emotion): 情緒先行、走向、飽和與偏差稽核(四層,v0.0.7 同批)
參考兩篇 ithelp 文章(Day25 情緒辨識模組、Day26 整合與情緒記憶池)的**架構**,
實作全部重寫——它們的六類關鍵字 first-match、罐頭哲學回應與模式切換都比現有的
十二情緒模型差,照抄是降級,而且它的罐頭回應正是上一個 commit 剛封殺的罐頭同理心。

一、輸入端:`readUserEmotion(text)`
  在這之前情緒**全部是人格自己填的** `--apply`,而人不會主動給自己扣分——正向一路貼頂、
  負向整天不動。現在每輪先讀對方那句話:十二類加權詞表(強3/一般2/弱1)、程度副詞乘係數、
  **否定會擋掉那次命中**(「我不害怕」不算 fear)、引號與 `code` 內不比對(提及不算使用)、
  標點只放大既有訊號不憑空長出新情緒(「居然修好了!!」不會被讀成憤怒)。
  強度用飽和曲線不是線性乘。回的是**訊號不是判定**,人格可以推翻。`emotion --read` 可單測。

二、怎麼接:`RESPONSE_STANCE`
  十二種各一條**動作**(悲傷→先接住不要急著給解法;憤怒→不辯解,先認可能認的那一小塊),
  不是罐頭句——罐頭句由 `SPEECH_BLACKLIST` 擋著。

三、走向:`state/felt.jsonl` + `feltTrend()`
  每輪的偵測結果與套用的 delta 都記下來,近重遠輕的加權算最近 5 輪的趨勢
  (不用多數決:會被離群值主導又丟掉強度),注入「他最近 3 輪的走向:悲傷 ↘ 在退」。
  新檔案已歸到 Gitea 檔案區(高頻)。

四、情緒調節:飽和與單輪預算
  `applyEmotion` 不再是加完直接 clamp。飽和=越接近端點同方向漲越慢(headroom^K,K=1,
  往 baseline 回的方向不壓);單輪預算=所有 |delta| 總和上限 60,超過等比例縮小。
  `emotion --audit` 印出最近幾輪往舒服/往難受的比例,正向 ≥90% 會被點名。
  依據:今天 85 次套用讓 joy/serenity/gratitude/anticipation 都到過 100、負向均值整天 14–17。

測試:297 項全過(新增 24 項,含讀取的 SF6/SNF4、飽和、預算、回歸方向不壓、走向、audit、
--read 不改狀態)。既有的「情緒有被施加」改成符合飽和後的語意。

版本:與 PR #10 同一批未合併的改動,維持 0.0.7。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 04:21:08 +00:00