# ペルソナカタログ システム&UI 構築計画書(別の Muse で作る場合) この計画書を自分の Muse に渡せば、ペルソナカタログ(DB+合成エンジン+閲覧UI)をゼロから構築できます。 個人情報・実在のペルソナ固有情報は含みません。登場する数値・形式はすべて「設計の既定値」です。 進め方の鉄則:**一気に全部を頼まない。段階ごとに「設計合意 → 実装 → テスト検証」を回す。** 各段階の指示文例はそのままコピーして使えます。 --- ## 1. 全体像 | 要素 | 役割 | |---|---| | カタログ(裏側) | ペルソナを部品化して管理するローカルDB+合成エンジン | | チャット(表側) | 合成結果を載せて実際に会話する舞台 | | 閲覧UI(表側) | カタログを検索・選択・プレビューする静的ページ | ### 絶対制約 - **データとコードはその環境内に留め、外部サービス・外部API・ホスティングに依存しない** - DBは **SQLite 単一ファイル**、実装は **Python 標準ライブラリのみ** - UIは **単一HTMLファイルの静的スナップショット**(実行時にDBを読まない。外部フォント・CDN・画像ファイルへのリクエストはゼロ) - 規模想定:ペルソナ数十〜数百件 --- ## 2. データモデル(4層) 1. **Identity核**:誰か(変わらない自己像:年齢・外見・経歴・世界の前提など)→ 合成結果の IDENTITY.md になる - **年齢は全ペルソナに成人であることを明記する**(数値でも「成人(種族名)」のような記述でもよい。後述のつまずきポイント参照) 2. **Soul核**:どういう性格か(変わらない性質・価値観)→ SOUL.md の土台 3. **関係性ステージ差分**:関係の段階で変わる項目だけ(距離感・呼び方・敬語・遠慮・許容範囲など)。 **必ず「核からの差分」で定義する**(前のステージからの差分で書かない) 4. **文体プロファイル**:表現だけの独立軸(人称・文長・会話比率・テンポ)。**内容(性格など)は書かない**。複数ペルソナで共有する部品 - キャラ固有の口調は文体ではなく核の属性(例:`speech_habits`)に書く ### 合成の優先順位 セッション内の個別指示 > identity×stage の例外(override)> ステージ差分 > Identity核/Soul核 > 文体 ### 関係性の持ち方 - 現在のステージは **1行の状態値(上書き)**。ステージ間の非線形な行き来(例:親密→険悪→親密)を許す - 遷移と出来事は **追記専用イベントログ** に残す。DBトリガーで UPDATE/DELETE を禁止し、SQLを直接叩いても書き換えられないようにする。履歴のある関係性は削除不可にする - partner は「相手の識別子」(原則 `user`)。**物語内での呼び名は partner に入れず、ステージ側の属性(例:`call_user`)に置く** - 同一ペルソナに並行する物語(継続性)が複数ある場合:核は1レコードで共有し、関係性を継続性ごとに分ける(partner を `user@継続性名` のようにスコープする) - イベント種別:`transition / promise / fight / reconcile / milestone / other` - イベント重要度:**3**=ステージを決める転機/**2**=注入対象のエピソード/**1**=記録専用の細部 ### 経緯圧縮(肥大化対策) - 元のイベント行は消さない。圧縮は注入側の別レイヤー(区間要約テーブル)に持ち、「畳んだイベントID範囲」を保持して二重畳み・重なりを防ぐ - 要約テキストは **カタログ自身では生成せず、外から渡す**(カタログはLLMを持たない) - 合成時の経緯注入の既定値:全体上限 **2000文字**/直近の未圧縮イベント **5件**/注入する重要度の下限 **2**/未圧縮が **20件** を超えたら圧縮推奨を表示 ### スキーマ(テーブル一覧) | テーブル | 役割 | |---|---| | `identities` | ペルソナ本体(slug・名前・属性JSON・本文・既定の文体) | | `identity_tags` | タグ(多対多) | | `style_profiles` | 文体プロファイル | | `stages` | ステージ定義(属性JSON=核基準の差分、並び順) | | `stage_overrides` | identity×stage の例外上書き | | `relationships` | 関係性(identity×partner×現在ステージ) | | `relationship_events` | 追記専用イベントログ | | `relationship_digests` | 経緯圧縮の区間要約 | | `sessions` | どのチャットがどの identity×stage×文体で動いているか | | `session_snapshots` | export 時の復元用スナップショット | 属性は JSON 列に格納し、合成はアプリ側でキー単位にマージする(null 値でキー削除)。 --- ## 3. 合成エンジンとインターフェース - ライブラリAPI+CLI(`python -m `)の両方を用意する - 主な操作:identity / stage / style / override / relationship の CRUD、transition(状態上書き+ログ追記を1トランザクション)、event 追加、検索(タグ・現在ステージ・文体・名前部分一致)、compose(合成) - 合成結果は **IDENTITY.md+SOUL.md 形式のテキスト**。SOUL側に Soul核+ステージ差分+文体をマージし、経緯(要約+重要イベント)を上限文字数内で差し込む - 出力形態は2つ: - **embed**:チャット内包用テキスト(ペルソナパック)。**通常運用はこちら** - **export**:アシスタント自身の IDENTITY.md/SOUL.md を差し替える単独専用形態。終了時にスナップショットから復元する - ガード:文体に内容キーを混ぜられない/参照中のステージは削除不可/イベントログは変更不可 ### 運用形態 - 基本は **サイドチャット+embed**(1ペルソナ=1チャット、並行運用できる。アシスタント自身の人格ファイルは触らない) - **メインチャット+export は特別なとき専用** - ステージ変化の反映は **自動にしない**。会話の後にユーザーが申告し、アシスタントが transition / event を記録する --- ## 4. 実装フェーズ計画 ### 段階0:設計合意 ``` チャット用ペルソナを部品化して管理する「ペルソナカタログ」を作りたいです。 - 4層モデル:Identity核(誰か)/Soul核(性格)/関係性ステージ差分(関係で変わる項目だけ)/文体プロファイル(表現のみ) - ステージ差分は前ステージ基準ではなく核基準で定義する - 現在のステージは1つの状態値、遷移と出来事は追記専用のイベントログに残す(非線形遷移を許す) - 合成結果は IDENTITY.md と SOUL.md の形式で出す - データとコードはこの環境内に留め、外部サービスに依存しないこと まず実装方式の候補(SQLite単一ファイル/ファイルベース等)を比較して提案してください。 ``` 受け入れ条件:方式の比較表と選定理由が文書化されること(推奨はSQLite単一ファイル:追記専用をDBトリガーで強制でき、状態更新とログ追記を原子的に行えるため)。 ### 段階1:v1実装 実装はコーディング担当(Claude Code等)にブリーフを書いて委託し、**テスト件数を指定して検証させる**のが確実です。 ``` ブリーフに従ってカタログを実装してください。要点: - CRUD(identity / stage / style / override / relationship / event log / session) - 検索(タグ・現在ステージ・文体・名前部分一致) - 合成エンジン(優先順位つきマージ。null値でキー削除。イベントは重要度つきで経緯として注入) - session export(ファイル差し替え用)と session embed(チャット内包用テキスト) - ガード:文体に内容キーを混ぜられない/履歴のある関係性と参照中のステージは削除不可/イベントログはSQL直叩きでも更新・削除できない - 検証用テストを書き、全件パスを確認して報告すること。特に「遷移の経路が違っても同じステージの合成結果が同じになること」「親密→険悪→親密で険悪の残り香が残らないこと」をテストに含めること ``` 受け入れ条件:テスト全件パス/サンプルデータ(架空のダミー数名)で compose の出力が目視確認できること。 ### 段階2:経緯圧縮の追加 ``` イベントログの肥大化に備えて経緯圧縮を追加してください。 - 元のイベント行は削除・変更しない。圧縮は注入側の別レイヤー(区間要約)で行う - 区間要約は「畳んだ範囲」を保持し、二重畳み込みと重なりを防ぐ - 要約テキストは引数で受け取る(カタログ自身はLLMを持たない) - 合成時の経緯は「区間要約+未圧縮の重要イベント」で、全体に文字数上限(既定2000字)を設ける - 未圧縮が閾値(既定20件)を超えたら圧縮推奨が分かる仕組み - 既存テストが全て通り続けること ``` ### 段階3:実データ投入の型を固める いきなり全員を自動移行せず、**まず1〜2人を手で書き、型を固めてから**広げます。 - 変わらない自己像・世界の前提 → Identity核。性格・価値観 → Soul核 - 関係で変わる項目だけ → ステージ差分。そのペルソナだけ外れる点 → override - イベント要約は1〜2文で「何がどう変わったか」だけを書く - 登録後は必ず合成プレビューを目視し、**出典(どのキーがどの層由来か)まで確認**する - 既存の人物メモからの移行は「下書き生成 → ユーザー確認・修正 → 登録」の半自動で。ペルソナでない人物は対象から外す ### 段階4:UI用データの書き出し - カタログから UI 用 JSON を生成するエクスポートスクリプトを作る(ペルソナ/関係性ごとの現在ステージ/経緯/合成済み IDENTITY・SOUL を含める) - **UIはこのJSONの静的スナップショット**であり、DBを直接読まない ### 段階5:UI本体(次節の仕様で) - 単一HTML(テンプレート)に JSON と写真マップを注入するビルドスクリプトを用意する - 完成した原稿HTMLをアーティファクト(web_static)として作成・更新する ### 段階6:仕上げ機能 - アクセスごとのシャッフル(§5.4) - ステージクリックでの呼び出し命令コピー(§5.3) --- ## 5. UI仕様 ### 5.1 画面構成 - **入場スプラッシュ**:屋号のネオン看板(タイトル文字列は差し替え可能な定数にする)+年齢確認ゲート。「入場」で一覧へ、「退場」で閉店画面へ - **ヘッダー**:ロゴ(屋号)、営業中インジケーター、流れるティッカー(集計情報) - **概要帯**:在籍数/関係ライン数/イベント数/最終記録日の4指標 - **絞り込み**:名前検索、タグチップ(AND条件)、ステージチップ(OR条件)、選択中件数の表示、一括リセット - **一覧**:カードグリッド - **詳細シート**:全画面オーバーレイ。前後移動つき - **フッター**:屋号と注意書き ### 5.2 一覧カードの文法 - 写真エリア:実写真があるペルソナは写真、ないペルソナは **手続き生成のモザイク胸像**(Canvas。slugから決まるシード乱数で生成し、外見テキストに含まれる髪色キーワードで髪色を決める) - **No.プレート**(左上):一覧順の番号を2桁ゼロ埋めで表示 - ステータス札:在籍中を示す点滅バッジ/写真がない場合は「写真非公開」札 - 名前・肩書き(occupation)・タグ・現在ステージのバッジ(継続性ごと) - カードの出現アニメーションは遅延つき(先頭十数枚まで段階表示) ### 5.3 詳細シート - No.と位置表示(n / 総数)、前後ボタン(§5.4の並び順に従う) - IDENTITY / SOUL のタブ切り替え(合成済みテキストのプレビュー) - 継続性(関係性)の切り替えタブ:切り替えると現在ステージ・経緯タイムラインも連動 - 経緯タイムライン:イベント種別ラベルつきで時系列表示 - 合成テキストのMarkdownコピーボタン - **ステージ名をクリックすると呼び出し命令をコピー**する: `『ペルソナ名』でサイドチャットを始めてください。継続性:継続性名(partnerキー)/ステージ:ステージ名` この文を任意のチャットに貼ると、そのペルソナ×ステージで新規チャットを開始できる ### 5.4 アクセスごとのシャッフル - ページ読み込み時に一覧配列を **Fisher–Yates で1回シャッフル** する - 表示順・カードのNo.・詳細のNo.と位置表示・前後移動は、すべてこの配列順から導出して整合させる - 同じ訪問中は順序を固定し、再読み込みのたびに新しい順番・番号になる ### 5.5 検索・絞り込みの規則 - 名前検索は **かな正規化**(カタカナ→ひらがな化、空白・中黒の除去)した部分一致 - タグは AND、ステージは OR で評価する - 該当なしの場合は空表示メッセージを出す ### 5.6 デザイン指定(雰囲気は好みに合わせて読み替え可) ``` 性産業っぽい、ちょっとピンクでセンシティブ、ケバケバしくサイバー。 紫黒の背景にネオンピンクの発光、風俗店の電飾看板とサイバーパンクの混合。 一覧はキャスト一覧の文法で。入場に年齢ゲート風のネオンスプラッシュ。 外部フォント・CDN・画像は使わず、スマホ幅でも崩れないこと ``` --- ## 6. 写真パイプライン(任意機能) - 写真はペルソナごとに1枚。原稿フォルダに `写真/.webp`(幅512px・WebP)で置く - ビルドスクリプトがフォルダを読んで **base64の写真マップとしてHTMLに埋め込む**(写真のないペルソナはモザイク表示にフォールバック) - 写真の差し替え手順:対象ファイルを取り替える → 再ビルド → アーティファクト更新 → **表示検品(差し替えた写真が実際に表示されているか・総数が変わっていないか)** - 写真は原稿フォルダ側で一元管理すること。一度きりのHTML直接編集で済ませると、次の再ビルドで消える --- ## 7. 運用ルール ### 「カタログに反映」の定義(ワンセット) DB登録(transition / event / override / 新規登録)だけでは反映完了ではない。以下を全部行う: 1. DB登録 2. compose で合成結果を検証 3. UI用JSONの再書き出し 4. ページの再ビルド 5. **表示検品**(変更したペルソナの表示行と、総数・ライン数・イベント数・最終記録日がDBと一致すること) ### その他 - 大きな書き込みの前にDBファイルを日付+ラベル付きでバックアップする - ステージ変化は申告ベース。イベントだけの追加はDBのみで済ませ、ページ再生成は次の遷移時か依頼時にまとめてもよい - 実装をコーディング担当に委託する場合、プランの利用上限に達したら作業を止めて通知するよう最初に伝えておく --- ## 8. つまずきポイント(先回り) 1. **partner と呼び名の混同**:partner に物語内の呼び名を入れると、合成系ツールが既定の相手(user)を探して失敗しやすい。識別子と呼び名は分ける 2. **履歴のある関係性は消せない**:追記専用の設計上、間違って作った関係性は削除できない。登録前に partner とステージをよく確認する 3. **UI生成時の独断除外**:生成側が、年齢不詳などの理由でペルソナを黙って除外したり、ログの文言を削ったりすることがある。対策は (a) 全ペルソナに成人年齢を明記しておく、(b) 生成後に人数と内容が元データと一致するか必ず検品する 4. **UIはスナップショット**:カタログを更新してもUIは自動で追随しない。再書き出し→再ビルド→更新→検品の手順を運用に含める 5. **写真の直接埋め込みは消える**:写真は原稿フォルダ経由でビルドに載せる。再ビルドで消える入れ方をしない --- ## 9. 受け入れチェックリスト - [ ] 方式比較と選定理由が文書化されている - [ ] テスト全件パス(特に「経路が違っても同じステージの合成が同一」「険悪の残り香が残らない」) - [ ] サンプルペルソナで compose の出典確認ができる - [ ] 経緯圧縮が元ログ不変・二重畳みなし・上限文字数内で注入される - [ ] UI:人数・ライン数・イベント数・最終記録日がDBと一致 - [ ] UI:検索(かな正規化)・タグAND・ステージOR が動作する - [ ] UI:再読み込みで順序とNo.が変わり、訪問中は詳細と一致する - [ ] UI:写真ありは実写真、なしはモザイクで表示される - [ ] 外部リクエストがゼロである(フォント・CDN・画像) - [ ] スマホ幅でレイアウトが崩れない