詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

@motemen/ai-streamerでAIずんだもんに実況してもらう

ちょっと前の Kyoto Tech Talk #8 というイベントで「AIに実況させる」という発表をしたので、もうちょっと詳細な情報も補足しつつ紹介します。

AIに実況させる / AI Streamer - Speaker Deck

きっかけ

対戦ゲーム(ポケモンSVランクバトル)の上達につなげたい気持ちで激安USBキャプチャボードを入手してプレイを録画できるようになったのがけっこう前のこと。録画に使うOBSをちょっと設定すれば配信までできることがわかったので試しに配信をしてみると、だんだん実況風のことをしたくなってくるもので、プログラムでゲーム画面の分析をしつつ、コメントをAIに生成させて立ち絵とともに音声化するだけでなんだかそれっぽく、こちらのほうが楽しくなってくる。

最初は全部まとめてひとつのプロジェクトとして作っていたのだけど、ゲーム特有の分析から離れた「コメント生成+立ち絵+音声化」部分だけ切り離して作ってみたものが motemen/ai-streamer。

GitHub - motemen/ai-streamer: AI Stremaer toolkit

最終的にどういう感じになるかは実際の動画を見てもらうとわかりやすいです(プレイの質はおいといて):

※ これ作ってる最中のやつでちょっとおかしくて、意地でも雑談を続けてしまうようになってたかも……。

技術的詳細

OBS Studio は配信のデファクトスタンダードツールで、さまざまな入力を組み合わせて配信や録画ができる。その入力の一つに Browser Source ってのがあり、これは Chrome Embedded Framework を利用してあらゆるウェブページを動画にオーバーレイできる。なんなら Web Audio API で音声も再生できる。動画はわからんがウェブならわかるってことでこれでなんとかすることになる。

プレイ動画分析くん(スライドでは eapu2)は定期的にフレーム画像を分析して動画のシーンやイベントを検出できるので、これをai-streamer側に送ればいい。ここまで来ると話は簡単で、honoでAPIエンドポイントを作り、プロンプトを受け取ったらAIでコメントを生成、VOICEVOXで音声化し、フロントエンドに送ったらずんだもんの立ち絵や字幕とともに再生してやるだけ。READMEのシーケンス図も参照してください。

そうはいっても「それっぽい」実況のために、細かい工夫はいろいろやった。

  • セリフを全部生成させてからまとめて音声化するとレイテンシが大きく、発話タイミングが画面の状況から大きくズレてしまうので、ストリーミングで受け取り、句読点で区切りながら逐一音声化とフロントエンドへ送出している。
  • 動画からイベントが検出されず、無言になる時間はけっこうある。人間の実況者はこういうとき雑談をしてるわけなので、「n秒間発話がなければ雑談する」みたいな挙動もできるようにしている。

AIプロバイダやモデルは差し替えられるようにしておきたいので、VercelのAI SDKを使った。プロバイダを気にせずに実装できるのでなかなか便利だった。たとえばセリフに合わせてずんだもんの表情を変える機能も用意しているが、これはToolとして実装している。プロバイダごとに呼び出し方が変わるのは相当だるそうだったのでここは大いに助けられた。

利用方法

完全に自分用で用途がニッチなんであまり親切につくれてはいませんが、こんな感じで起動:

# VOICEVOXを起動
% docker run -d -p 50021:50021 voicevox/voicevox_engine:latest
% DEBUG=aistreamer OPENAI_API_KEY=... pnpx @motemen/ai-streamer
  aistreamer Loaded configuration: {
  aistreamer   ai: { model: 'openai:gpt-4o-mini', temperature: 1 },
  aistreamer   prompt: 'あなたはゲーム実況ストリーマーです。\n' +
  aistreamer     'あなたは情緒豊かで、いつも視聴者に楽しい時間を提供します。\n' +
  aistreamer     'これからゲームのプレイ状況を伝えるので、それに合わせたセリフを生成してください。',
  aistreamer   maxHistory: 10,
  aistreamer   avatar: {
  aistreamer     enabled: true,
  aistreamer     directory: '...'
  aistreamer   },
  aistreamer   replace: []
  aistreamer } +0ms
Listening on http://localhost:7766

http://localhost:7766 にアクセスすると、OBSのブラウザソース向けのページが見られます。で

% curl localhost:7766/api/chat -d '{"prompt":"あいさつして"}'

のようにプロンプトを送ってやれば、おもむろに喋り始める。Web Audio API の関係で、いちどページ内でユーザ操作をしといたほうがいいかもしれないです。このエンドポイントをプログラムから叩く想定ですが、いちおう http://localhost:7766/director というページも作っていて、まあ手動でもやりたいことはできる感じにしようとした痕跡はある。

設定

設定はサンプルがあるのでそれを見るのがよいけど、いくつかピックアップすると、

モデルの設定は

[ai]
model = "anthropic:`claude-haiku-4-5" # ANTHROPIC_API_KEY
# or
# model = "google:gemini-2.0-flash-exp" # GOOGLE_GENERATIVE_AI_API_KE

みたいに書ける。これは Vercel AI SDK のおかげ。プロバイダによって必要とする環境変数が違うのに注意。

ヒマなときに雑談するには:

[idle]
timeout = 30000 # ms
prompt = "生活感のある雑談をして"

という感じ。起動時に設定ファイルを渡せるので、それで指定してください。

ちなみに設定ファイルをc12でロードしているのでTOML以外にもいろいろな形式で指定できて、なかでも.jsを使えるのでこんな指定ができる(抜粋):

import { execSync } from "node:child_process";
import { readFileSync } from "node:fs";
import * as path from "node:path";
import { z } from "zod";

export default {
  // システムプロンプト
  prompt: `
あなたは元気で親しみやすいVTuber、ずんだもんです。
一人称は「ぼく」。語尾は「のだ」や「なのだ」をつけて話します。

今日は以下のスライドをプレゼンするので、台詞をつくってください。
発話する内容なので、箇条書きなどは使わないこと。

1ページずつ進め、当該ページの内容だけを話してください。
スライドを進めるタイミングはこちらで指示します。
...

---

[...ここにスライドの内容を貼っている]

`,

  idle: {
    prompt:
      "nextSlide ツールでスライドを次に進めてください。その後、スライドに合わせた内容を話して",
    timeout: 3000,
  },

  tools: {
    nextSlide: {
      description: "スライドを次に進める",
      inputSchema: z.object({}),
      execute: async (_, { aiStreamer }) => {
        const currentPage = aiStreamer.store.currentPage || 1;
        console.log("nextSlide called", { currentPage });
        aiStreamer.store.currentPage = currentPage + 1;
        execSync(
          `osascript -e 'tell application "Google Chrome" to activate' -e 'tell application "System Events" to key code 124'`,
        );
        return { currentPage };
      },
    },
  }
};

つまり発話が終わったらidle(雑談)が発動し、そのタイミングでnextSlideツールが呼ばれ、AppleScript経由で右矢印が入力され、スライドが次に進む……というピタゴラスイッチ。この設定で収録されたのが以下です。夢の自動プレゼン。

ちなみに裏側はこんな感じでした。

MCPとしての利用

真面目にやるとプロンプトの送信を自分でやらなきゃいけなくて面倒なんだけど、http://localhost:7766/api/mcp をMCPのエンドポイントとして用意しているので、AIからずんだもんを喋らせることが可能。これなら楽できますね。

VS CodeでMCPとして利用しつつ、AIに編集させればAIライブコーディングができる。

おもしろいですね。

はてなで一緒に働きませんか?