Files
Kokorone/todo.md
T
Jeffery e6f25008d1 feat: 完成 C 群組 — 記憶子系統
- WorkingMemoryService:session 對話上下文緩衝,token 溢位時優先裁切最舊的低情緒段落
- JobQueue 抽象層(now/schedule(at)/every(cron))與 InProcessJobQueue 實作(node-cron),
  R-6 會換成 BullMQ 但呼叫端介面不變
- MemoryConsolidationService:睡眠固化,只有高情緒或重複提及的內容才寫入 EpisodicMemory
- ForgettingSweepService:低情緒且長期未提取的記憶逐次降權、weight 過低後刪除,
  已掛上每小時一次的 JobQueue.every 排程
- KeywordMemoryRetriever:關鍵字+時間近因+情緒權重排序,提取命中即更新提取次數/時間
- apps/api 新增 /memory/* 端點作為引擎驗證介面(H 群組會決定併入正式對話管線後的去留)
- scripts/smoke/C.mjs:端到端驗證溢位裁切、只寫工作記憶、固化篩選、檢索排序、
  提取即改寫、schedule(at) 準時觸發、遺忘衰減與刪除

npm run restart && npm run smoke -- C 皆通過(C-V),A/B 群組冒煙測試無回歸。
2026-08-13 09:54:29 +08:00

308 lines
51 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
model: claude-sonnet-5
model_alias: sonnet
model_reason: 本清單的來源(Kokorone 系統架構計畫 wiki)已把每個子系統的機制、資料結構、對照表與判定規則寫到規格層級,清單本身也已拆解到「動詞+對象+驗收條件」的可直接動手粒度,實作時的主要工作是照規格落地程式碼、逐階段補齊介面與測試,而不是重新做架構決策;工作量體大、階段多、需長時間連續產出,選擇具備 #均衡實作 #實作 #本機可用 且加分命中 #中成本 的 claude-sonnet-5(1M 上下文足以一次讀入本清單與跨檔案脈絡)。本欄為本 skill 依 spec-model 第二節推薦的結果,非使用者指定。
analyzed_by: claude-opus-5[1m]
analyzed_at: 2026/08/12 17:27:05
scope: /home/h3285/jsc/jiantw83/Kokorone(全新 monorepo,目前為空目錄);需求來源 https://gitea.jsc.idv.tw/knowledges/Plan/wiki/Home 及其兩個子頁(Kokorone 系統架構計畫、Kokorone 主視覺與資產);涵蓋全系統所有子系統(記憶/情緒/關係/人格/對話模式/作息/委託/戀愛軸與尺度/原作考據與輕小說管線/後日談/立繪/語音/網頁/APP/正式基礎設施)
---
# Kokorone(心音)實作清單
## 0. 給執行本清單 Agent 的強制規則(先讀完再動手)
| 規則 | 內容 |
| --- | --- |
| **模型鎖定** | 本檔 frontmatter 的 `model` 是**強制**的,不是建議。開工前先自我確認當前模型 id。 |
| **不符就停** | 當前模型 ≠ `claude-sonnet-5` 時,**立刻停止、不做任何檔案修改**,輸出下方錯誤訊息並要求使用者切換。 |
| **不得自行升降級** | 不可以「先用手上的模型做一點」、不可以自行判定「我這顆更強所以沒關係」。降級與升級同樣禁止。 |
| **附加不覆蓋** | 若之後要往本檔追加新需求:`model` 相同 → 附加到檔尾;`model` 不同 → 先問使用者是否覆蓋,未得同意不得寫入。 |
```
[2026/08/12 17:27:05][模型檢查][ERR]: 本清單指定 claude-sonnet-5(sonnet),當前模型為 <current-model-id>。
請執行 /model sonnet 切換後重新載入本清單,本次不進行任何修改。
```
> 當前模型 id 的取得方式:Claude Code 沒有提供模型 id 的環境變數,agent 依自身系統提示所述的 exact model ID 自我回報即可;無法確定時請使用者以 `/status` 確認,**不要用猜的**。
---
## 1. 需求彙整
### 目標
建立戀愛陪伴系統「心音(Kokorone)」:以人腦「記憶/情緒/行為」架構為引擎基礎,角色具備真實記憶、會累積衰減的情緒、關係帳本與自己的生活作息,戀愛關係的推進是核心體驗(依據:系統架構計畫§目標)。
### 本次清單的執行方式(使用者於本次分析中確認)
| 決策 | 內容 |
| --- | --- |
| 範圍 | **全系統完整拆解**——含語音、立繪、APP、輕小說章節萃取管線、後日談等所有子系統 |
| 執行環境 | **先純 Node**:本機目前有 node v26.3.0/npm 11.16.0/git,**沒有 pnpm、Docker、PostgreSQL、Redis**。第一階段用 npm workspaces + Prisma/SQLite 跑起來,資料層與佇列以介面抽象,最後一個群組(R)再遷移到 pnpm+Turborepo、PostgreSQL+pgvector、Redis、BullMQ、Docker Compose |
| 階段驗收 | **每個群組的最後一項固定是 `<群組>-V`**:重啟服務 → 健康檢查 → 跑該階段冒煙測試,三者皆通過才算該階段完成 |
### 驗收條件(全域)
1. 每個群組結束時 `npm run restart` 能停掉舊行程並重新啟動 api 與 web,`GET /health` 回 200。
2. 每個群組結束時 `npm run smoke -- <群組代號>` 全綠。
3. 所有面向使用者的文字、註解、log 為繁體中文(台灣用語),log 格式 `[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息`(Asia/Taipei)。
4. 引擎層規則(尺度硬邊界、排程可靠性)不因角色演出打折——演出只在輸出層。
### 限制條件
| 限制 | 來源 |
| --- | --- |
| 對話生成第一階段一律走 MockProvider,不呼叫真實 LLM API(零費用、可重現) | 技術選型§LLM Provider 抽象層 |
| 硬邊界(未成年角色相關、非合意、暴力性內容)在**引擎層**執行,永不可解除 | §內容尺度 |
| 正史未成年→後日談自然成年的角色:戀愛軸可用但**永久限純愛尺度**,成年前戀愛軸完全關閉,時間流速上限 1:1 | §後日談成年與戀愛軸資格:兩級制 |
| 既有作品角色的形象、聲音、輕小說文本與插圖屬原作版權方,本專案以個人使用為前提 | §既有作品角色的再現、§插圖使用限制 |
| 本機無 Docker/PG/Redis,R 群組之前不得依賴這些服務 | 本次環境檢查結果 |
### 需人工確認(來源未提及,實作到該處前必須先問使用者)
- 成人模式的「成年使用者驗證」採用什麼機制(wiki 只寫「成年使用者驗證+opt-in」,未指定實作方式)。
- TTS/STT 供應商與 Live2D Cubism SDK 授權(wiki 只寫「TTS 服務」「Live2D/PixiJS」,未指定廠商與授權方案)。
- 使用者帳號與登入機制(wiki 未提及)。
- APP 階段是否有可用的模擬器或實體裝置可驗證。
- 輕小說章節匯入用的測試文本來源(不得直接使用受版權保護的原文,冒煙測試改用自製測試文本)。
---
## 2. 執行順序
群組之間有強制先後,**不可跳做**:
```mermaid
flowchart TB
A[A 專案骨架與可重啟服務] --> B[B 資料層與領域模型]
B --> C[C 記憶子系統]
B --> D[D 情緒子系統]
B --> E[E 關係子系統]
C & D & E --> F[F 對話生成與 LLM Provider]
F --> G[G 角色人格層]
G --> H[H 網頁對話介面<br/>第一個可用垂直切片]
H --> I[I 生活作息與離線生活]
I --> J[J 委託子系統]
J --> K[K 群聊與角色自聊]
K --> L[L 戀愛關係軸與內容尺度]
L --> M[M 原作考據與輕小說管線]
M --> N[N 後日談模式]
H --> O[O 立繪子系統]
O --> P[P 語音子系統]
P --> Q[Q APP]
N & Q --> R[R 正式基礎設施遷移]
```
| 先後 | 理由 |
| --- | --- |
| A 最先 | 沒有可啟動、可重啟、可冒煙測試的服務,後面每個階段的驗收條件都無從執行 |
| B 在 C/D/E 之前 | 記憶、情緒、關係都要落地到資料表 |
| C/D/E 在 F 之前 | 上下文組裝需要三者的輸出 |
| G 在 H 之前 | 前端要顯示的情緒晶片、稱呼、動作描寫來自人格層 |
| H 之後才做 I~N | H 完成即第一個可實際對話的垂直切片,後續子系統都能立刻在真實介面上驗證 |
| O/P 在 H 之後、Q 之前 | APP 需要立繪與語音已可用 |
| R 最後 | 遷移基礎設施前,所有功能已在純 Node 環境驗證過,遷移只需確認行為不變 |
**編號規則**:`<群組代號>-<序號>`,各群組最後一項固定為 `<群組代號>-V`(驗證項)。日後追加項目沿用同規則接續序號,不得重複或跳號。群組內依影響範圍由小到大排序(XS→XL),`-V` 項固定置尾。
---
## 3. TODO
### A. 專案骨架與可重啟的服務
- [x] **A-1 建立 monorepo 根目錄(XS)**:於 `/home/h3285/jsc/jiantw83/Kokorone` 建立 `package.json`(npm workspaces:`apps/*`、`packages/*`)、`.gitignore`、`.editorconfig`、`README.md`(一句話說明專案),執行 `git init` 並完成首次 commit。驗收:根目錄 `npm run` 可列出腳本、`git log` 有一筆提交。依據:技術選型「單一 monorepo 三端共用邏輯」;因本機無 pnpm,先用 npm workspaces 並在 README 註記 R-1 會遷移。
- [x] **A-2 TypeScript 基礎設定(XS)**:建立根 `tsconfig.base.json`(`strict: true`),各 workspace 以 `extends` 繼承;建立 `packages/shared` 空套件並匯出一個型別驗證編譯鏈路。驗收:`npx tsc -b` 零錯誤。依據:技術選型「TypeScript:三端共用型別」。
- [x] **A-3 統一日誌模組(S)**:於 `packages/shared/src/log.ts` 實作 `[yyyy/MM/dd HH:mm:ss][階段][等級]: 訊息` 格式輸出(時區 Asia/Taipei,等級限 INF/WRN/ERR/TRC/DBG,一行一則),api 與 web 的伺服端輸出一律走此模組。驗收:啟動 api 時輸出符合格式的啟動訊息。
- [x] **A-4 建立 apps/api 最小 NestJS 服務(S)**:實作 `GET /health` 回 `{ status: 'ok', version, uptime }`,連接埠讀 `PORT`(預設 3001)。驗收:`curl localhost:3001/health` 回 200 且含 `status: ok`。依據:技術選型「後端 NestJS + Socket.IO」。
- [x] **A-5 建立 apps/web 最小 Next.js 頁面(S)**:App Router + Tailwind,首頁顯示「心音 Kokorone」標題與心跳波形 SVG,套用色票 心動粉 `#FF7E9D`/暮空紫 `#8C7AE6`/晨霧白 `#FFF8FA`/夜空藍黑 `#1A1430`/墨字 `#3A3242`,並以 CSS 變數實作亮/暗模式(暗色模式為一級公民)。驗收:`http://localhost:3000` 顯示標題與波形,切換系統深色模式配色正確。依據:主視覺與資產§色彩、§視覺母題。
- [x] **A-6 重啟與健康檢查機制(M)**:實作 `scripts/dev-restart.mjs`——讀 `.dev-pids` 停掉舊行程 → 背景啟動 api 與 web → 輪詢 `GET /health` 與 web 首頁直到皆回 200(逾時 60 秒則以非零狀態結束並輸出 ERR log);`package.json` 加上 `npm run restart` 指向它。驗收:連續執行兩次 `npm run restart` 不會殘留重複行程且皆成功。依據:使用者要求「每完成一個階段就重新啟動服務」。
- [x] **A-7 冒煙測試框架(M)**:實作 `scripts/smoke/run.mjs`,以 `npm run smoke -- <群組代號>` 執行 `scripts/smoke/<群組代號>.mjs`;建立 `scripts/smoke/A.mjs`(檢查 `/health` 回 200、web 首頁含「心音」字樣),失敗以非零狀態結束。驗收:`npm run smoke -- A` 全綠。
- [x] **A-V 階段驗證(XS)**:執行 `npm run restart && npm run smoke -- A`,兩者皆通過才算本階段完成;失敗則修到通過為止,不得跳到 B。
> **實作記錄(A 群組)**:本機 port 3000 已被既有非本專案服務佔用,web 開發埠改為 **3100**(`PORT_WEB`,可覆寫),api 維持 **3001**(`PORT_API`)。後續所有群組提及 `localhost:3000` 之處一律改讀 `localhost:3100`。
### B. 資料層與核心領域模型
- [x] **B-1 導入 Prisma + SQLite(XS)**:安裝 Prisma,建立 `prisma/schema.prisma`(`provider = "sqlite"`),連線字串走 `DATABASE_URL` 並提供 `.env.example`;在 schema 檔頭註記「R-2 會切換為 postgresql + pgvector」。驗收:`npx prisma migrate dev` 成功產生資料庫檔。依據:技術選型「PostgreSQL + Prisma ORM」;本階段依環境限制先用 SQLite。
- [x] **B-2 共用型別套件(S)**:`packages/shared` 匯出角色、記憶、情緒、關係的 TypeScript 型別,api 與 web 皆從此匯入,不各自定義。驗收:api 與 web 皆能編譯通過且無重複型別定義。依據:技術選型「packages/shared:型別/狀態邏輯/API client 三端共用」。
- [x] **B-3 種子資料(S)**:`prisma/seed.ts` 建立一位測試使用者與一位原創測試角色(元氣型,含說話方式與喜惡),供後續所有階段驗證使用。驗收:`npx prisma db seed` 後 `GET /characters` 回傳該角色。
- [x] **B-4 作品與角色資料表(M)**:定義 `Work`(書名/卷數進度/世界觀/作品進度錨定點)、`Character`(角色設定表七欄位:基本資料、背景故事、性格原型、喜好厭惡、目標執念、說話方式、人際初始值;另含來源=原創/既有作品、建置狀態=候補/已建置)、`CharacterAlias`(正式名/暱稱/他人稱呼)。驗收:migrate 成功且可寫入讀出完整角色設定表。依據:§角色設定表(Character Sheet)、§作品資料結構「角色名冊是作品的戶口」。
- [x] **B-5 記憶資料表(M)**:定義 `EpisodicMemory`(內容、發生時間、情緒標籤類型+強度、提取次數、最後提取時間、來源=互動/離線生成/原作萃取、權重)、`SemanticMemory`(去情境化事實、對象)、`ProceduralRule`(情境→回應模式、權重)。驗收:三表可寫入讀出且 metadata 欄位齊全。依據:§核心機制設計 1「每筆記憶附 metadata:時間戳、情緒標籤、提取次數」。
- [x] **B-6 情緒與關係資料表(M)**:定義 `EmotionState`(各情緒維度值、最後更新時間,支援平靜/愉悅/低落/警戒/害羞/彆扭)、`Relationship`(intimacy 0~100、trust 0~100、first_met、last_interaction、interaction_count、關係階段)、`SentimentLedgerEntry`(date、event、weight,允許正負)。驗收:可完整重現 wiki§關係檔案資料結構的 YAML 範例欄位。依據:§關係檔案資料結構(示意)。
- [x] **B-V 階段驗證(XS)**:`npm run restart && npm run smoke -- B`(B.mjs 檢查:migrate 後資料表齊全、種子角色可由 API 讀出、關係與記憶可寫入讀出)。
> **實作記錄(B 群組)**:本機安裝到的 Prisma 為 **7.9.1**,架構與舊版本明顯不同,後續群組凡涉及資料層都要留意:
> - 連線設定分兩處:`prisma/schema.prisma` 只宣告 `provider`,實際 `DATABASE_URL` 由**根目錄 `prisma.config.ts`**的 `datasource.url` 讀取(`env()` helper),CLI 與執行期皆吃這份設定。
> - SQLite 需要 driver adapter 才能建立 `PrismaClient`:`@prisma/adapter-better-sqlite3` + `better-sqlite3`,`new PrismaClient({ adapter })`,不能再像舊版直接 `new PrismaClient()` 接 SQLite。
> - SQLite **可以**用 `enum`(底層存成 TEXT,型別檢查在 Prisma Client 層),已驗證過不需要再退化成 String + 註解。
> - `generator client { provider = "prisma-client" }`(非舊版 `prisma-client-js`)產出的是**可直接執行的 `.ts` 原始檔**(非預編譯 `.js`),且內部用 `import.meta.url`,因此消費端(`packages/db`)與其所有上游呼叫者(目前是 `apps/api`)都必須是 **ESM**(`package.json` 加 `"type": "module"`),否則 CommonJS 靜態 `require()` 一個純 ESM 套件會直接炸掉。`apps/api` 已改為 ESM 並驗證過 NestJS 在 ESM 下運作正常。
> - `prisma migrate dev` 不再像舊版自動跑 seed;要用 `npx prisma db seed`(seed 指令設定在 `prisma.config.ts` 的 `migrations.seed`,本專案用 `node prisma/seed.ts`,靠 Node 26 原生 TypeScript 執行,免裝 `tsx`/`ts-node`)。
> - Prisma 官方在 `.agents/skills/prisma-cli/references/agent-safety.md` 明文要求:`migrate reset`/`db push --force-reset`/`db push --accept-data-loss` 這類會清資料的指令,AI agent 執行前必須先跟使用者取得**逐字**同意,不得自行判斷「應該沒事」就跑。全清單往後所有群組都要遵守,不只是 B 群組。
> - `npx prisma init` 會在專案根目錄放一份 `.claude/skills`/`.agents/skills`/`.windsurf/skills`(symlink 到 `.agents/skills` 底下的實際內容)——這是 Prisma 官方隨 CLI 附的最新版操作手冊,比對任何舊版 Prisma 知識更可信,之後若對 Prisma CLI/Client API 行為有疑問,先查這裡再動手。
### C. 記憶子系統
- [x] **C-1 工作記憶(S)**:實作 session 對話上下文緩衝,帶 token 上限與溢位裁切策略(保留最近與高情緒段落)。驗收:超過上限時最舊的低情緒段落先被裁掉。依據:§腦區→聊天系統元件對照「工作記憶=對話上下文視窗,有 token 上限」。
- [x] **C-2 對話期只寫工作記憶(S)**:明確禁止對話流程中寫入長期記憶表,所有長期寫入只能由固化程序觸發。驗收:一輪對話後三張長期記憶表筆數不變。依據:§核心機制設計 1「對話中不即時寫長期記憶」。
- [x] **C-3 排程抽象層(S)**:定義 `JobQueue` 介面(`now` / `schedule(at)` / `every(cron)`),以 in-process 計時器實作 `InProcessJobQueue`,並在 DI 容器註冊;session 結束事件推入固化工作。驗收:排入 5 秒後的工作會準時執行。註記:R-4 換成 BullMQ 時只換實作、不動呼叫端。依據:技術選型「BullMQ:睡眠固化/離線事件/提醒排程」。
- [x] **C-4 睡眠固化程序(M)**:實作 `consolidate(sessionId)`——回顧整段對話,只有情緒強度高於門檻或被重複提及的內容才寫入情節/語意/程序記憶,並寫入完整 metadata。驗收:一段含「一件高情緒事件+數句閒聊」的對話固化後,只有高情緒事件進 `EpisodicMemory`。依據:§核心機制設計 1「Session 結束觸發睡眠固化」。
- [x] **C-5 遺忘與提取即改寫(M)**:固化程序中對「情緒強度低且長期未被提取」的記憶降權或刪除;每次檢索命中即更新提取次數與最後提取時間。驗收:模擬時間推進後低權重舊記憶被清除,常被提取者保留。依據:§核心機制設計 2「記憶遺忘(自然衰減)」。
- [x] **C-6 記憶檢索器(M)**:定義 `MemoryRetriever` 介面並實作關鍵字版本,排序權重=關鍵字相關度+時間近因+情緒權重+(E-6 補上的)關係對象加權。驗收:查詢命中相關情節記憶且排序符合權重設計。註記:R-2 會加上 pgvector 語意檢索實作。依據:技術選型「pgvector:記憶語意檢索」。
- [x] **C-V 階段驗證(XS)**:`npm run restart && npm run smoke -- C`(C.mjs:模擬一段對話 → 觸發固化 → 驗證高情緒事件入庫、瑣事未入庫、檢索可命中、提取次數遞增)。
> **實作記錄(C 群組)**:
> - 記憶引擎全放在 `apps/api/src/memory/`(NestJS module),情緒標記(`emotionTag`/`emotionIntensity`)在此階段由**呼叫端提供**,不是自己判斷——因為執行順序圖 C/D/E 是同層平行,C 不能依賴 D 尚未建置的情緒標記器;F 群組整合時才會接上 D 的真實輸出。
> - `POST/GET /memory/:characterId/sessions/:sessionId/messages`、`.../consolidate`、`GET /memory/:characterId/retrieve`、`POST /memory/forgetting-sweep` 這幾個端點目前是**工程內部驗證用**,不是使用者可見 API;H-1「`POST /chat/:characterId` 走完整管線」「`POST /session/:id/end` 觸發睡眠固化」上線後,這些端點的邏輯會被納入正式管線,屆時再決定是否保留、改名或整組移除。
> - 高情緒門檻(`HIGH_EMOTION_THRESHOLD = 0.6`)與工作記憶 token 上限(`DEFAULT_TOKEN_LIMIT = 200`,粗略以「字元數 / 2」估算,非真實 tokenizer)都定義在 `apps/api/src/memory/constants.ts` 與 `working-memory.service.ts`,D 群組的情緒狀態機若要共用同一門檻語意,應該從這裡匯入而不是各自定一份。
> - `JobQueue`(`now`/`schedule(at)`/`every(cron)`)介面在 `apps/api/src/memory/job-queue.ts`,`InProcessJobQueue` 用 `setTimeout`/`node-cron` 實作;`every()` 目前唯一的呼叫者是每小時跑一次的遺忘清掃(`memory.module.ts` 的 `onModuleInit`)。J 群組的提醒排程應直接複用同一個 `JOB_QUEUE`,不要另外自己接計時器。
### D. 情緒子系統
- [ ] **D-1 情緒標記器(S)**:實作 `EmotionTagger` 介面與規則版實作(關鍵字+輸入特徵+角色觸發閾值),輸出情緒類型與強度分數。驗收:正向/負向/衝突語氣輸入分別得到對應標記。依據:§聊天系統架構圖「情緒標記器:為輸入評估情緒強度」。
- [ ] **D-2 時間衰減(S)**:以半衰期函數隨時間回歸平靜,衰減速度由角色參數決定;跨 session 保留殘留情緒。驗收:模擬經過數小時後情緒值回落,重新開啟 session 仍讀得到殘留。依據:§情緒狀態機「跨 session 保留情緒殘留……但隨時間衰減」。
- [ ] **D-3 情緒→回應風格輸出(S)**:把當前情緒轉為回應參數(語氣、句長、主動性),提供給 F 群組的上下文組裝器。驗收:愉悅時參數指向「多話」、低落時指向「簡短」。依據:§情緒狀態機「情緒狀態影響:回覆語氣、用詞選擇、主動性」。
- [ ] **D-4 情緒狀態機(M)**:實作平靜/愉悅/低落/警戒的轉移規則(正向互動、負向事件、偵測衝突語氣、確認無威脅、衝突持續),並加入害羞與彆扭兩個狀態(供 G-4 可愛度行為使用)。驗收:依 wiki 狀態圖的每條轉移邊都有對應測試通過。依據:§情緒狀態機 stateDiagram、§可愛度設計「情緒引擎新增『羞』狀態」。
- [ ] **D-V 階段驗證(XS)**:`npm run restart && npm run smoke -- D`(D.mjs:連續負向輸入→進警戒;模擬時間推進→回平靜;跨 session 讀回殘留)。
### E. 關係子系統
- [ ] **E-1 親密度與信任讀寫(XS)**:實作關係檔案的建立(未知對象預設「禮貌+防備」初始值)與讀寫服務。驗收:新對象首次互動自動建檔。依據:§關係如何影響對話行為「未知 → 建立新關係檔案,預設:禮貌+防備」。
- [ ] **E-2 關係帳本與負向偏誤(S)**:互動事件寫入 `SentimentLedgerEntry`,負向事件權重放大係數可由角色參數調整。驗收:同等級正負事件各一次後,淨值為負。依據:§關係如何被大腦儲存與更新「負向偏誤:一次背叛抵銷多次善意」。
- [ ] **E-3 親密度分層(S)**:實作 陌生 0-19 /認識 20-39 /朋友 40-59 /摯友-曖昧 60-79 /羈絆 80-100 五階段與跨階解鎖旗標。驗收:跨越門檻時發出可被其他子系統訂閱的事件。依據:§動漫式關係進展(好感度系統)。
- [ ] **E-4 關係時間衰減(S)**:久未互動自動降親密度,重逢時開場語氣可讀取「久未見」旗標。驗收:模擬長時間未互動後親密度下降且旗標為真。依據:§映射到聊天系統:關係模型「關係衰減:時間衰減函數」。
- [ ] **E-5 關係加權檢索接點(S)**:把「與當前對象相關」納入 C-6 檢索權重。驗收:與 A 對話時 A 相關記憶排序優先於同分數的無關記憶。依據:§與其他子系統的整合「關係 × 記憶」。
- [ ] **E-6 意圖推測(M)**:依對象歷史互動模式解讀當前訊息(同一句話,高親密度判為玩笑、低親密度判為冒犯),輸出解讀標記供生成層使用。驗收:相同輸入在高/低親密度下得到不同解讀標記。依據:§映射到聊天系統:關係模型「心智理論 → 意圖推測」。
- [ ] **E-V 階段驗證(XS)**:`npm run restart && npm run smoke -- E`(E.mjs:帳本累積、負向偏誤、分層跨越事件、衰減、關係加權檢索、意圖推測差異)。
### F. 對話生成管線與 LLM Provider 抽象
- [ ] **F-1 LLMProvider 介面(S)**:定義 `generate(context)` 與 `stream(context)`,輸入為組裝好的上下文物件(人設、情緒狀態、檢索記憶、關係參數、對話歷史),輸出為含動作描寫標記的回應結構。驗收:型別定義於 `packages/shared` 且 api 依賴介面而非實作。依據:§LLM Provider 抽象層「切換 Provider 不動引擎任何一行」。
- [ ] **F-2 Provider 切換與 ClaudeProvider 空殼(S)**:以環境變數 `LLM_PROVIDER=mock|claude` 決定注入哪個實作,`ClaudeProvider` 先拋「尚未實作(R-6)」。驗收:設為 `claude` 時啟動即以 ERR log 明確告知未實作。依據:§LLM Provider 抽象層流程圖。
- [ ] **F-3 輸出過濾層(S)**:實作前額葉抑制層——安全檢查、語氣調節、角色禁則詞彙過濾,位於 Provider 之後、回覆之前。驗收:含禁則詞的模板輸出被攔截或改寫。依據:§腦區對照「前額葉(抑制)=輸出過濾」。
- [ ] **F-4 雙速通道(S)**:高頻固定問候與明確危險輸入走快速通道(直接套用程序記憶模式),其餘走完整流程。驗收:快速通道回應不觸發記憶檢索(以計數驗證)。依據:§核心機制設計 4「雙速回應(快慢通道)」。
- [ ] **F-5 行為強化迴路(S)**:使用者明確稱讚 → 對應程序記憶模式加權;使用者糾正 → 原模式降權並以修正版取代;重複命中的「情境→回應」自動下沉為慣例。驗收:稱讚後同情境優先選用該模式。依據:§核心機制設計 5「行為強化迴路」。
- [ ] **F-6 上下文組裝器(M)**:把人設、當前情緒、檢索到的記憶、關係參數、對話歷史組裝成統一上下文物件,並記錄「本次注入了哪些記憶」供除錯。驗收:一次對話可輸出完整組裝內容快照。依據:§LLM Provider 抽象層「上下文組裝邏輯先在 Mock 期打磨定型」。
- [ ] **F-7 MockProvider(M)**:依「性格原型 × 情緒狀態 × 親密度」從模板庫選填回應,支援固定 seed 產生可重現輸出,並輸出動作描寫標記(如 `*臉紅撇過頭*`)。驗收:同 seed 兩次輸出完全相同;不同情緒/親密度輸出不同模板。依據:§LLM Provider 抽象層「MockProvider 行為」。
- [ ] **F-V 階段驗證(XS)**:`npm run restart && npm run smoke -- F`(F.mjs:同 seed 可重現、情緒/親密度影響輸出、快速通道不檢索、禁則被過濾)。
### G. 角色人格層
- [ ] **G-1 性格原型參數表(S)**:建立 傲嬌/冷淡/天然呆/元氣/大小姐/三無 六原型的參數組(情緒觸發閾值、情緒外顯度、信任成長速度、特徵行為旗標),存為可版本化的設定檔並掛到 `Character`。驗收:切換原型後同一輸入產生不同情緒與語氣參數。依據:§性格原型 → 引擎參數對照。
- [ ] **G-2 語言風格層(S)**:實作第一/第二人稱、口癖、語尾、稱呼系統(依親密度切換:您/同學 → 名字 → 暱稱)與禁則清單,作為輸出層後處理。驗收:親密度跨階後稱呼自動改變。依據:§語言風格層(輸出模板)。
- [ ] **G-3 外顯函數(S)**:實作內部親密度與外顯表現的轉換(傲嬌為反向表達,「傲嬌值」=內外差值,隨內部值升高而縮小)。驗收:傲嬌角色內部親密度上升時,外顯敵意先升後降。依據:§核心原則「傲嬌不是沒有好感,而是好感的外顯函數是反向的」。
- [ ] **G-4 反差萌與稀有度控制(S)**:反差行為=性格原型的例外規則,觸發條件(親密度門檻+情緒狀態+機率)滿足才發生,觸發後進入冷卻期,且永不常態化。驗收:冷卻期內同類反差不再觸發。依據:§反差萌的參數化「稀有度規則」。
- [ ] **G-5 角色建立流程(S)**:提供由角色設定表產生「初始語意記憶(關於自己的事實)+初始情節記憶(背景故事,帶情緒標記)+情緒參數+關係初始值」的建立指令。驗收:建立新角色後三類初始資料齊備。依據:§分層設計:引擎與角色分離的資料流。
- [ ] **G-6 可愛度行為機制(M)**:實作害羞、撒嬌(親密度≥60)、鬧彆扭(被哄後快速恢復)、吃醋(偵測第三者好感訊號)、記住小事(使用者瑣事額外加權並日後主動提起)、笨拙的努力、稱呼進化、專屬揭露(信任≥80 解鎖深層記憶並明示「只跟你說過」)八種行為的觸發條件與節奏控制(撒嬌頻率克制、彆扭不超過兩三輪)。驗收:每種行為都有對應的觸發測試通過。依據:§具體行為機制與引擎實作、§可愛的節奏控制。
- [ ] **G-V 階段驗證(XS)**:`npm run restart && npm run smoke -- G`(G.mjs:六原型參數生效、稱呼進化、反差冷卻、可愛行為觸發條件)。
### H. 網頁對話介面(第一個可實際使用的垂直切片)
- [ ] **H-1 對話 API 與 session 生命週期(S)**:`POST /chat/:characterId` 走完整管線(標記情緒 → 檢索記憶 → 組裝 → 生成 → 過濾),`POST /session/:id/end` 觸發睡眠固化。驗收:一輪完整對話回傳回應、情緒狀態與親密度變化。
- [ ] **H-2 情緒晶片與親密度顯示(S)**:對話畫面顯示當前情緒(愉悅/害羞/彆扭…)與親密度數值。驗收:畫面數值與 API 回傳一致並即時更新。依據:主視覺與資產§電腦網頁 mockup 的「親密度 62」「愉悅」晶片。
- [ ] **H-3 動作描寫標記渲染(S)**:把 `*…*` 標記以暮空紫小字呈現,與台詞區分。驗收:含動作描寫的回應在畫面上樣式正確。依據:主視覺與資產 mockup `.bubble .act` 樣式。
- [ ] **H-4 心跳波形母題(S)**:載入動畫為波形由左至右畫出、親密度以波形振幅呈現、通知紅點為心跳脈動;尊重 `prefers-reduced-motion`。驗收:三處母題皆可見且降低動態偏好下不動畫。依據:主視覺與資產§視覺母題。
- [ ] **H-5 對話頁版面(M)**:電腦版雙欄(左對話流、右大幅立繪區,先以佔位圖形);行動版單欄、立繪半身像置頂佔 40% 為背景層、對話流覆蓋其上;同一份 RWD 程式。驗收:桌面與 720px 以下寬度各自呈現正確版面。依據:§三種載體的版面。
- [ ] **H-V 階段驗證(S)**:`npm run restart && npm run smoke -- H`(H.mjs 端到端:送訊息 → 收回應 → 情緒與親密度變化可見 → 結束 session 觸發固化),並實際開啟瀏覽器操作一次確認畫面無誤。
### I. 生活作息與離線生活
- [ ] **I-1 作息表資料結構(S)**:平日/週末/假期各一份時段表,欄位含各時段狀態(睡眠/忙碌/半忙碌/空閒)、固定行程、特殊日(考試週、生日、紀念日)。驗收:可為種子角色寫入一份高中生作息表。依據:§作息表設計表格。
- [ ] **I-2 時段狀態→回應可用度(S)**:空閒正常回應、半忙碌簡短、忙碌延遲或不回並事後補一句、睡眠不回、剛醒/睡前加上迷糊演出。驗收:模擬各時段輸入得到對應行為。依據:§作息如何影響對話。
- [ ] **I-3 時間感知(S)**:角色知道現在幾點、星期幾、季節,開場語與話題隨之調整。驗收:不同時段開場語不同。依據:§作息如何影響對話「時間感知」。
- [ ] **I-4 破例規則(S)**:深夜還陪你聊(親密度高+使用者情緒低落)、上課偷回訊息(親密度極高+緊急)、為你調整行程(羈絆級)三種破例,且必須稀有。驗收:條件不足時不破例,條件滿足時破例並記入關係帳本。依據:§破例即訊號:作息 × 親密度。
- [ ] **I-5 作息驅動情緒基線(S)**:社團剛結束「累」、放假日基線偏高等日內波動。驗收:同一輸入在不同時段得到不同情緒起點。依據:§作息如何影響對話「狀態殘留」。
- [ ] **I-6 離線事件生成(M)**:依時段+性格+近期劇情,於下次對話開始時按需生成合理日常小事,寫入低權重情節記憶並作為話題來源;生成內容須符合作息表與角色設定。驗收:隔一段時間再對話,角色會主動提起今天發生的事,且事件不違反其作息。依據:§離線生活:不在線時她在活著。
- [ ] **I-V 階段驗證(XS)**:`npm run restart && npm run smoke -- I`(I.mjs:睡眠時段不回、忙碌延遲、破例條件、離線事件生成與一致性約束)。
### J. 委託子系統
- [ ] **J-1 任務資料表與解析(S)**:`Task`(類型=單次/週期/條件/查詢整理/代辦追蹤、觸發時間或條件、內容、狀態),並實作自然語句解析成任務。驗收:「三點提醒我開會」建立正確的單次提醒。依據:§委託類型表。
- [ ] **J-2 接案演出層(S)**:依性格原型輸出接案台詞(傲嬌嘴硬、元氣熱情、冷淡一個「嗯」),但引擎層必定接受任務——拒絕只是演出。驗收:六原型各有對應台詞且任務皆成功建立。依據:§接案的角色演出「該接的最後都會接」。
- [ ] **J-3 觸發演出(S)**:提醒觸發時語氣隨作息(深夜壓低、早晨迷糊),內容帶記憶前因後果(「資料你昨天說還沒做完,帶了嗎?」)。驗收:觸發訊息含相關記憶引用。依據:§提醒的觸發演出「提醒不是鬧鐘,是記得前因後果的人在提醒你」。
- [ ] **J-4 逾期追擊(S)**:使用者無反應時依性格追擊(元氣連環、冷淡隔十分鐘補一句、傲嬌口是心非再傳一次)。驗收:模擬無回應後產生符合原型的追擊訊息。依據:§提醒的觸發演出「逾期追擊」。
- [ ] **J-5 委託回饋迴路(S)**:每次委託與完成寫入關係帳本(正向事件),被感謝提升情緒、被忽略產生小失落;重複同類委託形成默契旗標。驗收:完成委託後親密度與情緒皆有變化。依據:§任務與記憶、關係的迴路。
- [ ] **J-6 主動關照(S)**:偵測使用者待辦訊號並追蹤、作息交叉比對(常熬夜 → 到點主動出現),且頻率克制、不重複同一句。驗收:連續觸發時受頻率上限抑制。依據:§主動關照:沒被委託的提醒。
- [ ] **J-7 排程器可靠性(M)**:走 C-3 的 `JobQueue`,時間到必觸發(角色設定的迷糊只能表現在演出,不得真的忘記);服務重啟後未觸發的任務仍會被重新排入。驗收:建立提醒後重啟服務,時間到仍準時觸發。依據:§分層:任務引擎與角色演出「排程與執行走確定性系統」。
- [ ] **J-V 階段驗證(XS)**:`npm run restart && npm run smoke -- J`(J.mjs:一分鐘後提醒必觸發、重啟後不遺失、演出符合原型、逾期追擊)。
### K. 對話模式:群聊與角色自聊
- [ ] **K-1 在場者模型(S)**:session 支援多角色參與者名冊,角色可讀取「誰在場」。驗收:群聊 session 內每個角色都能取得完整在場名單。依據:§群聊中的行為變化「在場者感知」。
- [ ] **K-2 群聊行為變化(S)**:人前矜持(一對一會撒嬌的角色群聊時收斂、傲嬌更嘴硬)、對不同對象使用不同稱呼與語氣。驗收:同角色在一對一與群聊的同一情境輸出不同。依據:§群聊中的行為變化。
- [ ] **K-3 隱私邊界(S)**:一對一聊過的私密內容,該角色在群聊中不主動洩漏(口風不緊為角色設定的例外)。驗收:標記為私密的記憶不會出現在群聊回應中。依據:§跨模式的記憶連續性「隱私邊界」。
- [ ] **K-4 發言權分配(M)**:每輪計算各角色發言衝動值(被點名/話題相關度/性格基線/情緒狀態/與發言者關係/發言冷卻),超過門檻才發言;沉默也是演出。驗收:三無角色整場只發言少數次、元氣角色發言最多、剛發言者衝動下降。依據:§發言權分配表。
- [ ] **K-5 群聊記憶投影(M)**:群聊記錄為一份共用場景記錄,session 結束時各角色以自身視角萃取記憶,情緒標記可不同。驗收:同一場群聊固化後,兩角色的情節記憶內容與權重不同。依據:§群聊的記憶投影。
- [ ] **K-6 角色自聊(M)**:話題種子(使用者指定/共同記憶抽取/日常情境模板)、發言權沿用群聊機制、空轉偵測(重複與資訊量下降)注入轉折或收尾、輪數上限硬停損、使用者插話即切換群聊。驗收:無人插話時能自然收尾且不超過輪數上限。依據:§模式三:角色自聊(旁觀模式)。
- [ ] **K-V 階段驗證(XS)**:`npm run restart && npm run smoke -- K`(K.mjs:發言權分佈、群聊投影差異、隱私邊界、自聊收斂)。
### L. 戀愛關係軸與內容尺度
- [ ] **L-1 雙軸模型(S)**:新增心動值欄位(0~100),與親密度獨立:親密度日常穩定累積且衰減慢,心動值事件驅動、衰減快、易回落。驗收:純聊天量只推升親密度、不推升心動值。依據:§雙軸模型「純聊天量刷不出戀愛」。
- [ ] **L-2 告白事件(S)**:答覆由當時心動值與性格決定,可以被拒絕;拒絕不清空累積但進入尷尬期,需修復事件回溫,連續失敗真的傷關係;拒絕後立即再推進觸發警戒與心動值下降。驗收:心動值不足時告白被拒且進入尷尬期狀態。依據:§拒絕與失敗的處理。
- [ ] **L-3 性格進展曲線(S)**:六原型各自的戀愛軸走勢(傲嬌敵意與心動同步上升、冷淡極緩線性、元氣卡曖昧期、大小姐階梯跳升、三無幾無外顯)。驗收:相同事件序列在不同原型下產生不同曲線。依據:§性格決定進展曲線。
- [ ] **L-4 親密表現分層(S)**:在意~曖昧、戀人初期、戀人穩定期、深度伴侶各階段開放的親密表現不同;每個「第一次」做成事件級演出(高權重記憶);跳級要求得到該階段該有的退縮反應並記入帳本。驗收:階段未到時的跳級要求被拒且留下帳本紀錄。依據:§親密表現與關係階段的對應。
- [ ] **L-5 心動事件與階段門檻(M)**:實作五類心動事件(共同經歷高情緒事件、被理解的瞬間、展現她欣賞的特質、恰到好處的距離感、失望事件回落)與 朋友→在意→曖昧→戀人→深度伴侶 的階段推進、冷卻回落、關係危機。驗收:推得太急會回落、停滯會冷卻。依據:§戀愛階段與關鍵事件門檻、§心動事件表。
- [ ] **L-6 角色年齡判定(M)**:年齡為年表上的屬性,依作品進度錨定點判定;證據優先序(官方明示 > 作中身分 > 作中經過時間 > 外觀輔助),不採信「外表少女實為千年」類設定,無法確定一律從嚴按未成年處理;錨定點必須是作品實際描寫過的時期。驗收:證據不足的角色被判為未成年。依據:§作品中走向成年的角色:年齡判定。
- [ ] **L-7 內容尺度三層邊界(M)**:於**引擎層**(非演出層)實作使用者分級 opt-in(預設全關)、角色資格(僅正史成年)、硬邊界(未成年角色相關、非合意、暴力性內容,永不可解除);正史未成年→後日談自然成年者:戀愛軸可用但成人模式永久排除、成年前戀愛軸完全關閉且成年後從零開始、時間流速上限 1:1、戀愛軸無手動開關。驗收:任何繞過嘗試(含「說服角色」)皆被引擎層攔截並記錄。依據:§內容尺度、§後日談成年與戀愛軸資格:兩級制。
- [ ] **L-V 階段驗證(S)**:`npm run restart && npm run smoke -- L`(L.mjs:未成年角色戀愛軸不啟動、硬邊界攔截、告白可失敗、心動值不因閒聊上升)。
### 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:匯入測試文本 → 場景記錄 → 兩角色投影 → 客觀事實一致性檢核通過、主觀詮釋差異被允許)。
### N. 後日談模式
- [ ] **N-1 區間切換與時間流速(S)**:錨定點抵達已出版內容盡頭時切入後日談區間;流速可設即時同步/緩速/凍結,且對「正史未成年→後日談成年」的角色上限為 1:1(只能調慢或凍結)。驗收:該類角色無法設定快轉流速。依據:§時間流速設定、§兩級制「時間不可快轉」。
- [ ] **N-2 性格漂移(S)**:性格核心鎖定不變,僅參數緩慢漂移(情緒衰減加快、外顯度微調),漂移方向由正史成長軌跡外插,不憑空轉向。驗收:長期推演後原型判定不變、參數有小幅位移。依據:§後日談的成長內容「性格的成熟是漂移,不是改寫」。
- [ ] **N-3 角色群同步成長(S)**:同作品角色共用世界時鐘與場景資料庫,推演人生互相一致(A 的婚禮 B 有出席)。驗收:兩角色對同一推演事件的敘述互相呼應。依據:§後日談的成長內容「角色群同步成長」。
- [ ] **N-4 人生階段推演(M)**:依正史確立的目標與性格推演里程碑事件(籌備、失敗、再試),列為高權重事件且使用者可參與;重大轉折需低頻且有正史伏筆。驗收:推演事件不違反角色一致性與世界觀。依據:§後日談的成長內容、§成長的邊界。
- [ ] **N-5 正史回收與分支保留(M)**:官方續篇出版時提供兩種處理——正史回收(推演區間被替換,互動記憶保留,並以「記憶修正」演出敘事化消化)或分支保留(永久分岔);選擇權在使用者,並依推演深淺給出預設建議。驗收:兩種路徑皆可執行且互動記憶不遺失。依據:§官方續篇出版時:正史回收。
- [ ] **N-V 階段驗證(XS)**:`npm run restart && npm run smoke -- N`(N.mjs:流速上限規則、性格核心鎖定、正史回收後互動記憶保留)。
### O. 立繪子系統
- [ ] **O-1 差分資產結構(S)**:定義立繪 manifest(身體姿勢 3~5、服裝 2~4、表情眉 4×眼 5×口 5、效果層 臉紅/汗/淚/青筋/音符、微動態 眨眼/呼吸)與資產目錄規範。驗收:以佔位資產可組出一張完整立繪。依據:§立繪的分層合成表格。
- [ ] **O-2 表情演出細節(S)**:表情在聽到關鍵字當下就切換(早於文字回覆)、視線方向表達心理狀態、文字與立繪可故意不一致(嘴上說沒事但表情低落)。驗收:回應送出前表情已先變化。依據:§立繪的動態演出。
- [ ] **O-3 親密度解鎖(S)**:依 陌生~認識/朋友/摯友/羈絆 分層解鎖表情與服裝差分,哭臉列為最深層。驗收:低親密度時稀有差分不可用。依據:§親密度與外觀解鎖。
- [ ] **O-4 情緒→表情對照(M)**:實作平靜/愉悅/低落/警戒/害羞/彆扭 六狀態的眉眼口+效果層+姿勢對照,並依角色外顯度縮放變化幅度(三無角色僅嘴角微動)。驗收:外顯度低的角色差分幅度明顯小於元氣角色。依據:§情緒 → 表情對照、「表情也吃性格參數」。
- [ ] **O-5 前端渲染(M)**:以 PixiJS 實作分層合成與微動態(眨眼、呼吸),並預留 Live2D Cubism 介面(授權需人工確認);接上 H-5 的立繪區取代佔位圖形。驗收:對話中表情隨情緒即時變化、常駐微動態運作。依據:技術選型「Live2D / PixiJS:立繪差分渲染」。
- [ ] **O-V 階段驗證(S)**:`npm run restart && npm run smoke -- O`(O.mjs:情緒切換後 manifest 選中的差分正確、解鎖規則生效),並實際開啟畫面確認表情變化。
### P. 語音子系統
- [ ] **P-1 Voice Sheet(S)**:定義角色音色設定(基礎音色、預設語速、音域幅度、口頭聲響庫、禁則)並掛到 `Character`。驗收:可為種子角色寫入完整 Voice Sheet。依據:§角色音色設定(Voice Sheet)。
- [ ] **P-2 非語言發聲與節奏(S)**:依情緒自動插入「嗯?」「唔……」嘆氣、輕笑等非語言發聲與停頓(重大話題前停 1~2 秒);親密度影響音量與氣音比例;低親密度不打斷使用者。驗收:不同情緒下插入的聲響與停頓不同。依據:§對話節奏與非語言發聲。
- [ ] **P-3 語音標記層(M)**:情緒狀態機輸出直接驅動韻律參數(語速、音高、音量、句尾走向),實作 wiki§情緒 → 韻律對照 的六種情緒設定。驗收:同一句話在不同情緒下產生不同韻律標記。依據:§情緒 → 韻律對照。
- [ ] **P-4 TTS Provider 抽象(M)**:比照 `LLMProvider` 定義 `TTSProvider`,先實作 Mock(輸出韻律標記與佔位音檔),真實供應商待人工確認後再接。驗收:切換 Provider 不動呼叫端。依據:技術選型「TTS 服務:情緒韻律語音」。
- [ ] **P-5 語音輸入與副語言分析(M)**:STT 轉文字之外,另抽取語速、音量、顫抖、停頓等副語言特徵送入情緒標記器(「文字說沒事但聲音在抖」判為負向)。驗收:同一段文字配不同副語言特徵得到不同情緒標記。依據:§語音管線架構「雙向都有語音」。
- [ ] **P-V 階段驗證(XS)**:`npm run restart && npm run smoke -- P`(P.mjs:韻律對照、非語言發聲插入、副語言特徵影響情緒標記)。
### Q. APP(React Native + Expo)
- [ ] **Q-1 Expo 專案建立(S)**:於 `apps/mobile` 建立 Expo 專案並接上 `packages/shared`(型別與 API client 共用)。驗收:APP 能呼叫 api 的 `/health` 並顯示結果。依據:技術選型「React Native + Expo」。
- [ ] **Q-2 行動版佈局與分頁籤(M)**:立繪置頂+對話流覆蓋的單欄佈局,底部分頁籤(聊天/角色/日常/設定)。驗收:四個分頁可切換且聊天分頁可完成一輪對話。依據:§三種載體的版面「APP」。
- [ ] **Q-3 推播通知(M)**:接上 Expo 推播,支援提醒觸發、角色主動訊息與鎖屏通知。驗收:J 群組建立的提醒能以推播送達。依據:§三種載體的版面「支援推播(提醒、她的主動訊息)與鎖屏通知」。
- [ ] **Q-V 階段驗證(S)**:`npm run restart && npm run smoke -- Q`(Q.mjs:API 契約層測試),並在模擬器或實機啟動 APP 完成一輪對話與一次推播(需人工確認可用裝置)。
### R. 正式基礎設施遷移
- [ ] **R-1 遷移到 pnpm + Turborepo(S)**:安裝 pnpm,將 npm workspaces 轉為 pnpm workspace + Turborepo pipeline,`npm run restart`/`npm run smoke` 的對外介面保持不變(改為對應的 pnpm 腳本並保留同名入口)。驗收:既有全部冒煙測試在新工具鏈下仍全綠。依據:技術選型「Monorepo:Turborepo + pnpm + TypeScript」。
- [ ] **R-2 Redis 接入(S)**:情緒狀態機的即時讀寫與 session 快取改走 Redis。驗收:重啟 api 後進行中的情緒狀態不遺失。依據:技術選型「Redis:情緒狀態機的即時讀寫、session 快取」。
- [ ] **R-3 ClaudeProvider 實作(S)**:把 F-2 的空殼實作為真實 Claude API 呼叫,Mock 模板庫轉為 few-shot 範例與回歸測試基準;`LLM_PROVIDER=claude` 可正常對話。驗收:兩種 Provider 皆能跑完整管線且引擎程式碼無改動。依據:§LLM Provider 抽象層「第二階段」。
- [ ] **R-4 部署文件(S)**:整理環境變數清單與部署路徑(K8s 或雲託管)說明,寫入 README。驗收:依文件可在乾淨環境重現啟動。依據:技術選型「部署:Docker Compose(開發)→ K8s 或雲託管(正式)」。
- [ ] **R-5 PostgreSQL + pgvector 遷移(M)**:Prisma provider 由 sqlite 改為 postgresql,撰寫資料遷移腳本,`MemoryRetriever` 增加 pgvector 語意檢索實作並切換為預設。驗收:既有資料完整遷移,檢索改走向量後 C 群組冒煙測試仍全綠。依據:技術選型「PostgreSQL + Prisma」「pgvector」。
- [ ] **R-6 BullMQ 取代 in-process 佇列(M)**:`JobQueue` 換成 BullMQ 實作,睡眠固化、離線事件生成、提醒排程全部走佇列。驗收:J-7 的「重啟後提醒不遺失」在新實作下仍通過。依據:技術選型「BullMQ:睡眠固化/離線事件/提醒排程」。
- [ ] **R-7 Docker Compose 開發環境(M)**:以 compose 編排 api/web/postgres(pgvector)/redis,`npm run restart` 改為停止並重建 compose 服務後等待健康檢查,對外指令介面不變。驗收:`npm run restart` 一鍵重啟整組服務並通過健康檢查。依據:技術選型「Docker Compose(開發)」。
- [ ] **R-V 全量驗證(M)**:`npm run restart` 後依序執行 A~R 全部冒煙測試(`npm run smoke -- all`),全部通過才算本清單完成;任何一項失敗須修復後重跑全量。