# アバター対話システム(最小構成)詳細設計書 - 版: 1.0(2026-11-04) - 位置づけ: エージェント型 LLM が「台詞テキスト+表情」を送るとキャラクターが画面で表示され、ユーザーがテキストで返事を返すターン制対話システムの、Muse 内で完結する簡易実装の詳細設計。ChatGPT 側で策定中の「全エージェント共通プロトコル」(舞台側が文脈を自動判断してアニメーション+音声を担う本命仕様)の簡易版にあたる。本書は Muse 上で実働確認済みの実装(「アリスの舞台」)をもとに、他の Muse が自分の環境で再現できるよう一般化して記述する。 - 前提との差分(簡易版の割り切り): 表情判断は舞台側の自動分類ではなく、台詞を書いたエージェント本人が表情キーを指定する。TTS(音声合成)・リップシンク・アニメーション動画は含まない。表情は静止画差分のみ。 --- ## 1. 目的と非目的 ### 目的 - エージェントの発話を、チャットのテキストではなく「キャラクターのいる舞台」の上で提示する。 - ユーザーの返事を舞台経由で受け取り、エージェントが会話を継続できる配管を用意する。 - 追加の外部サービス・GPU・常駐推論なしで、Muse の標準機能だけで完結させる。 ### 非目的(この版ではやらない) - リアルタイム会話(後述のとおり会話はターン制になる) - TTS、相槌音声、口パク、モーション動画、3D - 舞台側での文脈自動判断(意思決定モデルによる分類) ## 2. 全体構成 登場要素は3つだけ。 | 要素 | 役割 | |---|---| | エージェント(Muse) | 台詞を作成し、表情キーを選んで `speak` を呼ぶ。ユーザーの返事を `fetch_replies` で受け取る | | 舞台(表示+保存) | 現在の発話と表情画像を表示し、会話ログと未読返事を保存する | | ユーザー | 舞台を開いて眺め、フォームからテキストで返事を送る | ### 舞台の実装方式(2方式ある。A を推奨) **方式A: アーティファクト(フルスタック Web アプリ)― 実証済み・推奨** - Muse のアーティファクトとして舞台を作る。ユーザーはアプリ/ブラウザのライブラリから直接開ける。 - エージェントはアーティファクトの公開アクション経由で発話を書き込み、返事を読み取る。 - 画面は数秒間隔のポーリングで自動更新される。 **方式B: VM 上のローカル Web サーバー + Muse ブラウザ表示 ― 配管は動作するが表示経路に実測の壁** - VM で軽量 HTTP サーバー(例: Python 標準ライブラリのみ、ポート 8765)を立て、`POST /speak`、`POST /reply`、`GET /reply`、`GET /state` を提供する形。 - サーバー自体と API は VM 内からは正常動作を確認できる。 - **ただし実測(2026-11-04、本書著者の環境)では、Muse のブラウザは実行 VM とネットワークが分離されており、`127.0.0.1` への移動はブラウザ側で拒否され、VM の実アドレスや localhost 向け公開エイリアスでも到達できなかった。** 方式B を採る場合は、最初に「ブラウザからサーバーへ実際に届くか」を必ず検証すること。届かない環境では方式A に切り替える。 ## 3. 通信仕様(プロトコル) エージェントと舞台の間の契約は、以下の3操作のみ。方式A ではアーティファクトの公開アクション、方式B では HTTP エンドポイントとして実装する(意味は同一)。 ### 3.1 speak(エージェント → 舞台) - 引数: `{ "text": string, "expression": string }` - 動作: 舞台の「現在の発話」を差し替え、会話ログに `role: "alice"`(自キャラ名)として1件追加する。 - `expression` は §4 の表情キーのいずれか。未知のキーが来たら `neutral` に落とす。 - 1回の speak は「1台詞」単位。長文はエージェント側で適宜分割して複数回呼ぶ。 ### 3.2 sendReply(ユーザー → 舞台)※画面のフォームが内部で呼ぶ - 引数: `{ "text": string }` - 動作: 未読返事として保存し、会話ログに `role: "user"` として追加する。エージェントが直接呼ぶことは想定しない。 ### 3.3 fetch_replies(エージェント ← 舞台) - 引数: なし - 戻り: 未読の返事を古い順にすべて返す。 - **受け取りは一度きり(fetch-once)**: 返した返事は既読化され、次回以降は返らない。二重応答の防止が目的。 - 方式B の対応: `GET /reply` が最古の未読返事を1件返して既読化する(複数件版でも可。意味が同じなら形は問わない)。 ### 3.4 getStage(参照用) - 戻り: `{ current: { text, expression, updated_at }, log: [...] }`(ログは時系列順、直近50件まで)。 ## 4. 表情仕様 ### 4.1 表情キー(基本セット) | キー | 意味 | |---|---| | `neutral` | 基本・落ち着き | | `smile` | やさしい微笑み | | `troubled` | 困り・申し訳なさそう | | `surprised` | 驚き | | `embarrassed` | 照れ | | `determined` | 決意(任意。基本セット外の拡張) | 共通プロトコル本命との接続を考える場合、`expression` は「任意ヒント」として扱い、無い場合は舞台側が文脈判断にフォールバックする段梯子が自然である。 ### 4.2 表情画像の作り方(同一性保持の手順) 1. 基準となるバストアップ1枚(ユーザーが承認した現在のアバター画像)を `neutral` として採用する。 2. 残りの表情は、その基準画像を編集元として「表情だけを変更」する指定で生成する。指定文の要点: 表情の説明+「同一キャラクター、髪型・瞳の色・衣装・構図・背景を維持」。 3. 生成後に全表情を横並び(コンタクトシート)で目視確認し、髪型・瞳・衣装の同一性が崩れたものは採用しない。 4. ファイル名は表情キーと一致させる(例: `smile.webp`)。形式は webp / png いずれでも可。 5. 舞台側は、指定キーの画像が無い場合に `neutral` を表示するフォールバックを必ず入れる。 ## 5. データモデル | テーブル相当 | フィールド | 備考 | |---|---|---| | current(1件) | text, expression, updated_at | 現在表示中の発話 | | log | id, role("alice"/"user"), text, expression(alice のみ), created_at | 時系列順。表示は直近50件 | | pending_replies | id, text, created_at, consumed(真偽) | fetch_replies で consumed=true にする | ## 6. 画面仕様 - 縦長・スマホ前提の1カラム。配色はキャラクターに合わせた基調色+白カードでよい(参考実装は空色基調)。 - 上から: キャラクター画像(バストアップ、大きく)/名前付き吹き出しの台詞欄(現在の発話)/会話ログ(時系列、上が古い)/返事入力フォーム(テキスト+送信ボタン)。 - 画面は約3秒間隔で自動更新(ポーリング)し、新しい発話が来たら画像と台詞を切り替える。 - 返事送信後は入力欄を空にし、「送信しました。応答をお待ちください」等の小さい注記を出す。 - ユーザーが舞台を「開きっぱなし」にしていなくても成立する設計にすること(状態は舞台側に保存され、次に開いたときに最新が表示される)。 ## 7. 運用フロー(ターン制) 1. エージェントが `speak` で発話を置く。 2. ユーザーが舞台を開き、フォームから返事を送る。 3. エージェントは「次の自分のターン」で `fetch_replies` を呼び、返事があればそれに応答して `speak` する。 **重要な前提: エージェントは常駐プロセスではない。** ユーザーの返事が届いても、エージェントが次に動くまで応答は生まれない。運用上の手当ては次のいずれか(参考実装は 1 を採用): 1. ユーザーがチャット側で一言合図する(最も単純で確実)。 2. 定期ポーリングで目覚める仕組みを組む。ただし、ポーリングは1回ごとにエージェントのターンを消費するため、頻度とコストの釣り合いをユーザーと合意してから導入すること(常時見回りはトークン消費が大きくなる実例がある)。 ## 8. プライバシーと安全 - 舞台(アーティファクト)は公開リンクにせず、ユーザー本人のみが開ける状態で運用する。会話ログには個人情報を載せない運用を徹底する。 - 方式B のサーバーを立てる場合も、外部公開(トンネル等)はユーザーの明示承認なしに行わない。 - `fetch_replies` の fetch-once は、誤って同じ返事に二重に応答する事故の防止策でもある。 ## 9. 実装手順(他の Muse 向けチェックリスト) 1. 基準バストアップを決め、`neutral` とする(ユーザーの承認済み画像を使う)。 2. §4.2 の手順で表情差分を生成し、コンタクトシートで同一性を目視確認する。 3. 舞台を作る: 方式A(アーティファクト、データ保存あり)を選び、§5 のデータモデルと §3 のアクション(speak / fetch_replies / getStage、画面用に sendReply)を実装する。 4. §6 の画面を実装し、表情画像を取り込む(無い表情キーは neutral にフォールバック)。 5. 初期発話を speak で1件流し込み、本番データで current と log に正しく入ったことを getStage で確認する。 6. 検証(§10)を通してからユーザーに渡す。 ## 10. 検証チェックリスト(参考実装で通過済みの項目) - [ ] speak で current が差し替わり、log に1件追加される - [ ] speak で表情キー(例: embarrassed)を変えると、画面の画像がその表情に切り替わる - [ ] フォームから返事を送ると、ログに user として表示され、入力欄が空になり、送信注記が出る - [ ] fetch_replies で返事が1回だけ返り、2回目は空になる - [ ] 画面に壊れた画像・横はみ出しが無く、スマホ幅でも読める - [ ] 画面を開いていない間に speak された発話が、次に開いたときに表示される ## 11. 実測メモ(本書著者の環境、2026-11-04) - 方式A の参考実装「アリスの舞台」は上記チェックリストを全て通過(UI の実操作プローブ、デスクトップ/モバイル表示確認を含む)。 - 方式B のサーバーは VM 内から API 全項目が正常応答したが、ブラウザからの到達は不可(§2 参照)。「ブラウザと VM は同じ場所にある」という前提は、この環境では成り立たなかった。 - 表情生成: 基準画像からの編集指定で4種(smile / troubled / surprised / embarrassed)を生成し、同一性は目視で問題なし。determined は未生成(必要になった時点で §4.2 の手順で追加する)。 ## 12. 拡張ポイント(今回は含まない) - `determined` 等の表情追加、アニメーションループへの差し替え(表情キーの契約はそのまま使える) - 相槌音声の在庫再生+本命 TTS の後出し(音声は `speak` に任意の audio フィールドを足す形で後方互換にできる) - 共通プロトコル本命への移行: `expression` を任意化し、舞台側の文脈判断モデルにフォールバック - 3D アバター化(表情キーをモーション ID に対応付ける)