// Kokorone 資料模型 // R-2 會將 provider 由 sqlite 切換為 postgresql,並為 MemoryRetriever 加上 pgvector 語意檢索。 generator client { provider = "prisma-client" output = "../packages/db/generated" } datasource db { provider = "sqlite" } enum CharacterSource { ORIGINAL // 原創 EXISTING_WORK // 既有作品 } enum CharacterBuildStatus { CANDIDATE // 候補 BUILT // 已建置 } enum MemorySource { INTERACTION // 互動 OFFLINE_GENERATED // 離線生成 SOURCE_EXTRACTION // 原作萃取 CORRECTION // M-10 回溯修正:發現記錯了,以「想起來其實不是這樣」的新事件消化,不竄改既有的 INTERACTION 記憶 } // 情緒標籤類型:對應情緒子系統六種狀態(見 D 群組) enum EmotionTag { CALM // 平靜 JOY // 愉悅 SAD // 低落 ALERT // 警戒 SHY // 害羞 GRUMPY // 彆扭 } enum RelationshipStage { STRANGER // 陌生 0-19 ACQUAINTANCE // 認識 20-39 FRIEND // 朋友 40-59 CLOSE_AMBIGUOUS // 摯友-曖昧 60-79 BONDED // 羈絆 80-100 } // L-5 戀愛階段:與上面的 RelationshipStage(友誼軸分層)是完全獨立的兩套狀態機。 enum RomanceStage { NONE // 戀愛軸未啟動(朋友) INTERESTED // 在意 AMBIGUOUS // 曖昧 PARTNER // 戀人 DEEP_PARTNER // 深度伴侶 CRISIS // 關係危機(戀人階段的重大失望,需修復事件) } // L-6 角色年齡證據:證據強度依序為 官方明示(決定性)>作中身分/作中經過時間(強)>外觀與社會呈現(僅輔助, // 不可單獨採信為成年)。isEpilogue 標記此證據屬於後日談推演區間、還是正史(已出版內容錨定點內)。 enum CharacterAgeEvidenceType { OFFICIAL_STATED IN_STORY_STATUS ELAPSED_TIME APPEARANCE } // L-5 心動事件類型與 L-2/L-4 的告白/里程碑事件,統一記在同一張表,供曲線與稀有度判斷共用。 enum RomanceEventType { SHARED_HIGH_EMOTION_EXPERIENCE // 共同經歷高情緒事件(大幅上升) UNDERSTOOD_MOMENT // 被理解的瞬間(大幅上升) ADMIRED_TRAIT // 展現她欣賞的特質(中幅上升) PERFECT_DISTANCE // 恰到好處的距離感(緩升) DISAPPOINTMENT // 失望事件(回落) CONFESSION_ACCEPTED CONFESSION_REJECTED MILESTONE_REACHED MILESTONE_REJECTED } // I-1 週期表:平日/週末/假期各一份時段表 enum ScheduleDayType { WEEKDAY // 平日 WEEKEND // 週末 HOLIDAY // 假期/特殊日 } enum ScheduleSlotStatus { SLEEP // 睡眠 BUSY // 忙碌 SEMI_BUSY // 半忙碌 FREE // 空閒 } // J-1 委託類型 enum TaskType { ONE_TIME // 單次提醒 RECURRING // 週期提醒 CONDITIONAL // 條件提醒 QUERY_ORGANIZE // 查詢整理 TODO_TRACKING // 代辦追蹤 } enum TaskStatus { PENDING // 尚未觸發 TRIGGERED // 已觸發,等候使用者回應 COMPLETED // 已完成 FAILED // 執行失敗 CANCELLED // 已取消 } model User { id String @id @default(cuid()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt relationships Relationship[] relatedMemories EpisodicMemory[] tasks Task[] adultModeConsent AdultModeConsent? romanceEventLogs RomanceEventLog[] adultContentAttempts AdultContentAttemptLog[] @@map("users") } model Work { id String @id @default(cuid()) title String volumeProgress String? // 卷數進度 worldview String? // 世界觀 progressAnchor String? // 作品進度錨定點(M-9/N 群組使用;描述作品實際描寫過的時期,人類可讀) anchorStoryOrder Int @default(0) // M-9 錨定點的數值刀口:對應 Scene.storyOrder,之前是親身經歷,之後不存在 // N-1 後日談區間:錨定點抵達已出版內容盡頭後切入,時間繼續流動。 inEpilogue Boolean @default(false) epilogueStartStoryOrder Int? // 切入後日談當下的錨定點位置,供 N-2 性格漂移計算「已經推演多久」 timeFlowMode String @default("REALTIME") // FAST/REALTIME/SLOW/FROZEN(N-1;後日談成年角色不可設 FAST) branchedFromCanon Boolean @default(false) // N-5 分支保留:一旦分岔,之後官方新內容不再匯入這個作品實例 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt characters Character[] scenes Scene[] illustrations Illustration[] contradictions SceneContradiction[] @@map("works") } model Character { id String @id @default(cuid()) workId String? work Work? @relation(fields: [workId], references: [id], onDelete: SetNull) source CharacterSource buildStatus CharacterBuildStatus @default(CANDIDATE) // 角色設定表七欄位 basicInfo String // 基本資料:名字、年齡、外型、出身 backgroundStory String // 背景故事:成長經歷、重大事件 personalityArchetype String // 性格原型:傲嬌/冷淡/天然呆/元氣/大小姐/三無 likesDislikes String // 喜好/厭惡 goalsObsessions String // 目標/執念 speechStyle String // 說話方式:口癖、語尾詞、第一/第二人稱 initialRelationValue String? // 人際初始值:對誰親近、對誰有心結 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt aliases CharacterAlias[] episodicMemories EpisodicMemory[] semanticMemories SemanticMemory[] proceduralRules ProceduralRule[] emotionState EmotionState? relationships Relationship[] contrastTriggerLogs ContrastTriggerLog[] scheduleSlots ScheduleSlot[] specialDays SpecialDay[] scheduleExceptions ScheduleExceptionLog[] tasks Task[] proactiveCareLogs ProactiveCareLog[] relationshipsToOthers CharacterRelationship[] @relation("RelationshipFrom") relationshipsFromOthers CharacterRelationship[] @relation("RelationshipTo") ageEvidence CharacterAgeEvidence[] romanceEventLogs RomanceEventLog[] adultContentAttempts AdultContentAttemptLog[] scenePresences ScenePresence[] spokenLines SceneLine[] costumeCatalogEntries CostumeCatalogEntry[] poseVocabularyEntries PoseVocabularyEntry[] languageCorpusEntries LanguageCorpusEntry[] personalityTraitSnapshots PersonalityTraitSnapshot[] relationshipEventLogsFrom CharacterRelationshipEventLog[] @relation("EventLogFrom") relationshipEventLogsTo CharacterRelationshipEventLog[] @relation("EventLogTo") epilogueMilestoneLogs EpilogueMilestoneLog[] voiceSheet VoiceSheet? @@map("characters") } // P-1 角色音色設定:語音子系統的角色專屬設定,同 EmotionState 走 1:1 關聯。 model VoiceSheet { id String @id @default(cuid()) characterId String @unique character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) baseTimbre String // 基礎音色描述,例如「清亮少女音」「低沉御姐音」「軟糯蘿莉音」 baseRate Float @default(1.0) // 預設語速倍率:性格決定的基準(元氣快、冷淡慢) pitchRangeSemitones Float @default(4) // 音域幅度(半音數):語調起伏大小,元氣大、三無近乎單音 verbalTicsJson String @default("[]") // 口頭聲響庫(JSON 字串陣列):SQLite 不支援原生字串陣列欄位 forbiddenSoundsJson String @default("[]") // 禁則(JSON 字串陣列):此角色不會有的聲音表現 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("voice_sheets") } // L-6 角色年齡判定的原始證據登錄;由 AgeDeterminationService 依證據優先序算出年齡狀態,不直接存最終結論 // ——結論永遠是「依目前登錄的證據重新算出來」,避免證據更新後忘記同步結論欄位。 model CharacterAgeEvidence { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) evidenceType CharacterAgeEvidenceType impliesAdult Boolean isEpilogue Boolean @default(false) // true=後日談推演區間的證據,非正史(已出版內容) description String createdAt DateTime @default(now()) @@map("character_age_evidence") } // L-5 心動事件與 L-2/L-4 的告白/里程碑,統一記錄,供戀愛軸曲線計算、稀有度節流、里程碑追蹤共用。 model RomanceEventLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) eventType RomanceEventType delta Float @default(0) detail String? // 例如里程碑類型(HAND_HOLDING/HUG/FOREHEAD_KISS…) occurredAt DateTime @default(now()) @@map("romance_event_logs") } // L-7 使用者是否已完成成年驗證+明確 opt-in 開啟成人模式;預設全關,且僅影響「使用者分級」這一層邊界, // 角色資格與硬邊界仍需獨立通過才會真的放行(見 RomanceEligibilityService)。 model AdultModeConsent { id String @id @default(cuid()) userId String @unique user User @relation(fields: [userId], references: [id], onDelete: Cascade) verifiedAdult Boolean @default(false) optedIn Boolean @default(false) updatedAt DateTime @updatedAt @@map("adult_mode_consents") } // L-7 驗收:任何嘗試(無論是否放行)都留下記錄,證明繞過嘗試會被引擎層攔截並記錄,不會被悄悄放過或悄悄擋掉。 model AdultContentAttemptLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) allowed Boolean reason String attemptedAt DateTime @default(now()) @@map("adult_content_attempt_logs") } // K-2/K-4 角色間關係:群聊中「角色間關係上場」與發言權分配的「與發言者的關係」都吃這張表。 // 有向邊(characterId 對 otherCharacterId 的觀感),故同作品兩角色互相的觀感可以不對稱。 model CharacterRelationship { id String @id @default(cuid()) characterId String character Character @relation("RelationshipFrom", fields: [characterId], references: [id], onDelete: Cascade) otherCharacterId String otherCharacter Character @relation("RelationshipTo", fields: [otherCharacterId], references: [id], onDelete: Cascade) affinity Int @default(50) // 0~100,越高越要好;50 為中性 dynamic String? // 互動基調描述性標籤,例如 loyal-friend/rival/protective/crush createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([characterId, otherCharacterId]) @@map("character_relationships") } // G-4 反差萌稀有度控制:記錄每次反差行為觸發的時間,供冷卻期判定。 model ContrastTriggerLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) triggeredAt DateTime @default(now()) @@map("contrast_trigger_logs") } // I-1 作息表:平日/週末/假期各一份時段表,每個時段一列。 model ScheduleSlot { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) dayType ScheduleDayType startMinute Int // 當天 00:00 起算的分鐘數(0~1440),跨午夜的時段請拆成兩列 endMinute Int status ScheduleSlotStatus label String? // 固定行程名稱,例如「社團活動」 @@map("schedule_slots") } // I-1 特殊日:考試週、生日、原作紀念日——當天整天視為 HOLIDAY 作息。 model SpecialDay { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) date DateTime // 只使用日期部分(比對年/月/日) label String @@map("special_days") } // I-4 破例規則:記錄每種破例最後一次觸發的時間,供稀有度冷卻判定。 model ScheduleExceptionLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) exceptionType String // LATE_NIGHT_CHAT/SNEAK_REPLY_IN_CLASS/ADJUST_SCHEDULE triggeredAt DateTime @default(now()) @@map("schedule_exception_logs") } model CharacterAlias { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) formalName String // 正式名 nickname String? // 暱稱 calledByOthers String? // 他人稱呼(作品中其他角色如何稱呼) createdAt DateTime @default(now()) @@map("character_aliases") } model EpisodicMemory { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) content String occurredAt DateTime // 發生時間 emotionTag EmotionTag emotionIntensity Float // 情緒強度 retrievalCount Int @default(0) // 提取次數 lastRetrievedAt DateTime? source MemorySource weight Float @default(1) // 權重(供遺忘/檢索排序使用) relatedUserId String? // 這段記憶與哪位使用者相關(E-5 關係加權檢索用) relatedUser User? @relation(fields: [relatedUserId], references: [id], onDelete: SetNull) isPrivate Boolean @default(false) // K-3 隱私邊界:一對一聊過的私密內容,群聊中不主動洩漏(口風不緊原型除外) // M-7/M-10 可追溯性:這筆記憶是否由某個場景投影而來。只有這裡非 null 的記錄會被「重新投影」刪除重建; // 使用者互動記憶(source=INTERACTION)永遠不會設這個欄位,因此重新投影永遠不會動到使用者互動記錄。 sourceSceneId String? sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) @@map("episodic_memories") } model SemanticMemory { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) fact String // 去情境化事實 about String // 對象(關於誰/什麼) sourceSceneId String? // M-4/M-7 可追溯性:由哪個場景萃取而來(新登場設定) sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) @@map("semantic_memories") } model ProceduralRule { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) situation String // 情境 responsePattern String // 回應模式 weight Float @default(1) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("procedural_rules") } model EmotionState { id String @id @default(cuid()) characterId String @unique character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) calm Float @default(100) // 平靜 joy Float @default(0) // 愉悅 sad Float @default(0) // 低落 alert Float @default(0) // 警戒 shy Float @default(0) // 害羞 grumpy Float @default(0) // 彆扭 updatedAt DateTime @updatedAt @@map("emotion_states") } model Relationship { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) intimacy Int @default(0) // 親密度 0~100(友誼軸:穩定累積、衰減慢) trust Int @default(0) // 信任度 0~100 firstMet DateTime? lastInteraction DateTime? interactionCount Int @default(0) stage RelationshipStage @default(STRANGER) // L-1 戀愛軸:與友誼軸(intimacy)獨立,事件驅動、衰減快、易回落。僅限成年角色資格生效後才會被寫入非零值。 romanceMeter Int @default(0) // 心動值 0~100 romanceStage RomanceStage @default(NONE) lastRomanceEventAt DateTime? awkwardUntil DateTime? // L-2 告白被拒後的尷尬期:互動變客氣,直到此時間點 consecutiveRejections Int @default(0) // L-2 連續告白失敗次數:累積到門檻會真的傷關係 partnerSince DateTime? // L-4 進入戀人階段的時間點,用於判斷戀人初期/穩定期 romanceEventsInStage Int @default(0) // L-4 進入目前階段後累積的正向心動事件數,同樣用於判斷穩定期 ledgerEntries SentimentLedgerEntry[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([characterId, userId]) @@map("relationships") } model SentimentLedgerEntry { id String @id @default(cuid()) relationshipId String relationship Relationship @relation(fields: [relationshipId], references: [id], onDelete: Cascade) date DateTime event String weight Float // 允許正負:正向事件為正值,負向事件為負值 createdAt DateTime @default(now()) @@map("sentiment_ledger_entries") } // J-1 委託:使用者請角色做的事。執行(觸發準時度)是引擎責任,演出(接案/觸發/追擊的台詞)是角色責任。 model Task { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) type TaskType content String // 委託內容(去除時間片語後的純委託描述,例如「開會」) rawText String // 使用者原始輸入,供回顧/記憶引用 triggerAt DateTime? // 單次提醒的觸發時間;週期提醒改用 cronExpression cronExpression String? // 週期提醒(node-cron 表達式) condition String? // 條件提醒的條件描述(MVP 為文字紀錄,不做真實環境條件監測) status TaskStatus @default(PENDING) triggeredAt DateTime? // 最近一次實際觸發的時間 triggerMessage String? // 最近一次觸發演出的訊息內容(供前端/測試輪詢) overdueNudgeCount Int @default(0) // J-4 逾期追擊次數 lastNudgeAt DateTime? completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("tasks") } // J-6 主動關照:記錄每次主動關心的觸發時間與內容,供稀有度節流與「不重複台詞」判定。 model ProactiveCareLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) userId String message String triggeredAt DateTime @default(now()) @@map("proactive_care_logs") } // M-1 插圖類型:對立繪的用途不同——封面定基礎氣質、彩頁給服裝差分、黑白插圖給表情/姿勢差分。 enum IllustrationType { COVER COLOR_PAGE BW_ILLUSTRATION } // M-1 插圖索引:不儲存受版權保護的原圖本身,只登錄「畫面描述」供服裝目錄/姿勢語彙/表情差分參考使用。 model Illustration { id String @id @default(cuid()) workId String work Work @relation(fields: [workId], references: [id], onDelete: Cascade) volume Int page Int? type IllustrationType sceneId String? // 對應場景(插圖與文本的交叉索引) scene Scene? @relation(fields: [sceneId], references: [id], onDelete: SetNull) description String // 畫面描述:表情/姿勢/服裝等文字記錄 createdAt DateTime @default(now()) costumeCatalogEntries CostumeCatalogEntry[] poseVocabularyEntries PoseVocabularyEntry[] @@map("illustrations") } // M-1 服裝目錄:逐卷登錄角色出現過的服裝,立繪服裝層只能從這個目錄取用,不可自創。 model CostumeCatalogEntry { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) illustrationId String? illustration Illustration? @relation(fields: [illustrationId], references: [id], onDelete: SetNull) label String // 例如「制服」「便服」「禮服」 createdAt DateTime @default(now()) @@map("costume_catalog_entries") } // M-1 姿勢語彙:繪師畫該角色時慣用的身體語言(總是抱著書、習慣性歪頭),納入立繪姿勢差分的官方認證來源。 model PoseVocabularyEntry { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) illustrationId String? illustration Illustration? @relation(fields: [illustrationId], references: [id], onDelete: SetNull) label String // 例如「總是抱著書」「習慣性歪頭」 createdAt DateTime @default(now()) @@map("pose_vocabulary_entries") } // M-4 場景內逐行的類型:對應五類萃取產物裡「事件」以外,需要逐行分類才能各自導向正確目的地的部分。 enum SceneLineType { DIALOGUE // 台詞 → 語言風格語料 ACTION // 動作描寫 → 事件的一部分 MONOLOGUE // 內心獨白/心理描寫 → 性格參數證據 NARRATION // 敘述/新登場設定 → 語意記憶 } enum SceneLineStatus { PENDING // 尚待歸屬判定 CONFIRMED // 信心分數達門檻且指紋一致,已寫入場景資料庫 QUARANTINED // 低信心或指紋矛盾,隔離、不寫入任何角色 } // M-3 共用場景資料庫:章節文本以場景為單位切分後的客觀記錄單位,角色中立,不含主觀詮釋。 // M-8/M-9 的共用年表以 storyOrder 排序——同作品所有場景共用一條數線,錨定點即在此數線上切一刀。 model Scene { id String @id @default(cuid()) workId String work Work @relation(fields: [workId], references: [id], onDelete: Cascade) volume Int chapterLabel String storyOrder Int // 全作品共用年表排序鍵 sourceType String @default("NOVEL") // M-8 矛盾裁決優先序用:NOVEL/OFFICIAL_SETTEI/ANIME_ADAPTATION summary String // 場景摘要(客觀記錄:在場者、誰說了哪句、誰做了什麼) rawText String // 匯入的原始場景文本(僅供測試/自製文本使用,不得為受版權保護原文) projected Boolean @default(false) // M-9:是否已投影進角色記憶(只有 storyOrder <= 錨定點的場景會被投影) createdAt DateTime @default(now()) participants ScenePresence[] lines SceneLine[] illustrations Illustration[] facts SceneFact[] episodicMemories EpisodicMemory[] semanticMemories SemanticMemory[] relationshipEventLogs CharacterRelationshipEventLog[] personalityTraitSnapshots PersonalityTraitSnapshot[] interactions SceneInteraction[] @@unique([workId, storyOrder]) @@map("scenes") } // M-3/M-5 場景名冊(硬約束):不在場的角色不可能說話,歸屬候選只限在場者。 model ScenePresence { id String @id @default(cuid()) sceneId String scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) @@unique([sceneId, characterId]) @@map("scene_presences") } // M-4/M-5/M-6 場景內逐行記錄:每一行都有獨立的歸屬信心分數與狀態,是隔離區機制運作的最小單位。 model SceneLine { id String @id @default(cuid()) sceneId String scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade) lineIndex Int lineType SceneLineType rawText String hasExplicitMarker Boolean @default(false) // 是否有「○○說道」等明示標記(決定性證據) markerCharacterId String? // 明示標記指向的角色(若有) speakerCharacterId String? // 目前歸屬(可能是明示、也可能是推論後確認的結果) speakerCharacter Character? @relation(fields: [speakerCharacterId], references: [id], onDelete: SetNull) confidence Float @default(0) status SceneLineStatus @default(PENDING) createdAt DateTime @default(now()) @@map("scene_lines") } // M-8 場景事實:客觀、可檢查的事實標記(例如天氣),跨場景比對同一事件標籤下的事實是否矛盾。 model SceneFact { id String @id @default(cuid()) sceneId String scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade) eventLabel String // 事件標籤:同一事件標籤下的事實理應一致(例如「秋季運動會」) factKey String // 例如「天氣」 factValue String // 例如「雨天」 createdAt DateTime @default(now()) @@map("scene_facts") } // M-4 互動的中立記錄:場景資料庫階段只記錄「發生了什麼互動」,尚未套用到任何角色的關係帳本—— // 套用(寫入 CharacterRelationshipEventLog)是投影階段的職責,一樣受 M-9 錨定點知識邊界節制。 model SceneInteraction { id String @id @default(cuid()) sceneId String scene Scene @relation(fields: [sceneId], references: [id], onDelete: Cascade) characterId String otherCharacterId String delta Float description String createdAt DateTime @default(now()) @@map("scene_interactions") } enum ContradictionStatus { PENDING_HUMAN_REVIEW RESOLVED } // M-8 矛盾偵測:不同場景描寫衝突時標記待人工確認;裁決優先序見 ContradictionDetectionService。 model SceneContradiction { id String @id @default(cuid()) workId String work Work @relation(fields: [workId], references: [id], onDelete: Cascade) eventLabel String factKey String description String status ContradictionStatus @default(PENDING_HUMAN_REVIEW) resolution String? createdAt DateTime @default(now()) @@map("scene_contradictions") } // M-4/M-6 語言風格語料:已確認歸屬的台詞樣本,供語言指紋統計(人稱/語尾/口癖頻率)使用。 model LanguageCorpusEntry { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) sceneId String? text String createdAt DateTime @default(now()) @@map("language_corpus_entries") } // M-8 性格演變追蹤:參數不是單一值而是帶時間戳的序列,依錨定點取用當時的性格參數。 model PersonalityTraitSnapshot { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) sourceSceneId String? // M-4/M-7 可追溯性:由哪個場景的獨白/心理描寫萃取而來 sourceScene Scene? @relation(fields: [sourceSceneId], references: [id], onDelete: SetNull) atStoryOrder Int // 對應的年表位置 note String // 描述性文字:這個時間點角色參數上的變化說明 expressivenessOverride Float? // 可選:覆寫 G-1 外顯度參數,模擬成長軌跡 createdAt DateTime @default(now()) @@map("personality_trait_snapshots") } // M-4 互動→關係帳本(角色對角色版):每次原作互動評正負向權重,累積出她對每個角色的信任/親密度, // 可追溯到來源場景;同一事件寫入雙方視角,權重可以不對稱。 model CharacterRelationshipEventLog { id String @id @default(cuid()) characterId String character Character @relation("EventLogFrom", fields: [characterId], references: [id], onDelete: Cascade) otherCharacterId String otherCharacter Character @relation("EventLogTo", fields: [otherCharacterId], references: [id], onDelete: Cascade) sceneId String? scene Scene? @relation(fields: [sceneId], references: [id], onDelete: SetNull) delta Float description String createdAt DateTime @default(now()) @@map("character_relationship_event_logs") } // N-4 人生階段推演:記錄已生成過的里程碑,供頻率節流(低頻)與避免重複生成同一件人生大事。 model EpilogueMilestoneLog { id String @id @default(cuid()) characterId String character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) storyOrder Int label String createdAt DateTime @default(now()) @@map("epilogue_milestone_logs") }