1. 入力から応答が届くまで
対象・調査限界は README を参照。以下の数値はコードの値であり、本番の実効設定を意味しない。
1.1 全体の責務
flowchart TD
A[YouTube / Twitch / TikTok / Kick / 音声 / 手動] --> B[CommentController: 正規化]
B --> C[ChatGPTController: 受付条件・安全検査]
C --> D[CommentStock: 用途別キューと採用]
O[運営カンペ] --> D
G[認証済みゲームイベント] --> D
D --> E[ChatGPTController: 台本・発話権の確認]
E --> F[SituationSnapshotを固定]
F --> H[LLMController: 人格・記憶・状況・演出の統合]
H --> I[LLM生成・再試行・出力整形]
I --> J[安全性・長さ・鮮度・実行scopeを再検査]
J --> K[TTSController: 音声・表情・口パク]
K --> L[実際に再生された本文の確定]
L --> M[記憶・質問受付・話題の続きへ反映]
S[ScriptRunner / MonologueController] --> H
S --> K
中心はUnity。Functionsがすべての返答を生成してUnityへ配信する構造ではなく、Unityの LLMController が各プロバイダーへHTTP要求し、Unityの TTSController が音声を配送する。クラウドにはキャラ設定、台本、記憶同期、運営コマンド、状態報告などの役割がある。
| 層 | 主な責務 | 主な実装 |
|---|---|---|
| 入力 | 取得元の違いを共通イベントへ変換 | CommentController、NormalizedComment |
| 採用 | 用途別優先度、古さ、順序、公平性、台本の受付 | CommentStock、ScriptRunner |
| 応答の進行 | 1件/複数件、処理ロック、取消、最終配送 | ChatGPTController |
| 内容生成 | プロンプト、モデル、再試行、補助タグ | LLMController と各partial |
| 聞こえる表現 | 合成、チャンク、感情、再生、停止、配送結果 | TTSController |
| 番組の進行 | 台本ノード、自発発話、質問待ち、寄り道 | MonologueController、ScriptRunner |
1.2 入力の種類と区別
CommentPlatform は Unknown / YouTube / TikTok / Twitch / Voice / Manual / System / WebVoice / Discord / Kick。CommentWorkKind は「どこから来たか」と独立し、「何として扱うか」を表す。
CommentTrace はプラットフォーム、source、元message ID、投稿UTC時刻、安定user ID、課金イベント由来、owner/moderatorバッジ、Web Voiceとの相関情報を持つ。元時刻が無い入力は受信時刻を使い、未来時刻は受信時刻へ補正する。元の投稿時刻が取れない経路では、実際の投稿からの鮮度までは判定できない。
表示名と安定IDは分けられ、ViewerIdentityRegistry.RegisterObservedIdentity が観測を記録する。IDが常に全プラットフォームから来るという保証ではない。バッジによる役割も、名前の文字列から推測しない。
入力が正規化されたこと、コメント欄に表示されたこと、AIが採用したこと、音声になったことは別の段階である。OnNormalizedComment と応答用のイベントも分かれている。
1.3 通常コメントの入口で起こること
ChatGPTController.HandleNewComment の順序に沿う。
- source message IDとuser IDを補完する。
- コメント応答OFF、配信切替中、空文字を拒否する。Adminの練習入力には一部バイパスがある。
- 原則3文字未満を拒否する。回答受付中で短文回答が許可される場合は通す。
- 「申し訳ありませんが」「お応えできません」「お答えできません」「エラーが発生しました」を含む文を、AI拒否/エラー文として拒否する。
- 通常コメントは投稿基準60秒を超えていれば破棄する。
@で始まる本文をリスナー同士の会話とみなし、AI応答経路を通さない。- 台本がコメントを受け付けない間は、安全検査後にキューへ保留する。相槌や視聴者情報抽出の経路へ進めない。
- 通常時は安全検査後、視聴者情報抽出、コメント欄の雰囲気、来訪・関係性を更新する。
- HumanLikenessの方針により、無視 / 短い相槌 / 完全応答へ分岐する。相槌は睡眠・別処理・音声再生中には抑止される。
- キュー投入に成功した入力を質問への回答候補として記録し、処理ポンプを動かす。
体験への含意: 「草」「w」「うん」などは、回答受付の特例外では3文字条件で落ち得る。AIエラー文に似た言い回しも文意によらず弾かれる。短い盛り上がり反応を拾えるかは、生成モデルより手前の仕様に左右される。
1.4 キューと優先順位
CommentStock.DequeueNextSingleLocked / GetWorkPriority の順序。
| 優先順 | 用途 | 優先値 | 注意 |
|---|---|---|---|
| 1 | 緊急カンペ | 500 | 割り込みと生成取消の連携あり |
| 2 | 通常カンペ | 400 | 視聴者応答ON/OFFとは別レーン |
| 3 | 認証済みGameEvent | 350 | 専用キュー。15秒の鮮度、最大8件 |
| 4 | 課金イベント | 300 | 視聴者応答・台本のゲート条件を受ける |
| 5 | 練習コメント | 200 | 追跡可能な通常応答として扱う |
| 6 | SystemGenerated | 100 | 運営カンペと同一ではない |
| 7 | 一般コメント | 0 | 関連度、公平性、待ち時間で選ぶ |
質問締切後の回答開始猶予中は、優先レーンの次に、その質問に受付済みのmessage IDだけを選ぶ。無関係なコメントで猶予を延ばさない。
- 通常キューには100件の容量トリムがある。別に
MAX_STOCK=10と受付可否の管理もあり、「100件まで必ず受け付ける」という意味ではない。 - スパチャキューは 受信順。投入側の古いコメントには「金額順」とあるが、実装の
AddToSuperChatQueueはFIFOである。 - 課金イベントは最大50件。超過時は最低額、同額なら古いものを除外し、警告を出す。したがって「全課金イベントへの返答保証」ではない。
- 一般コメントの60秒制限は、enqueueだけでなく処理開始・生成前後・配送直前でも検査する。保留できても、発話まで必ず残るわけではない。
- 課金イベントは一般60秒制限から除外される。配信切替時には6時間超の課金イベントを除外する処理がある。
公平性が有効なときの採用スコアは、AudienceTurnFairness に定義される。
待ち時間 < 30秒: 関連度 − 直近8配送内の同一視聴者回数 × 0.75 + 待ち秒 / 30
待ち時間 >= 30秒: 1000 + 待ち秒
同じ人を連続で拾い続ける偏りを緩め、長く待った入力は古い順で回収しやすくする。全員への均等配分や、内容の面白さを評価する選別ではない。優先レーンにはこの公平性は適用しない。
1.5 いつ発話を始められるか
ProcessNextComment は SemaphoreSlim で処理を直列化する。配信切替中、別のコメント処理中、自発発話中、TTS実再生中、睡眠、台本ゲートなどを確認する。一般コメントを受け取ることと、処理を開始できることは分離されている。
ASMR中の通常コメントは30秒間隔の制限がある。課金・カンペ・GameEventには優先バイパスがある。台本による応答可否、音声競合など、別のゲートまで一括で解除する意味ではない。
複数コメントのまとめ応答
台本が動いておらず、自由会話の回答待ちでもない場合は、最大20件をバッチ処理できる。少なくとも2件・2人が必要。公平性が有効な場合は同一視聴者の複数件を採用しない。GenerateResponse(... bypassDirector: true) により、単一コメント用の演出・寄り道と区別する。
この経路は混雑への対処だが、1人ずつとの対話を保証しない。通常の単発返答と、バッチで拾われたコメントを同じ「返答率」に数える場合は指標の定義が必要になる。
1.6 1件の生成・配送の詳細
ProcessCommentInternal の通常生成経路は、次の段階を持つ。
| 段階 | 処理 | 失敗・変化が起きた場合 |
|---|---|---|
| 状況固定 | Snapshot、返答言語、発話IDを固定 | 旧scopeなら中止 |
| 前準備 | 入力NG判定、必要なセッションメモリ更新、UI処理中表示 | 通常入力は安全代替文になることがある |
| 応答生成 | 生コメントと内部文脈を区別してLLMへ渡す | 再試行/経路別失敗処理 |
| 生成後検証 | 実行scope、コメント鮮度、カンペ条件 | 破棄または限定的な再生成 |
| 本文投影 | 自問自答演出、安全検査、タグ抽出、長さ制限、好みの具体性 | 本文変更時は解析タグの副作用を抑止 |
| 感情確定 | タグがあれば利用、なければEmotionManagerで解析 | 追加解析が待ち時間になることがある |
| 配送直前 | 安全性・世代・鮮度・発話ループを再確認 | 発話見送り。ループ時は通常経路に自己回復処理あり |
| 配送 | 字幕、TTS、表情、必要な操作 | TTSのrequestGateでも再検査 |
| 配送結果 | HasAudibleDelivery、Completed、DeliveredTextを評価 |
途中まで聞こえた文と全文完了を分ける |
| 確定後 | 発話履歴、視聴者回答回収、話題候補、記憶 | scopeが有効か、配送が完了したかで分岐 |
感情・思考・ゲーム操作などの補助タグは、そのまま読み上げるための文字列ではない。安全フィルターや長さ制限で本文が変わった場合、元のタグによる操作・記憶更新を無条件に実行しない。
ただしすべての表示/副作用が「音声成功後」に揃っているわけでもない。通常字幕や一部の操作・感情状態更新は再生完了より前に行われる。実可聴の保証は SpeechPlaybackResult とその後のcommitについての契約として読む必要がある。
1.7 カンペとゲームの専用経路
運営カンペ
ChatGPTController.OperatorCue、LLMcontroller.RequestScope、OperatorCueAdherenceValidator が中心。
- 用途メタデータで運営入力と判定する。視聴者が本文に「カンペ」と書くだけで昇格する仕組みではない。
- 通常視聴者の記憶・話題演出・providerの共有会話状態から隔離したプロンプトを作る。
- 生成カンペは主題や必須条件を検査し、不適合時は元の指示と不足条件で1回だけ再生成する。
- 直接TTSカンペは前後trim後の原文一致をprovider境界まで要求する。安全処理で変更が必要なら代替文を読み上げず失敗にする。
- キュー投入だけで成功とはしない。運営へのdispatching / speaking / completed等は予約と実再生の段階に結びつく。
GameEvent
これは一般的なゲーム実況の全経路を指す名前ではなく、認証済みゲーム拡張からの専用入力。現在の処理にはTAERUの接続・画像来歴確認がある。
- 元ゲーム接続と整合する、単一のfreshな画像を固定する。画像確保には最大約1秒の待機ループがある。
- 画像を送れないproviderや、画像を持たないテキスト応答へ降格しない。
- ゲーム操作タグがある場合は、Godot側の相関ACKを確認してから観客向けの発話へ進む。
- 安全処理でゲーム応答が一般代替文に変わったときも、画面を解析した正常応答に見せないため発話しない。
通常の画面理解や自発ゲームリアクションとの違いは 3章 に記す。
1.8 遅延の構造
投稿→発話開始
= プラットフォーム/relayの配送待ち
+ 受付・キュー・台本/音声ゲート待ち
+ 状況/画像/記憶/知識の取得
+ LLM要求と全文生成(再試行があれば加算)
+ 出力検査・必要な感情/修正文生成
+ TTSの順番待ち・先頭音声の合成
+ Unityの実再生開始まで
視聴者が聞くまで = 上記 + 配信サービス・視聴端末側の遅延
RequestTransport の一般HTTP経路は DownloadHandlerBuffer で完了を待つ。ChatGPTController も GenerateResponse 完了後にTTSへ渡す。TTSチャンク分割は存在するが、一般コメントの「LLM生成トークンを逐次音声化する」接続ではない。
一般HTTP要求のtimeoutは60秒。通常生成ループは最大3回の試行を持ち、さらに失敗時の別provider経路もある。これは「1件の返答は60秒以内」という全体締切ではない。むしろ一般コメントの投稿60秒制限と合わさり、時間を使って生成した後で古さにより配送されない場合がある。
実測の秒数・中央値・p95は未取得。現行コードだけから、特定モデルやTTSが遅延の主因だとは断定できない。