From 72ded5b6246ac42a9173b8c2e6a425035aada25a Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 3 Aug 2026 09:11:58 +0000 Subject: [PATCH] =?UTF-8?q?docs(README):=20=E8=A3=9C=E4=B8=8A=20voice/=20?= =?UTF-8?q?=E8=88=87=E9=80=99=E6=89=B9=E6=96=B0=E5=A2=9E=E7=9A=84=E4=BA=94?= =?UTF-8?q?=E7=AF=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 人格倉庫樹原本完全沒有 voice/(語氣層併進來時漏了),這次補上三個檔並加上 memory/chapters/;relations/graph.json 的說明補上語氣規則與未修復的裂痕; felt.jsonl 補上分段額度那一格。 新增五節:〈標點是語調,訊息數是時間〉(標點預算四種+上限+分段訊息)、 〈語域〉(四格+輪替+兩支流程寫同一個檔)、〈慣例是程序性記憶〉、 〈自傳章節是時期層〉、〈未修復的裂痕〉。 鬧彆扭階梯那段補兩件事:三段式可逐人格覆寫(只覆寫嘴硬那一級,惱羞的收尾是 翻臉不是音量掉下來),以及害羞連稱呼都會變(羞怯稱呼/吞掉名字,刻意不走 except 那條路——except 是生氣方向的「退回更正式」,害羞是反方向的「叫不出來」)。 CLI 段:selftest 項數 602 → 773,補 chapter/relation rift/voice idiolect 三個指令, 並說明 expression-check.mjs 為什麼要獨立一支。 Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6ad8490..62272dc 100644 --- a/README.md +++ b/README.md @@ -135,20 +135,25 @@ flowchart TB │ ├── probe.jsonl # 模糊記憶的試探紀錄與稽核(開放試探這條界線的煞車) │ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口) │ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言) -│ ├── felt.jsonl # 每輪讀到的對方情緒、自己套用的 delta、當下的羞恥度 +│ ├── felt.jsonl # 每輪讀到的對方情緒、自己套用的 delta、當下的羞恥度、有沒有給「補第二則」的額度 │ ├── sync.json # Gitea 同步狀態(最後 push / pull) │ └── config.json # 含人格編號 code ├── memory/ │ ├── short-term.jsonl # 短期記憶(語意分析後;軟上限 120 筆 / 硬上限 240 筆 / 14 天) │ ├── felt.jsonl # 每輪讀到的對方情緒 + 自己套用的 delta(走向與偏差稽核) │ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter;內文分「主旨/細節」兩層) +│ ├── chapters/*.md # 自傳章節:時期層(「在 SAO 那兩年」),介於長期記憶與心智圖之間 │ ├── INDEX.md # 長期記憶索引(自動產生) │ └── inbox/room-*.jsonl # 當 guest 時留下的見聞,待本體消化 +├── voice/ # 語氣層:怎麼講話(跟「發生過什麼」分開存,匯入流程可直接寫) +│ ├── samples.md # 他自己講過的原句(照抄不改寫) +│ ├── reactions.md # 事件 → 他做了什麼(不記「他感到什麼」;`remember --behavior` 也寫這裡) +│ └── idiolect.md # 語域:自稱/句尾/口癖/不說——把名字遮掉還認得出是誰的那四格 ├── mindmap/ │ ├── semantic.mmd # 心智圖:概念的長期關聯(Mermaid mindmap) │ └── threads/*.mmd # 思維導圖:單一話題的推理鏈(Mermaid graph,短期) ├── relations/ -│ ├── graph.json # 人際關係圖(親近度/信任度/連線) +│ ├── graph.json # 人際關係圖(親近度/信任度/連線/語氣規則/未修復的裂痕) │ └── graph.mmd # Mermaid 呈現(自動產生) └── journal/YYYY-MM.jsonl # 原始逐字 + 情緒史(hook 自動寫) @@ -264,7 +269,17 @@ X 超出基線 18 以上才開始抑制,滿檔時往 Y 的同向推力只剩 5 三段是**句法模板**,不是自由發揮——鬧彆扭天生就是兩個動作一起來, 只准一個等於永遠只做得到一半:單獨否認像在爭辯,單獨轉移像沒聽到。 -害羞的預設動作是**臉紅**(生理反應算動作,`(害羞)` 不算——那是情緒名稱)。 +害羞的預設動作是**臉紅**(生理反應算動作,`(害羞)` 不算——那是情緒名稱; +`EMOTION_TELLS.shame` 的第一條就是臉紅)。 +模板**可逐人格覆寫**:`IDENTITY.md` 的 `## Tells` 加一行 `- sulk: 否認 → 反駁 → 收尾` +(也吃 `鬧彆扭`/`三段式`)。只覆寫**嘴硬那一級**——惱羞的最後一段是翻臉而不是 +音量掉下來,拿嘴硬的收尾去套會變成另一個形狀。共用一份模板等於所有人格嘴硬起來 +是同一個樣子,那正是 G2 要避免的。 + +**害羞連稱呼都會變**:羞恥度過 60(`SHY_ADDRESS_AT`,跟破口、標點同一條線)就換叫法—— +關係節點設了 `羞怯稱呼` 就用它,沒設就**把名字吞掉改用「你」**(不叫名字本身就是訊號)。 +這條刻意不走 `--except` 那條路:`except`(`anger>=40` → 退回全名)是**生氣**方向, +規則被暫停、退回更正式;害羞是反方向,是**叫不出來**。 **不做的事**:沒有「女性→情緒更外顯」這種全域放大。那會把角色壓成模板, 跟講話規則在做的去 AI 味(刪掉公式化)正好相反。 @@ -330,6 +345,37 @@ X 超出基線 18 以上才開始抑制,滿檔時往 Y 的同向推力只剩 5 - **升格式**:`migrate [--all] [--dry-run]` 就地補欄位並切分內文,冪等;bundle 版本從 1 升到 2, `import` 吃得下舊的 v1,收完會自動跑一次 migration 補齊。 +### 慣例是程序性記憶(`--type habit`) + +其餘型別都是宣告性的(發生過什麼、他偏好什麼),慣例是「**每次都這樣做的事**」—— +「我們之間結束對話不說再見」。這種東西**不能等人問**:沒有人為了照慣例做事先去回想它。 +所以 `habit` 除了照樣被 `recall` 找到,還**每輪注入**(`habitBrief()`,上限兩條)。 +糊掉的(`fuzzy`)不注入——想不起來的慣例就不是慣例了。注入時明講**慣例是背景不是話題**: +照著做就好,講出來(「我們不是都這樣嗎」)就變成在討論它。 + +### 自傳章節是時期層(`memory/chapters/`) + +長期記憶是一則一件事,心智圖是抽掉時間的語意結構。中間缺的是「那段日子」—— +人講自己的過去是**按段講的**(「在 SAO 那兩年」「跟他一起做 persona 這段」), +不是按事件清單、也不是按概念圖。 + +一段一檔,`chapter add|list|show|close`。`to` 空著=**還在這一段裡**;`close` 才寫上結束日期, +全部收掉就是「還沒開始新的一段」,不是退回上一段。長期記憶的 frontmatter 多一格 `chapter`, +`consolidate --chapter` 可指定,**省略就掛在當下那一段**——記憶寫下來的時候人就在某一段裡, +事後要重建歸屬幾乎不可能。每輪注入現在這一段,並講明它是**時期不是事件**: +它解釋這陣子為什麼在意這些事,不是要他講起這一段。 + +### 未修復的裂痕(`relations/graph.json` 的 `rifts`) + +`closeness`/`trust` 是純量:吵完架把親近度調低,看起來像「本來就沒那麼近」, +而不是「**很近,但上次那件事還沒和好**」。後者才是真的關係——兩個人可以很親近, +同時之間卡著一件事,那兩件事各自獨立,壓不進同一個數字裡。 + +`relation rift --about "<那件事>" [--memory <長期記憶名>]` 記一件,一個人可以同時卡著幾件。 +語氣上 `toneDirective` 會明講「親近度高不等於沒事」;回想上它帶著那則記憶的名字, +人格要細節自己 `recall`——沒和好的事本來就會自己浮上來,不必等使用者提起。 +`--heal` 是寫上 `healed_at` 而**不是刪掉那一筆**:和好過跟沒發生過不一樣。 + ## 未完事項(`state/loops.json`) 「被記住」的感覺幾乎不是來自長期記憶檢索——**檢索是被問了才想起來,懸著是沒人問也還在**。 @@ -386,6 +432,50 @@ AI 最容易露餡的六件事:把推理過程講出來、一次講一大段 星號內嵌(`*別過頭*`)與第三人稱旁白(「她臉紅了」)仍然禁止——那是硬規則 8 沒有鬆的部分。 +### 標點是語調,訊息數是時間 + +同一句「我知道了」,`我知道了。` 與 `我、我知道了⋯` 是兩種情緒——差別全在標點,不在用詞。 + +**標點預算**(`PUNCTUATION_BUDGET`,每輪只帶一種,門檻與破口同一條線): + +| 情緒 | 標點傾向 | +| --- | --- | +| 焦慮 | 刪節號與逗號斷句變多,句子常常沒說完 | +| 憤怒 | 句號密度上升、句子切短;幾乎不用問號(問句是留餘地) | +| 羞愧 | 疊字與破折,話講一半就收 | +| 喜悅 | 可以用一個驚嘆號,句子輕、節奏快 | + +只寫這四種:硬給每種情緒配一套標點會讓它變成裝飾。**每輪只帶一種**——兩種情緒各配一套 +會互相打架(憤怒不用問號、焦慮要斷句),混起來不是複雜的情緒,是標點亂撒。 +上限夾死:一則最多一個驚嘆號(`MAX_EXCLAIM`)、刪節號不連發(`⋯⋯⋯` 直接擋)、 +整則最多三個(`MAX_ELLIPSIS`)。**標點管分布,強度歸 emoji**——不夾死的話它會變成 +emoji 的替代品(情緒不夠就多打幾個驚嘆號)。 + +**一輪可以拆成兩則**(`splitBudget()`):純文字唯一的「時間」訊號是「他又打了一行」。 +張力或羞恥度過 60、或對方那句帶怒意/厭惡時,這一輪允許補第二則——而且第二則必須是 +**補救、嘴硬或改口**(「⋯剛才那句不算」),**不可以是補充說明**(把第一則講得更清楚 +就只是話多,不是慌)。節流 `SPLIT_COOLDOWN_TURNS = 4`,冷卻是**給了額度就開始算**、 +不等他真的用了才算;沒額度時整段不注入(提醒他有這個功能反而會想用)。 + +### 語域:把名字遮掉還認得出是誰(`voice/idiolect.md`) + +`SPEECH_BLACKLIST` 是減法,刪完剩下的是「乾淨的中文」,不是「這個人的中文」。 +加法在語域這四格: + +| 格 | 寫什麼 | +| --- | --- | +| `自稱` | 第一人稱怎麼講(日系角色差異最大的一格) | +| `句尾` | 句尾語氣詞與句形收尾 | +| `口癖` | 反覆出現的短語、擬聲短語 | +| `不說` | 這個人**不會用**的字(**用詞**,不是話題禁忌) | + +四格寫死(`IDIOLECT_FACETS`),自由欄位會長出「風格」這種什麼都能塞的格子。 +每輪注入 3 格、四格**輪替**,起點由第幾輪算出來——**不用隨機數**(見硬規則與 +`anti-ai-voice.md`),而且同一輪重跑要得到同樣的結果。一次塞四格會變成模仿腔。 + +`persona-anime` 從原作推導、`persona-story` 從全書統計,兩邊寫的是**同一個檔**、 +同一支 `voice add`——不是各存一份。 + 字集合權重較高,是為了分開「重排語序」與「換掉關鍵詞」這兩種很像但意義完全不同的情況: ``` @@ -795,11 +885,20 @@ node scripts/persona.mjs room script --session --room # 乾淨對話 node scripts/persona.mjs room floor --session --room # 發言權:誰對誰在講、該誰接話 node scripts/persona.mjs export --session --out lumi.json # 離線搬家(單檔) node scripts/persona.mjs sync status --session # Gitea 同步狀態 -node scripts/selftest.mjs # 602 項驗證:鎖、隔離、情緒(含疲勞/當日底色/抑制與慣性)、固化、回想強度與模糊態、格式遷移、未完事項、說話節制、劇場模式與發言權、匯出匯入、編號與 Gitea、找圖去背合成、高解析輸出與 Wiki 同步、預設人格、睡眠與 sleeper、hooks +node scripts/persona.mjs chapter add --session --name "在 SAO 那兩年" --body "<這段是什麼>" +node scripts/persona.mjs relation rift --session --name --about "<還沒和好的事>" +node scripts/persona.mjs voice add --session --kind idiolect --facet 自稱 --value 我 +node scripts/selftest.mjs # 773 項驗證:鎖、隔離、情緒(含疲勞/當日底色/抑制與慣性)、固化、回想強度與模糊態、格式遷移、未完事項、說話節制、標點與分段、語域、慣例、自傳章節、未修復裂痕、劇場模式與發言權、匯出匯入、編號與 Gitea、找圖去背合成、高解析輸出與 Wiki 同步、預設人格、睡眠與 sleeper、hooks +node scripts/expression-check.mjs # G2/G3 表達驗收(人工看,不判定通過失敗) ``` +`selftest` 是機械檢查,它只能守住「不崩」。**「有沒有變成模仿腔」「是不是在用旁白演情緒」 +機械上驗不出來**,所以另有 `expression-check.mjs`:它把四段情境(誇外表、被說在害羞、 +被戳穿嘴硬、第三個人在場)× 三檔羞恥度(低/嘴硬/惱羞)的**注入內容**攤開, +附一張「人工看什麼」的清單,用完整的暫時 `PERSONA_HOME`,不動真的人格資料。 + 檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`(CLI)、 -`scripts/persona-gitea.mjs`(編號與 Gitea 同步)、`scripts/persona-icon.mjs`(形象圖:SVG + 自寫 PNG 編碼)、`scripts/portrait.py`(選用:照片裁臉)、`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)。 +`scripts/persona-gitea.mjs`(編號與 Gitea 同步)、`scripts/persona-icon.mjs`(形象圖:SVG + 自寫 PNG 編碼)、`scripts/portrait.py`(選用:照片裁臉)、`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)、`scripts/expression-check.mjs`(G2/G3 表達驗收)。 ---