feat: 完成 M 群組 — 既有作品考據與輕小說章節管線

共用場景資料庫(Scene/ScenePresence/SceneLine/SceneFact/SceneInteraction,
角色中立客觀記錄)與投影(ProjectionService 寫入 EpisodicMemory/
SemanticMemory/PersonalityTraitSnapshot/CharacterRelationshipEventLog,
逐一可追溯到來源場景)完全分層。歸屬信心分數(明示標記決定性、語言指紋
從已歸屬語料統計、場景名冊硬約束、對話輪替)與隔離區/指紋冷啟動(低信心
或樣本不足一律隔離,指紋精煉後可重新嘗試釋放)。多角色投影與修正(改一處
場景資料庫、reprojectScene 同步更新所有受影響角色,使用者互動記憶因為
從不帶 sourceSceneId 而結構性地不受影響,發現得晚的歸屬錯誤額外記一筆
CORRECTION 事件)。作品名冊建置門檻(達標自動建置,門檻檢查排在投影完成
之後才做,避免用到投影前的舊統計)。錨定點知識邊界(雙向:前進載入、
後退遺忘)與矛盾偵測(同優先序來源待人工確認,跨優先序可依小說原文>
官方設定集>動畫改編自動裁決)。插圖索引與衍生服裝目錄/姿勢語彙表。

刻意簡化:章節→場景切分與互動正負權重皆採結構化輸入(不做自動 NLP
場景邊界偵測與情感分析),比照 F-7 MockProvider 的做法——機制先做對,
之後有真實 NLP/LLM 能力再替換輸入來源。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-08-13 16:58:04 +08:00
co-authored by Claude Sonnet 5
parent cd948f0026
commit c66799ee7c
21 changed files with 1747 additions and 20 deletions
+22 -11
View File
@@ -328,17 +328,28 @@ flowchart TB
### M. 既有作品考據與輕小說章節管線
- [ ] **M-1 插圖索引(S)**:登錄插圖(卷數/頁碼/對應場景/類型=封面/彩頁/黑白),並衍生服裝目錄與姿勢語彙表;立繪服裝層只能從目錄取用、不自創。驗收:可由事件查到對應插圖,服裝目錄可列出。依據:§插圖作為立繪參考、§插圖與文本的交叉索引。
- [ ] **M-2 作品名冊與建置門檻(S)**:角色名冊登錄正式名/暱稱/他人稱呼,新角色先進候補狀態,達門檻(出場場景數、具名台詞數、與主要角色實質互動)才正式建置。驗收:路人角色維持候補、主要角色達標後自動建置。依據:§章節匯入流程:強化或新建、§建置門檻。
- [ ] **M-3 章節匯入與場景切分(M)**:匯入章節文本,以場景為單位切分並寫入**共用場景資料庫**(客觀記錄:在場者、誰說了哪句、誰做了什麼,不含主觀詮釋)。驗收:以自製測試文本匯入後可列出場景與在場者。依據:§共用場景資料庫:處理一次,各自投影。註記:測試文本不得使用受版權保護的原文(需人工確認來源)。
- [ ] **M-4 逐場景萃取(M)**:萃取事件、目標角色台詞、內心獨白/心理描寫、與他人的互動、新登場設定五類產物。驗收:五類產物各自入庫且可追溯到來源場景。依據:§逐章處理管線。
- [ ] **M-5 歸屬信心分數(M)**:每句台詞/動作的歸屬附信心分數,證據強度依序為 明示標記(決定性)>語言指紋(強)>場景名冊(硬約束:不在場者不可能說話)>對話輪替(中)>內容合理性(中)。驗收:無標記對話能給出帶信心分數的歸屬。依據:§防線一:歸屬時的多重證據與信心分數。
- [ ] **M-6 隔離區與指紋冷啟動(M)**:信心低於門檻或與既有語言指紋矛盾者一律進隔離區、不寫入任何角色,累積成待裁決清單;前幾章先用有明示標記的台詞建立指紋基準再回頭處理無標記對話。驗收:低信心台詞不污染角色語料,裁決後可釋放入庫。依據:§防線二:寫入前的交叉驗證。
- [ ] **M-7 多角色投影(M)**:由場景資料庫投影出各角色的情節記憶(以她的視角改寫、她不在場的不記得、她誤解的按她以為的版本存)、語料與關係帳本事件;修正只改場景資料庫一處後重新投影。驗收:修正一句台詞歸屬後,受影響的多個角色資料同步更新。依據:§共用場景資料庫、§各類萃取物的處理規則。
- [ ] **M-8 跨章節整合(M)**:共用年表排序、依文本篇幅與心理描寫深度評情緒權重、矛盾偵測與裁決優先序(小說原文 > 官方設定集 > 動畫改編),無法裁決標「待人工確認」。驗收:故意置入的矛盾被偵測並標記。依據:§跨章節整合的關鍵。
- [ ] **M-9 錨定點與知識邊界(M)**:作品進度錨定點統一於作品層級,錨定點之前為角色的親身經歷、之後不存在;性格參數為帶時間戳的序列,依錨定點取值;同作品角色共用錨定點但年齡逐角色判定。驗收:錨定點前移後,角色不再知道後段劇情。依據:§跨章節整合「時間點錨定」、§錨定時間點的統一。
- [ ] **M-10 增量補全與回溯修正(M)**:新卷匯入時追加至年表尾端(錨定點之後者存為未啟用);新資訊可回溯修正舊參數;已上線角色發現歷史歸屬錯誤時,修正場景資料庫 → 重新投影 → 重算指紋與關係帳本,但**與使用者已發生的互動記憶不回溯竄改**,改以「想起來其實不是這樣」的新互動事件消化。驗收:修正後使用者互動記錄完整保留。依據:§增量補全、§誤判發現得晚怎麼辦。
- [ ] **M-V 階段驗證(S)**:`npm run restart && npm run smoke -- M`(M.mjs:匯入測試文本 → 場景記錄 → 兩角色投影 → 客觀事實一致性檢核通過、主觀詮釋差異被允許)。
- [x] **M-1 插圖索引(S)**:登錄插圖(卷數/頁碼/對應場景/類型=封面/彩頁/黑白),並衍生服裝目錄與姿勢語彙表;立繪服裝層只能從目錄取用、不自創。驗收:可由事件查到對應插圖,服裝目錄可列出。依據:§插圖作為立繪參考、§插圖與文本的交叉索引。
- [x] **M-2 作品名冊與建置門檻(S)**:角色名冊登錄正式名/暱稱/他人稱呼,新角色先進候補狀態,達門檻(出場場景數、具名台詞數、與主要角色實質互動)才正式建置。驗收:路人角色維持候補、主要角色達標後自動建置。依據:§章節匯入流程:強化或新建、§建置門檻。
- [x] **M-3 章節匯入與場景切分(M)**:匯入章節文本,以場景為單位切分並寫入**共用場景資料庫**(客觀記錄:在場者、誰說了哪句、誰做了什麼,不含主觀詮釋)。驗收:以自製測試文本匯入後可列出場景與在場者。依據:§共用場景資料庫:處理一次,各自投影。註記:測試文本不得使用受版權保護的原文(需人工確認來源)。
- [x] **M-4 逐場景萃取(M)**:萃取事件、目標角色台詞、內心獨白/心理描寫、與他人的互動、新登場設定五類產物。驗收:五類產物各自入庫且可追溯到來源場景。依據:§逐章處理管線。
- [x] **M-5 歸屬信心分數(M)**:每句台詞/動作的歸屬附信心分數,證據強度依序為 明示標記(決定性)>語言指紋(強)>場景名冊(硬約束:不在場者不可能說話)>對話輪替(中)>內容合理性(中)。驗收:無標記對話能給出帶信心分數的歸屬。依據:§防線一:歸屬時的多重證據與信心分數。
- [x] **M-6 隔離區與指紋冷啟動(M)**:信心低於門檻或與既有語言指紋矛盾者一律進隔離區、不寫入任何角色,累積成待裁決清單;前幾章先用有明示標記的台詞建立指紋基準再回頭處理無標記對話。驗收:低信心台詞不污染角色語料,裁決後可釋放入庫。依據:§防線二:寫入前的交叉驗證。
- [x] **M-7 多角色投影(M)**:由場景資料庫投影出各角色的情節記憶(以她的視角改寫、她不在場的不記得、她誤解的按她以為的版本存)、語料與關係帳本事件;修正只改場景資料庫一處後重新投影。驗收:修正一句台詞歸屬後,受影響的多個角色資料同步更新。依據:§共用場景資料庫、§各類萃取物的處理規則。
- [x] **M-8 跨章節整合(M)**:共用年表排序、依文本篇幅與心理描寫深度評情緒權重、矛盾偵測與裁決優先序(小說原文 > 官方設定集 > 動畫改編),無法裁決標「待人工確認」。驗收:故意置入的矛盾被偵測並標記。依據:§跨章節整合的關鍵。
- [x] **M-9 錨定點與知識邊界(M)**:作品進度錨定點統一於作品層級,錨定點之前為角色的親身經歷、之後不存在;性格參數為帶時間戳的序列,依錨定點取值;同作品角色共用錨定點但年齡逐角色判定。驗收:錨定點前移後,角色不再知道後段劇情。依據:§跨章節整合「時間點錨定」、§錨定時間點的統一。
- [x] **M-10 增量補全與回溯修正(M)**:新卷匯入時追加至年表尾端(錨定點之後者存為未啟用);新資訊可回溯修正舊參數;已上線角色發現歷史歸屬錯誤時,修正場景資料庫 → 重新投影 → 重算指紋與關係帳本,但**與使用者已發生的互動記憶不回溯竄改**,改以「想起來其實不是這樣」的新互動事件消化。驗收:修正後使用者互動記錄完整保留。依據:§增量補全、§誤判發現得晚怎麼辦。
- [x] **M-V 階段驗證(S)**:`npm run restart && npm run smoke -- M`(M.mjs:匯入測試文本 → 場景記錄 → 兩角色投影 → 客觀事實一致性檢核通過、主觀詮釋差異被允許)。
> **實作記錄(M 群組)**:
> - 新模組放在 `apps/api/src/canon/`。**M-3 場景切分的刻意簡化**:這裡不做自由文本的自動場景邊界偵測(真正的章節文本→場景切分需要相當於一個 NLP 分段模型),改成接受「呼叫端已經切好場景、逐行標好類型」的結構化輸入(`ImportSceneInput`:`lines: [{lineType, rawText, hasExplicitMarker?, markerCharacterId?}]`)——這個簡化與 F-7 `MockProvider` 代替真實 LLM 是同一種取捨:引擎的其他機制(信心分數、隔離區、投影、錨定點)都能在沒有真正 NLP 分段器的情況下完整測試,之後要接上真的自動分段/自動偵測明示標記,只需要在 `SceneImportService` 前面補一層前處理,不影響下游任何機制。**M.mjs 的測試文本全部是本次會話自製的短句,不是任何受版權保護的原作內容**,符合 M-3 驗收註記的要求。
> - **場景資料庫(`Scene`/`ScenePresence`/`SceneLine`/`SceneFact`/`SceneInteraction`)是角色中立的客觀記錄,投影(寫入 `EpisodicMemory`/`SemanticMemory`/`PersonalityTraitSnapshot`/`CharacterRelationshipEventLog`)是完全獨立的第二步**,兩者由 `ProjectionService` 銜接。這個分層是 M-7「修正只改一處、重新投影全部同步」與 M-9「知識邊界」共同的地基:`Scene.projected` 這個布林欄位記錄「這場戲的效果有沒有套用到角色資料」,`ProjectionService.reprojectScene` 永遠是「先刪掉這個場景先前投影出的所有資料(用 `sourceSceneId`/`sceneId` 精準篩選),再重新投影一次」,不是就地修改——這保證了「改一處、處處同步」不會有殘留的舊資料。
> - **M-9 錨定點知識邊界是這個分層的直接推論,幾乎不需要額外程式碼**:`SceneImportService` 只在 `scene.storyOrder <= work.anchorStoryOrder` 時才呼叫 `projectScene`;超前的場景就乖乖留在場景資料庫裡等錨定點推進。`AnchorService.setAnchor` 同時處理**前進**(把新進入範圍的場景投影進去)與**後退**(把新排除在範圍外、但先前已投影的場景資料清掉,`Scene.projected` 打回 `false`)——**這裡刻意支援雙向**,因為「使用者想避免爆雷、把錨定點設回比之前更早的章節」是真實會發生的操作,不是只有「追完新一季往前推」這種單向情境。
> - **M-10「與使用者互動記憶不回溯竄改」是結構性保證,不是靠邏輯判斷做到的**:`sourceSceneId`/`sceneId` 這類可追溯欄位只有場景投影會寫入,`source: "INTERACTION"` 的記憶(一對一聊天產生的)從來不會有這些欄位——因此 `reprojectScene`/`AnchorService` 的刪除查詢天然就篩不到使用者互動記憶,不需要額外寫「排除 INTERACTION」的特殊判斷,少一行防禦性程式碼就少一個之後可能漏寫的風險點。已上線角色發現歷史歸屬錯誤時(`CorrectionService.correctLineAttribution`),除了觸發重新投影,還會分別幫舊歸屬角色與新歸屬角色各記一筆 `source: "CORRECTION"` 的新事件(「想起來其實不是這樣」),**新增了 `MemorySource.CORRECTION` 這個列舉值**(`packages/shared` 與 Prisma schema 都要同步加,兩邊型別要對得上,這次建置時就因為漏了 `packages/shared` 那邊而卡到一次型別錯誤)。
> - **M-5/M-6 語言指紋是「從語料統計學出來的」,不是 G 群組那種原型模板**:`FingerprintService` 從 `LanguageCorpusEntry`(已確認歸屬的台詞)統計一組特徵詞(`SIGNAL_TOKENS`)的出現頻率,跟 G-2 `language-style.ts` 裡手寫的「這個原型絕不用驚嘆號」是兩套完全不同的機制、不要混淆:G 群組的是**設計時鎖定**的角色語言風格規則,M 群組的指紋是**從已歸屬文本統計出來**的、會隨語料增加而變準的證據來源。指紋樣本量門檻(`FINGERPRINT_MIN_SAMPLES=3`)很重要:樣本不足時一律回傳中性分數 0.5,不會因為剛好命中一個特徵詞就誤判——冷啟動階段(只有明示標記台詞、語料還很少)本來就該保守,這正是 M-6「先用明示標記建立指紋基準,再回頭處理無標記對話」的字面意思。
> - **中文分詞的經驗延續**:K 群組在 `speaking-right.service.ts` 踩過「切詞正規表達式沒把全角標點當分隔符,導致單字特徵詞比對不到」的坑(見 K 群組實作記錄);這次 `fingerprint.service.ts`/`attribution.service.ts` 一開始就用單字級的 `text.includes(token)` 子字串比對(完全不切詞),直接繞開同一類分詞陷阱,是刻意選的簡化方案。
> - **建置門檻檢查(M-2)的呼叫順序是個真的踩到的 bug**:一開始 `checkAndPromote` 排在 `projectScene` 之前呼叫,導致「與其他角色的實質互動」這個門檻條件永遠用「上一次投影完」的舊資料去判斷(這次匯入場景帶來的新互動還沒套用到 `CharacterRelationshipEventLog`,因為那是投影階段才寫入的)——結果角色明明已經達標卻沒被自動建置。修正成**投影完才檢查門檻**,`AnchorService.setAnchor` 推進錨定點時也一樣要在對應場景投影完之後才檢查——**這類「事件觸發的統計門檻檢查」永遠要排在所有會影響統計結果的寫入完成之後**,這條經驗值得日後任何群組寫類似的自動判定邏輯時留意。
> - **角色間互動的關係帳本刻意寫雙方視角、且權重可以不對稱**:`SceneImportService` 接受的 `interactions` 輸入是呼叫端(人工/未來的萃取流程)明確給的「誰對誰做了什麼、正負權重多少」,並沒有做真正的情感分析去推論互動的正負向——這是本群組另一個「先用結構化輸入代替真實 NLP」的簡化,跟場景切分是同一種取捨,理由也相同:機制先做對,之後有真的 NLP 能力再接上去替換輸入來源即可,不影響 `CharacterRelationshipEventLog`/建置門檻/關係一起成長這些下游機制。
> - **M-8 矛盾偵測只處理「同一事件標籤下、同一事實鍵卻有不同事實值」這種最容易驗證的矛盾形式**(`SceneFact`:`eventLabel`+`factKey`+`factValue`),裁決優先序(小說原文 > 官方設定集 > 動畫改編)靠 `Scene.sourceType` 判斷;同優先序來源互相矛盾(例如兩段小說原文互相衝突)沒有客觀依據可以自動選邊,標記 `PENDING_HUMAN_REVIEW`,不會硬選一個。**沒有做語意層級的矛盾偵測**(例如兩段描述用不同措辭講同一件事實但沒有明確標成同一個 `eventLabel`)——這需要真正的自然語言理解,超出本群組範疇,留給日後有實際 LLM 介入文本處理階段時再擴充。
### N. 後日談模式