feat(記憶與目標): 專案目標三條 + 固化候選不再重算(0.2.1) #17

Merged
admin merged 41 commits from develop into master 2026-08-03 09:15:21 +00:00
Showing only changes of commit 72ded5b624 - Show all commits
+104 -5
View File
@@ -135,20 +135,25 @@ flowchart TB
│ ├── probe.jsonl # 模糊記憶的試探紀錄與稽核(開放試探這條界線的煞車) │ ├── probe.jsonl # 模糊記憶的試探紀錄與稽核(開放試探這條界線的煞車)
│ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口) │ ├── inner.jsonl # 心裡話(推導過程;只回報「心想 N 句」,不說出口)
│ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言) │ ├── said.jsonl # 說過的話(用來擋短時間內的重複發言)
│ ├── felt.jsonl # 每輪讀到的對方情緒、自己套用的 delta、當下的羞恥度 │ ├── felt.jsonl # 每輪讀到的對方情緒、自己套用的 delta、當下的羞恥度、有沒有給「補第二則」的額度
│ ├── sync.json # Gitea 同步狀態(最後 push / pull │ ├── sync.json # Gitea 同步狀態(最後 push / pull
│ └── config.json # 含人格編號 code │ └── config.json # 含人格編號 code
├── memory/ ├── memory/
│ ├── short-term.jsonl # 短期記憶(語意分析後;軟上限 120 筆 / 硬上限 240 筆 / 14 天) │ ├── short-term.jsonl # 短期記憶(語意分析後;軟上限 120 筆 / 硬上限 240 筆 / 14 天)
│ ├── felt.jsonl # 每輪讀到的對方情緒 + 自己套用的 delta(走向與偏差稽核) │ ├── felt.jsonl # 每輪讀到的對方情緒 + 自己套用的 delta(走向與偏差稽核)
│ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter;內文分「主旨/細節」兩層) │ ├── long-term/*.md # 長期記憶(一則一檔 + frontmatter;內文分「主旨/細節」兩層)
│ ├── chapters/*.md # 自傳章節:時期層(「在 SAO 那兩年」),介於長期記憶與心智圖之間
│ ├── INDEX.md # 長期記憶索引(自動產生) │ ├── INDEX.md # 長期記憶索引(自動產生)
│ └── inbox/room-*.jsonl # 當 guest 時留下的見聞,待本體消化 │ └── inbox/room-*.jsonl # 當 guest 時留下的見聞,待本體消化
├── voice/ # 語氣層:怎麼講話(跟「發生過什麼」分開存,匯入流程可直接寫)
│ ├── samples.md # 他自己講過的原句(照抄不改寫)
│ ├── reactions.md # 事件 → 他做了什麼(不記「他感到什麼」;`remember --behavior` 也寫這裡)
│ └── idiolect.md # 語域:自稱/句尾/口癖/不說——把名字遮掉還認得出是誰的那四格
├── mindmap/ ├── mindmap/
│ ├── semantic.mmd # 心智圖:概念的長期關聯(Mermaid mindmap │ ├── semantic.mmd # 心智圖:概念的長期關聯(Mermaid mindmap
│ └── threads/*.mmd # 思維導圖:單一話題的推理鏈(Mermaid graph,短期) │ └── threads/*.mmd # 思維導圖:單一話題的推理鏈(Mermaid graph,短期)
├── relations/ ├── relations/
│ ├── graph.json # 人際關係圖(親近度/信任度/連線) │ ├── graph.json # 人際關係圖(親近度/信任度/連線/語氣規則/未修復的裂痕
│ └── graph.mmd # Mermaid 呈現(自動產生) │ └── graph.mmd # Mermaid 呈現(自動產生)
└── journal/YYYY-MM.jsonl # 原始逐字 + 情緒史(hook 自動寫) └── 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 味(刪掉公式化)正好相反。 跟講話規則在做的去 AI 味(刪掉公式化)正好相反。
@@ -330,6 +345,37 @@ X 超出基線 18 以上才開始抑制,滿檔時往 Y 的同向推力只剩 5
- **升格式**`migrate [--all] [--dry-run]` 就地補欄位並切分內文,冪等;bundle 版本從 1 升到 2, - **升格式**`migrate [--all] [--dry-run]` 就地補欄位並切分內文,冪等;bundle 版本從 1 升到 2,
`import` 吃得下舊的 v1,收完會自動跑一次 migration 補齊。 `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` ## 未完事項(`state/loops.json`
「被記住」的感覺幾乎不是來自長期記憶檢索——**檢索是被問了才想起來,懸著是沒人問也還在**。 「被記住」的感覺幾乎不是來自長期記憶檢索——**檢索是被問了才想起來,懸著是沒人問也還在**。
@@ -386,6 +432,50 @@ AI 最容易露餡的六件事:把推理過程講出來、一次講一大段
星號內嵌(`*別過頭*`)與第三人稱旁白(「她臉紅了」)仍然禁止——那是硬規則 8 沒有鬆的部分。 星號內嵌(`*別過頭*`)與第三人稱旁白(「她臉紅了」)仍然禁止——那是硬規則 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 <id> --room <room> # 乾淨對話
node scripts/persona.mjs room floor --session <id> --room <room> # 發言權:誰對誰在講、該誰接話 node scripts/persona.mjs room floor --session <id> --room <room> # 發言權:誰對誰在講、該誰接話
node scripts/persona.mjs export --session <id> --out lumi.json # 離線搬家(單檔) node scripts/persona.mjs export --session <id> --out lumi.json # 離線搬家(單檔)
node scripts/persona.mjs sync status --session <id> # Gitea 同步狀態 node scripts/persona.mjs sync status --session <id> # Gitea 同步狀態
node scripts/selftest.mjs # 602 項驗證:鎖、隔離、情緒(含疲勞/當日底色/抑制與慣性)、固化、回想強度與模糊態、格式遷移、未完事項、說話節制、劇場模式與發言權、匯出匯入、編號與 Gitea、找圖去背合成、高解析輸出與 Wiki 同步、預設人格、睡眠與 sleeper、hooks node scripts/persona.mjs chapter add --session <id> --name "在 SAO 那兩年" --body "<這段是什麼>"
node scripts/persona.mjs relation rift --session <id> --name <who> --about "<還沒和好的事>"
node scripts/persona.mjs voice add --session <id> --kind idiolect --facet 自稱 --value 我
node scripts/selftest.mjs # 773 項驗證:鎖、隔離、情緒(含疲勞/當日底色/抑制與慣性)、固化、回想強度與模糊態、格式遷移、未完事項、說話節制、標點與分段、語域、慣例、自傳章節、未修復裂痕、劇場模式與發言權、匯出匯入、編號與 Gitea、找圖去背合成、高解析輸出與 Wiki 同步、預設人格、睡眠與 sleeper、hooks
node scripts/expression-check.mjs # G2G3 表達驗收(人工看,不判定通過失敗)
``` ```
`selftest` 是機械檢查,它只能守住「不崩」。**「有沒有變成模仿腔」「是不是在用旁白演情緒」
機械上驗不出來**,所以另有 `expression-check.mjs`:它把四段情境(誇外表、被說在害羞、
被戳穿嘴硬、第三個人在場)× 三檔羞恥度(低/嘴硬/惱羞)的**注入內容**攤開,
附一張「人工看什麼」的清單,用完整的暫時 `PERSONA_HOME`,不動真的人格資料。
檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`CLI)、 檔案結構:`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`G2G3 表達驗收)
--- ---