rusica.net

ロールプレイチャットシステムを開発中その2

とりあえず基盤は完成したので v1.0.0 とする。更新履歴は以下の通り。

## [1.0.0] - 2026-04-24

### Added

#### コアチャット機能
- LM Studio(OpenAI互換エンドポイント)との双方向チャット連携
- ストリーミング生成(`/chat/stream` SSE)およびストップ機能
- チャットセッション管理(作成・削除・履歴閲覧)
- メッセージ送信履歴を直近15往復に制限し、トークン超過を防止
- Thinking(思考モード)のオン/オフ切り替えとフロントエンド表示

#### キャラクター・ユーザー管理
- キャラクタープロファイル(名前・設定・システムプロンプト・画像)の作成・編集・削除
- ユーザープロファイル(呼称・自己紹介など)の管理
- キャラクター別アイコン画像のアップロードと静的配信(`/images/`)

#### 長期記憶システム
- チャット後の記憶自動抽出(差分方式、手動トリガー)
- 記憶のピン留め・ブックマーク機能
- キーワードフィルタリングと全ピン解除
- 記憶の編集・削除
- 記憶インデックスのリセット機能
- 外部記憶(Lore Book)の管理機能

#### 画像生成連携
- Draw Things(ローカル画像生成サーバー)との連携基盤
- チャット内からの画像生成トリガーとプレビュー表示
- メッセージ・セッション削除時の関連画像ファイルの自動削除

#### バックアップ
- プロジェクトデータのZIPバックアップ機能

### Changed

#### データ基盤(Pydantic移行)
- キャラクター・ユーザー・記憶・チャット・システム設定の全モデルを `models.py` でPydanticクラスとして定義
- バックエンド全体でJSONの読み書きをPydanticモデル経由に統一し、型安全性と保守性を向上

#### UIテーマ・スタイル
- CSSカスタムプロパティ(CSS変数)によるテーマ管理を導入
- プリセットテーマ(ライト・ダーク・各種カラーテーマ)の選択機能
- ハードコードされた色定義をすべてCSS変数に置換
- カスタムテーマ作成・保存・切り替え機能(バグ修正済み)

#### フロントエンドアーキテクチャ
- `ChatArea.jsx` を8つのサブコンポーネントに分割
- ロジックをカスタムフック(`useChat` 等)に分離
- パーサー関数・共有UIコンポーネントを独立ファイルに切り出し
- LLM通信処理の共通化

#### バックエンド整理
- `memory.py` のリファクタリングと不要コードの削除
- ルーター構成の整理(`character` / `user` / `memory` / `chat` / `system` / `image`)
- `vite.config` にローカルIPアドレスを追記(LAN環境からのアクセス対応)

### Fixed

- ユーザープロファイルで指定した呼称がLLMに反映されない不具合
- 記憶抽出結果が英語になるバグ
- 記憶生成時のトークン不足エラーへの対処(プロンプト最適化)
- Thinking出力の表示漏れ
- Draw Things連携の各種バグ
- カスタムテーマ切り替え時の表示崩れ

今後の構想としては以下の通り。実現できるかどうかわからない。あとUIも改善したいところ。


## 1. LLMサーバーと画像生成APIについて
- クラウドAPIやローカルのOllamaなど、任意のLLMを利用できるようにする。
- 画像生成についてもComfyUIに対応したいし、同一ネットワーク内の別端末のAPIを叩けるようにする。

## 2. 非同期エージェント・パイプライン
メインの生成処理を待たずに(あるいは並行して)外部情報を取得・処理する設計。

* **生成フローの分離**
  - **Pre-generation**: 入力直後に実行(例:ユーザー入力の要約、感情分析)
  - **Parallel**: メイン生成と同時に実行(例:画像生成プロンプトの作成)
  - **Post-generation**: 出力完了後に実行(例:レスポンスの反省、スロット更新)
* **技術的要点**: 
  - Node.jsの `Promise.all` 等を使用した非同期処理によるUXの最適化。
  - 各エージェントに個別のプロンプトを注入し、結果を最終プロンプトに統合するオーケストレーション。
  - 現在の構成の場合、Pythonでの非同期実装では async/await が標準らしい。エージェント処理(思考や検索)をすべて async で定義し、複数のLLM呼び出しを asyncio.gather() で並行実行すれば、Node.jsのPromiseと遜色ないパフォーマンスが出るとのこと。

## 3. 状態管理(Tracker System)
会話履歴とは別に、世界の「状態」を保持する専用のデータベースレイヤー。
※ベクター検索(RAG)も活用?

* **Trackerの役割**
  - **World State**: 日時、天気、場所、登場人物。
  - **Character Stats**: 感情値、好感度、装備、ステータス。
  - **Quest/Lore**: 進行中のクエスト、フラグ管理。
* **実装方式**: 
  - 会話のたびにエージェントがTrackerテーブルを更新。
  - LLMへ送信する際、System Promptの末尾に、現在の「状態」を構造化データとして注入する。

## 4. モデルの適材適所(Dynamic Routing)
タスクの難易度に応じて、ロードするモデルや呼び出すプロバイダーを切り替える仕組み。
実行環境によってはLLMモデルを複数同時にロードすることも検討。

* **階層化モデル構成**
  - **Reasoning Tier**: 思考、計画、状態更新を担当(軽量モデルで高速化)。
  - **Generation Tier**: 描写、会話、ロールプレイを担当(大規模モデルで高品質化)。
- **投機的サンプリング** の導入も検討。

## 5. プロンプトのモジュール化(Depth Injection)
一枚岩のSystem Promptを廃し、ブロック単位で管理する仕組み。

* **構成要素**:
  - Persona(キャラクター定義)、World Info(世界観)、State(Tracker情報)、Instruction(行動指示)。

## 6. マルチキャラクター・オーケストレーション
複数キャラクターが共存し、相互作用するための仕組み。

* **Response Orchestrator(発言制御エージェント)**
  - グループ内の状況(誰が最後に話したか、誰が注目されているか)を判断し、次に発言すべきキャラクターを選定する専門エージェントを配置。
* **クロス・アウェアネス実装**
  - 個々のキャラクターが、自分以外のキャラクターの動向やステータスを「共通状態管理テーブル」を通じて参照できるように設計。
* **実装方式**:
  - 全員の履歴を単純結合するのではなく、オーケストレーターが「今回の出力に含めるべき会話のコンテキスト」をフィルタリングしてプロンプトを構築する。
  - ユーザーが不在でも特定のトリガーや時間経過で自律的にメッセージを生成する仕組みの構築。