feat: 完成 B 群組 — 資料層與核心領域模型

- 導入 Prisma 7.9.1 + SQLite(better-sqlite3 driver adapter),prisma.config.ts 管理連線設定
- 完整資料模型:User/Work/Character/CharacterAlias/EpisodicMemory/SemanticMemory/
  ProceduralRule/EmotionState/Relationship/SentimentLedgerEntry
- packages/db:Prisma Client 存取層(ESM,因 Prisma 7 產出的 generated client 僅支援 ESM)
- apps/api 隨之改為 ESM 以相容 packages/db
- packages/shared:新增角色/記憶/情緒/關係共用型別,api 與 web 皆從此匯入
- prisma/seed.ts:建立測試使用者與元氣型測試角色,含完整關係帳本與記憶種子資料
- apps/api:新增 GET /characters
- scripts/smoke/B.mjs:驗證資料表齊全、API 讀出種子角色、關係與記憶可寫入讀出
- 保留 Prisma 官方隨 CLI 附的 agent skill 文件(.agents/skills 等),供後續群組查閱

npm run restart && npm run smoke -- B 皆通過(B-V),A 群組冒煙測試無回歸。
This commit is contained in:
Jeffery
2026-08-13 09:43:15 +08:00
parent ae9c44dde7
commit f44200f543
124 changed files with 13566 additions and 48 deletions
+16 -7
View File
@@ -124,13 +124,22 @@ flowchart TB
### B. 資料層與核心領域模型
- [ ] **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。
- [ ] **B-2 共用型別套件(S)**:`packages/shared` 匯出角色、記憶、情緒、關係的 TypeScript 型別,api 與 web 皆從此匯入,不各自定義。驗收:api 與 web 皆能編譯通過且無重複型別定義。依據:技術選型「packages/shared:型別/狀態邏輯/API client 三端共用」。
- [ ] **B-3 種子資料(S)**:`prisma/seed.ts` 建立一位測試使用者與一位原創測試角色(元氣型,含說話方式與喜惡),供後續所有階段驗證使用。驗收:`npx prisma db seed` 後 `GET /characters` 回傳該角色。
- [ ] **B-4 作品與角色資料表(M)**:定義 `Work`(書名/卷數進度/世界觀/作品進度錨定點)、`Character`(角色設定表七欄位:基本資料、背景故事、性格原型、喜好厭惡、目標執念、說話方式、人際初始值;另含來源=原創/既有作品、建置狀態=候補/已建置)、`CharacterAlias`(正式名/暱稱/他人稱呼)。驗收:migrate 成功且可寫入讀出完整角色設定表。依據:§角色設定表(Character Sheet)、§作品資料結構「角色名冊是作品的戶口」。
- [ ] **B-5 記憶資料表(M)**:定義 `EpisodicMemory`(內容、發生時間、情緒標籤類型+強度、提取次數、最後提取時間、來源=互動/離線生成/原作萃取、權重)、`SemanticMemory`(去情境化事實、對象)、`ProceduralRule`(情境→回應模式、權重)。驗收:三表可寫入讀出且 metadata 欄位齊全。依據:§核心機制設計 1「每筆記憶附 metadata:時間戳、情緒標籤、提取次數」。
- [ ] **B-6 情緒與關係資料表(M)**:定義 `EmotionState`(各情緒維度值、最後更新時間,支援平靜/愉悅/低落/警戒/害羞/彆扭)、`Relationship`(intimacy 0~100、trust 0~100、first_met、last_interaction、interaction_count、關係階段)、`SentimentLedgerEntry`(date、event、weight,允許正負)。驗收:可完整重現 wiki§關係檔案資料結構的 YAML 範例欄位。依據:§關係檔案資料結構(示意)。
- [ ] **B-V 階段驗證(XS)**:`npm run restart && npm run smoke -- B`(B.mjs 檢查:migrate 後資料表齊全、種子角色可由 API 讀出、關係與記憶可寫入讀出)。
- [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. 記憶子系統