前一批留在工作區沒進版控的那份。內容是 persona-story 的階段清單 與四題拍板結果(語氣檔放 voice/、機械進 CLI、時間軸換算西元日期、 情緒基線差 10 以上要人看),還有踩過一次的坑:劇情時間不可以寫進 first_seen/last_seen,那兩欄是記憶的新鮮度時鐘。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 KiB
TODO:故事匯入(小說 → 人格記憶/語氣/情緒)
把小說變成一個人格的記憶,同時讓它從原作學語氣與情緒表達。
技能:/jsc-persona:persona-story(skills/persona-story/)。
開場的五題問答在 reference/interview.md,場景要抽什麼在 reference/extract.md。
機械那半是 persona.mjs 的 novel 與 voice 子指令。
三條界線先立,後面所有步驟都受它們約束:
- 視角:人格不在場、也沒人告訴他的事,最多進
canon或別人的關係欄,不能變成他的event。 - 原文:整本不入倉庫。只留摘要、他自己的台詞、以及可追回出處的章節標記。
- 個性:
SOUL.md不由流程自動改寫。匯入只出提案,使用者逐條核可才寫入。
階段 -1:實作前一定要先問使用者(四題,沒答案不要開工)
這四題會決定檔案放哪、介面長什麼樣,答錯要重做。問完拿到答案再進階段 0。
- Q1 語氣檔放在人格倉庫的哪一層?→ 新開
voice/(samples.md原句、reactions.md事件對反應) - Q2 匯入要做成 CLI 子指令,還是全部由 skill 編排?→ 混合:機械的進 CLI,判斷的留 skill
- Q3 同一部作品分多本匯入時,
first_seen的時間軸怎麼接續?→ 換算成西元日期 - Q4 情緒基線差值超過多少才要人看?→ 差 10 以上
階段 0:前置(做一次)
- 0.1 確認目標人格編號、作品名、章節檔清單、原文編碼
→ 不預先問,由 skill 在匯入的當下問(2026-08-03 使用者拍板)。
問法與順序寫在
skills/persona-story/reference/interview.md。 - 0.2 建正名表:角色的各種譯名/簡稱/原文名 → 關係節點的
name→ 從匯入的章節自動判斷候選,再給使用者確認(2026-08-03 使用者拍板,覆蓋原本的「問譯名版本」)。 問他是哪個譯本等於要他猜;他手上那批寫的是什麼字,讀一次就知道了。novel scan抽候選(對話歸屬、敬稱結尾、片假名、高頻詞),附出現次數與一兩行上下文, 並猜關係節點分四種信心度(exact/alias/fuzzy/unknown);novel name review/confirm --accept-exact/ignore做確認。names.json分map(正名)與ignore(確認過不是人名,例如地名與招式名)兩塊—— 已在其中之一的 token 下次掃不再出現,不然每加一章就要重看同一批。novel candidate add遇到about有未確認的 token 會擋下來。 - 0.3 定故事內時間軸的表示法 → 西元日期(Q3 拍板),另加
date_source標canon/derived/guess,見上面「換算日期不可以假裝是明寫的」那一節。 - 0.4 定顯著度配額 → 預設
90+ 最多 5 則、80-89 最多 20 則(novel init --quota可調)。 超過配額由novel merge從低分往下壓一級(90+ → 85、80-89 → 75)並回報壓了幾則。 - 0.5 定跳過紀錄檔 →
memory/import/<work-slug>/skipped.jsonl, 一行一章(chapter/reason/ts),由novel skip寫入。 - 0.6 備份人格倉庫現況(
SOUL.md、情緒基線、關係圖),匯入出錯要回得去 (2026-08-03 已有一份整包備份在 scratchpad;真的要匯之前再做一次新的。)
工作區長什麼樣(0.5 定案後的結果)
memory/import/<work-slug>/
├── work.json 書名、slug、建立時間、顯著度配額
├── names.json 正名表(譯名 → 關係節點 name)
├── skipped.jsonl 跳過紀錄(章節、理由、時間)
└── candidates.jsonl 記憶候選(欄位見下面那張表)
階段 1:每一章(可平行,一章一把劍)
這一段是執行程序,不是要蓋的東西——真的匯一本書的時候才會逐項發生。 程序已經落地成技能:
skills/persona-story/SKILL.md第 ③ 節, 七項與知情層級的細節在reference/extract.md。下面的清單留著當對照表。
- 1.1 讀章節。只讀這一章,不要跨章推論
- 1.2 修亂碼 → 轉繁體中文 → 依正名表正名 (必須在 1.3 之前;亂碼或簡體字形會讓名字比對失敗)
- 1.3 判斷人格在場或知情。兩者皆否才跳過
- 1.4 跳過的章也要寫進跳過紀錄檔——靜默跳過會漏章,事後查不出來
- 1.5 把在場段落切成場景(換地點、換對手、換目的就切;一章通常 2 到 5 個)
- 1.6 每個場景抽七項:故事內時間、地點、在場人物、發生什麼、他做了什麼、他當下情緒、他說過的原句一到兩句
- 1.7 標知情層級:我做的/我看到的/別人告訴我的/事後才知道
- 1.8 轉第一人稱、壓成摘要。原文長段不留,只留他自己的台詞
- 1.9 新人物先建關係節點(
relation node),再寫記憶 (順序反了about對不到 id;name用會被說出口的完整稱呼,親密度查persona-relation/reference/closeness.md) - 1.10 輸出這一章的記憶候選 JSON(欄位見下)
- 1.11 抽語氣樣本:他自己講的原句,照抄不改寫,附對象與場合(戰鬥/日常/道別)
- 1.12 抽情緒反應對照:事件 → 他實際做了什麼(不要記「他感到什麼」)
- 1.13 若這章與現有
SOUL.md矛盾,開一則個性校正提案(只提案,不寫入)
記憶候選 JSON 欄位
name 檔名用的 slug
title 一句話標題
type canon(設定)/event(發生過)/insight(他自己的體悟)/promise(承諾)/boundary
about 人名,照抄關係節點的 name,一字不差(解析只認完全相等)
topics 2-4 個小寫關鍵詞,跨章沿用同一組
salience 照 0.4 的配額給,不要每則都高
emotion 那一刻的情緒(固化進欄位,不要重放去洗情緒基線)
first_seen 故事內時間,不是匯入日期(寫進長期記憶時會落在 `happened_at`,見下方警告)
source 書名+章節(可追回出處)
body 第一人稱摘要
quote 他自己說過的原句(可省)
know_level 1.7 的知情層級
date_source canon(原作明寫)/derived(推算)/guess(只抓大概) ← Q3 追加
寫進長期記憶時,body 會照 0.2.0 的格式切成「主旨/細節」兩層(衰減先吃細節),
並自動蓋上 strength/when/where/mood。date_source: guess 的記憶
在回答日期問題時走模糊態界線:可以說不確定、可以問,不可以斷言。
⚠ 劇情時間不可以寫進 first_seen/last_seen(踩過一次)
那兩欄是記憶的新鮮度時鐘:memoryStrength() 拿 last_seen 算衰減。
把劇情日期塞進去等於宣告「這則記憶上次被想起是兩年前」——實測 2024 年劇情的記憶
匯進去的那一秒就是模糊 0%,而且掉到 0% 之後 touchRecall 不再更新它,永遠回不來。
只有 canon 與 salience >= 80 靠保護清單活下來,40 到 79 分的 event 全部出生即死。
所以兩種語意分成兩組欄位:
| 欄位 | 意思 | 時間 |
|---|---|---|
first_seen/last_seen |
這則記憶什麼時候形成、上次什麼時候想起 | 真實時間(匯入當天) |
happened_at/happened_until |
故事裡這件事什麼時候發生 | 劇情時間(配 date_source) |
候選 JSON 照舊填 first_seen(那是抽場景時自然的講法),novel write 會把它搬到
happened_at,first_seen/last_seen 一律是匯入當天。
階段 2:全書收斂(章節都跑完之後)
- 2.1 跨章去重合併:同一件事只留一則;
salience取高、first_seen取最早、last_seen取最晚 - 2.2 依配額重新定標:90+ 只留真的改變他的那幾件
- 2.3 人工抽查十則,專看有沒有視角越界(他不該知道的事)
- 2.4 批次寫入:一則一檔
consolidate;情緒欄位只固化不重放 - 2.5 更新關係圖與心智圖
- 2.6 跑
relation doctor,確認about全部對得上(歧義與對不到的都要處理完) - 2.7 重建索引、同步 Gitea
- 2.8 另寫一則 meta 記憶:這批來自哪本書、哪些章、匯入日期
- 2.9 回歸驗收:問幾個只有書裡有的問題,看他答得出來、且不越界回答他不在場的事
階段 3:語氣與情緒(跟記憶分開走)
記憶是「發生過什麼」,語氣與情緒是「他怎麼反應」。分三層存,改動權限不同。
| 層 | 存哪 | 誰能改 | 來源 |
|---|---|---|---|
| 語氣樣本 | 人格倉庫的語氣檔(例如 voice/samples.md) |
匯入流程可直接寫 | 1.11 累積的原句 |
| 情緒反應規則 | 語氣檔的反應段 | 匯入流程可直接寫 | 1.12 的事件→反應對照 |
個性(SOUL.md 的 Core Truths/Boundaries/Vibe) |
SOUL.md |
只有使用者拍板才能改 | 1.13 的提案 |
3A 語氣(統計,可直接落地)
- 3A.1 句長分布 → 定一句話的上限與斷句時機
- 3A.2 高頻詞與口頭禪 → 定他會說的字
- 3A.3 他不會說的說法 → 禁語表(比正面規則有效)
- 3A.4 對不同人的稱呼與語氣差 → 對上語氣層(
bond× 親近度) - 3A.5 沉默與迴避的時機 → 什麼時候不回答比回答像他
- 3A.6 場合差(戰鬥/日常/道別)→ 同一個人在不同場合的句子形狀
- 3A.7 語氣檔寫入後,抽幾句實際對話對照,看有沒有變成模仿腔
3B 情緒
- 3B.1 每個場景只記「事件 → 他做了什麼」(情緒要演出來,不是旁白說明)
- 3B.2 全書跑完才用統計定情緒基線 (逐則套 delta 等於讓最後一章決定他的性格)
- 3B.3 統計每種情緒的觸發類型與強度上限 → 得到「什麼事讓他生氣而不是害怕」
- 3B.4 掩飾模式單獨記(嘴硬、換話題、講笑話)——這是人味的來源,不是情緒值
- 3B.5 基線寫入前跟現況比差值,超過門檻要人看
3C 個性校正
- 3C.1 提案要寫齊:哪一章、原文依據、與
SOUL.md哪一句衝突、建議怎麼改 - 3C.2 收斂階段把提案彙整成一份 diff
- 3C.3 使用者逐條核可
- 3C.4 核可過的才寫進
SOUL.md,並固化一則insight記錄為什麼改 - 3C.5 沒核可的提案留著不刪——下一本書可能出現同樣的證據
未決事項
四題已移到最前面的階段 -1,實作前先問使用者。這裡只留答案回填欄。
| # | 問題 | 使用者的答案(2026-08-03 拍板) |
|---|---|---|
| Q1 | 語氣檔放哪一層 | 新開 voice/:samples.md(他自己講過的原句,附對象與場合)、reactions.md(事件 → 他做了什麼)。匯入流程可直接寫這一層,SOUL.md 維持只有使用者能改——權限界線要有實體隔離,不靠自律 |
| Q2 | CLI 子指令或 skill 編排 | 混合。機械的進 CLI(正名、去重合併、配額重定標、批次寫入、跳過紀錄),判斷的留 skill(在場與知情、切場景、第一人稱摘要、個性校正提案)。跟 sleep 同一個分工方式 |
| Q3 | 分多本時時間軸怎麼接 | 換算成西元日期(first_seen: 2022-11-06)。跟現有的 strength/衰減公式同型,memoryStrength() 直接算得動。原作沒明寫的地方要換算,所以下面 0.3 多一條規則:換算來的日期一律標記,不可以看起來跟明寫的一樣 |
| Q4 | 情緒基線差值門檻 | 任一格差 10 以上就停下來給人看(3B.5)。基線是氣質,改了等於換一個人 |
依 Q3 追加的規則(換算日期不可以假裝是明寫的)
換算成西元日期換來一個新風險:猜出來的日期看起來跟原作明寫的一模一樣。
所以 first_seen 之外要多一個欄位記來源:
date_source |
意思 |
|---|---|
canon |
原作明寫的日期(SAO 開服 2022-11-06 這種) |
derived |
由明寫的日期推算(第 74 層攻略是開服後第幾天) |
guess |
只能抓大概(某卷「大約半年後」) |
guess 的記憶在回答日期類問題時不可以斷言——這剛好接上 0.2.0 的模糊態界線:
可以說不確定、可以問,不可以斷言。