はじめに
TBS日曜劇場『VIVANT』に登場するAI「ハヤト」に興味を持ち、同じような体験を自分で作れるか試しました。
イベント「VIVANT IMMERSIVE MISSION」では、来場者がミッションをこなしたあと、最終的な適性数値をAIハヤトが算出します。
声は高山みなみさんが担当しています。
この流れを参考に、4つのミッションの結果から「適性値」を算出し、AIが講評を音声で読み上げるWebアプリを作りました。
ハヤトそのものは再現せず、AIキャラクターは別の設定で作り、声も変えています。
本記事は個人の検証で、作品やイベントの制作側とは関係ありません。
この記事では、次の順で書きます。
- AIハヤトの場面で使われている Google の AI
- 作ったものの概要
- ローカル版の実装(Amazon Bedrock、VOICEVOX)
- Amazon S3 + Amazon CloudFront + AWS Lambda への公開
- 公開でハマった点(同時実行数の予約、OAC 経由の POST、AWS WAF)
- デモ動画の撮影
- AIキャラクターの名前の決め方
- 動作確認と、声が違う理由
ここで2つの問いを提示しておきます。
1つは、ローカル版と公開版で声が違う理由です。
もう1つは、AIキャラクターの名前に「-07」が付いている理由です。
どちらも最後に答えます。
AIハヤトの場面で使われている Google の AI
作る前に、TBS がハヤトの場面をどう作っているかを調べました。
確認できた範囲を整理します。
- 使われたツール: Google の動画生成AI「Veo 3」と、静止画生成の「Nano Banana」です。
細部を指定した静止画を Nano Banana で作り、それを Veo 3 に動かさせる2段階の方法が取られたとされています。 - 使われた場面: 病院の屋上機械室の爆発映像と、AIハヤトが説明する場面でモニターに映るボイラー室の爆発映像です。
- 苦労した点: 同じ指示でも毎回違う映像になる「生成AIガチャ」があり、満足のいく映像が出るまで試行を重ねたそうです。
- 位置づけ: 続編で Veo 3 を放送本編に使うのは、TBS のドラマとして初めてです。
TBS は、この制作の話を Google のイベントでも紹介しています。
2026年7月30日と31日に東京ビッグサイトで開かれた「Google Cloud Next Tokyo 26」に出展し、ブースのテーマは「TBSテレビが見せる、日曜劇場『VIVANT』制作と生成AI活用のいま」でした。
実写や絵コンテを入力に Nano Banana と Veo 3 で映像の下地を作り、専門家が確認して実写と合成し、放送用の映像に仕上げる流れが紹介されています。
来場者が『VIVANT』風のキャラクターに変身できる体験もありました。
確認できなかったことも書いておきます。AIハヤトそのものの声や動きの実装に Google が使われているかは、見つけた資料に記載がありませんでした。
そのため、「TBS が Google で実装したものを AWS で再現した」とは書けません。
調査情報デジタルの記事は、今回は本文を開けず、記事の要約で確認した範囲です。
つまり、ハヤトの場面の映像づくりは Google の AI が担っています。
今回作ったのは、ミッションと診断、講評、声、公開の部分で、こちらは AWS のサービスで組みました。
今回のアプリに、映像を生成する機能はありません。
作ったもの
画面の流れ
画面には、AIキャラクター「カナメ-07」が表示されます。
受験者は4つのミッションを順にこなし、最後にAIが適性値を算出して講評を読み上げます。
ミッションは毎回ランダムに選ばれます。
| ミッション | 内容 | バリエーション |
|---|---|---|
| 記憶 | 表示された文字列を覚えて入力 | 数字6桁、6桁を逆順で入力、英字5文字、数字8桁 |
| 反応 | 緑になった瞬間にタップ | 1回、2回連続(平均で評価)、赤の間は押してはいけないフェイント付き |
| 判断 | 状況に対して最も適切な行動を選ぶ | 4つの状況、選択肢の順番は毎回シャッフル |
| 解読 | 暗号を解いて英単語を入力 | 3つずらし、ずらし幅が可変、逆順、番号置換(A=1) |
各ミッションは100点満点で、適性値は4つの平均です。反応の点数は、反応時間を使って 100 - (ms - 200) / 6 で計算しています。
ローカル版の構成
ブラウザ(HTML / JavaScript)
└─ Python サーバー
├─ Amazon Bedrock ... 講評の生成
├─ VOICEVOX ......... 音声(青山龍星)
└─ Amazon Polly ..... VOICEVOX が使えないときの代替
サーバーに繋がらないときは、テンプレートの講評とブラウザ標準の音声で動きます。
ローカル版の実装
人格をプロンプトで固定
講評は Amazon Bedrock の Claude Haiku 4.5 で生成します。
システムプロンプトでは、次の点を指定しました。
- 冷静で簡潔に話し、数値と根拠で語る
- 一人称は「当機」、受験者は「あなた」と呼ぶ
- 講評は3〜4文、200字以内で、最後に配属先を述べる
- 与えたスコア以外の事実は作らない
- 音声で読み上げるため、見出しや記号、Markdown は使わず平文だけで出力する
最後の指定は、最初の実行で必要になりました。
指定なしだと、講評に「講評」のようなMarkdown記号が混ざり、画面表示にも読み上げにも出てしまいました。
呼び出しは Converse API を使っています。
res = bedrock.converse(
modelId="us.anthropic.claude-haiku-4-5-20251001-v1:0",
system=[{"text": PERSONA}],
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": 400, "temperature": 0.7},
)
声は VOICEVOX の青山龍星
音声は VOICEVOX エンジンに合成させます。
AIハヤトの声を担当する高山みなみさんの声は、そのままでは使えません。
そのため、ローカル版では VOICEVOX の青山龍星、公開版では Amazon Polly の Takumi と、声を変えました(クレジットは末尾に記載しています)。
手元のエンジンは、既定の 50021 ではなく 50032 番ポートで動いていました。
話者一覧を取得すると、青山龍星のノーマルは ID 13 でした。
curl -s localhost:50032/speakers
合成は、audio_query で読み上げ用のデータを作り、speedScale を設定してから synthesis に渡す2段階です。
q = json.loads(urlopen(Request(
f"{URL}/audio_query?speaker=13&text={quote(text)}", method="POST")).read())
q["speedScale"] = 1.0
wav = urlopen(Request(f"{URL}/synthesis?speaker=13",
data=json.dumps(q).encode(),
headers={"Content-Type": "application/json"}, method="POST")).read()
VOICEVOX に繋がらないときは、Amazon Polly の Takumi に切り替えます。
Polly 側は SSML の prosody で速さを指定できます。
課題も見つかりました。長い講評を合成すると、手元の環境では十数秒かかります。
講評が画面に出てから声が始まるまで、待ち時間が生じます。
AWSへの公開
構成
公開版は、次の構成で AWS CDK(Python)から作りました。
スタックは us-east-1 に作っています。
CloudFront 用の AWS WAF の Web ACL は、us-east-1 に作る必要があります。
- 画面: Amazon S3(非公開)を Amazon CloudFront の OAC で配信
- API: AWS Lambda(Python 3.13)の Function URL を IAM 認証にし、CloudFront の
/api/*から OAC で呼ぶ - 権限: Lambda に
bedrock:InvokeModel(Haiku 4.5 の基盤モデルと推論プロファイル)とpolly:SynthesizeSpeechだけを付与 - 制限: AWS WAF のレート制限
Lambda の中身は、ローカルのサーバーとほぼ同じです。
音声は Polly(Takumi)だけを使います。
同時実行数の予約が通らない
公開したエンドポイントからは、誰でも Bedrock と Polly を呼べます。
課金の上限を絞るため、最初は Lambda に予約済み同時実行数(5)を設定しました。
デプロイは次のエラーで失敗しました。
Resource of type 'AWS::Lambda::Function' ... is not updatable with parameters provided.
(HandlerErrorCode: NotUpdatable)
原因を調べるため、アカウントの設定を確認しました。
aws lambda get-account-settings --region us-east-1 --query AccountLimit
同時実行数の上限が10で、未予約分も10でした。
予約すると、未予約分が Lambda の下限を割るために設定できないと判断しました。
これは私の推測で、公式の説明までは確認していません。
予約の設定を外すと、デプロイは通りました。
上限の絞り込みは、AWS WAF に任せています。
OAC 経由の POST には SHA-256 ヘッダーが必要
CloudFront から OAC で AWS Lambda の Function URL に POST する場合は、クライアントがリクエストボディの SHA-256 ハッシュを x-amz-content-sha256 ヘッダーに付ける必要があります。
公式ドキュメントの記述に従ったもので、ヘッダーなしの挙動は今回検証していません。
画面側は、Web Crypto API でハッシュを計算して付けています。
async function post(path, body) {
const data = JSON.stringify(body);
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data));
const hex = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
return fetch(path, {
method: "POST",
headers: { "Content-Type": "application/json", "x-amz-content-sha256": hex },
body: data,
});
}
crypto.subtle は HTTPS か localhost でしか使えないため、ローカルでも公開版でも動きます。
Function URL には InvokeFunction の権限も追加
2025年10月以降に作る Function URL には、lambda:InvokeFunctionUrl に加えて lambda:InvokeFunction の権限も必要です。
新規の URL でこれが足りないと 403 になると把握していたため、CDK のスタックには、CloudFront のディストリビューションを条件にした InvokeFunction の許可を明示的に追加しました。
この権限がなくても動くかどうかは、検証していません。
AWS WAF のレート制限
予約済み同時実行数が使えないため、CloudFront に AWS WAF のレート制限を付けました。
- 対象:
/api/で始まるパスだけ。画面本体は制限しません - 条件: 同じ IP から5分間に100リクエストを超えたらブロック(403)
- 根拠: 1回のプレイで API を10回前後(デモ動画の撮影時は音声8回と講評1回の計9回)呼ぶため、通常の利用には影響しません
実際に効果があるか、存在しないパスに160回リクエストして確かめました。
Bedrock や Polly を呼ばないパスです。
- 結果: 404 が157回、403 が3回
- 45秒待ってから再度送ると、すべて 403
100回を超えたあともしばらくは通っていたため、制限は即時には反映されません。
また、この検証で自分の IP もブロックされました。
解除までにかかる時間は、今回は確認していません。
なお、WAF は課金の上限を保証するものではありません。
Web ACL とルールには固定費もかかります。
金額は AWS の料金ページで確認してください。
デモ動画の撮影
デモ動画は、ローカル版と公開版の2本を、Playwright でブラウザ操作を録画して作りました。画面の録画には音声が入らないため、読み上げ音声は別に集めて合成します。
- 画面: Playwright の動画録画(webm)。ミッションの回答は、画面の内容を読み取って自動で入力
- 音声: ページ側で
fetchを包み、/api/speakの応答を保存 - 合成: ffmpeg で、音声を再生された時刻に合わせて動画に重ね、mp4 に変換
ローカル版
公開版
AIキャラクターの名前
最初の名前は「カガリ」でした。その後、次の順に変えています。
- カナメ: 同名のアプリやサービスが複数ありました。業務ソフトの「KANAME簡単レポート」、予算申請のアプリ「KANAME Hub」、AI開発の手法「Kaname」などです。
- サトリ: 同名のアプリ「SATORI」があるため、候補から外しました。
- 造語の5候補(Clavis、Noema、Miras、Solva、Verita): 検索したところ、すべてに同名の AI アプリやサービスがありました。
今回検索した範囲では、発音しやすい短い名前はいずれも既存の AI 系のものと重なりました。名前だけで被りを避けるのは難しいと判断し、コードネーム型にしました。
「カナメ-07」の「-07」は、その結果です。
数字自体に意味はなく、型番の形にするために付けました。
ここまでで、冒頭の問いの1つに答えられます。
名前に「-07」が付いているのは、名前の被りを型番で区別するためです。
商用化や商標登録を考える場合は、特許庁の J-PlatPat で別途確認する必要があります。
声が違う理由と動作確認
もう1つの問いです。
ローカル版と公開版で声が違うのは、VOICEVOX のエンジンが手元のパソコンで動いているためです。
Lambda からは呼べません。公開版でも青山龍星にするには、VOICEVOX エンジンをクラウド上に立てる必要があり、
今回はそこまでやっていません。公開版の音声は、Amazon Polly の Takumi です。
動作確認の結果です。
- 公開版のページ、音声(MP3)、講評が返ることを、curl とブラウザの両方で確認しました。
- デモ動画は、ローカル版が約93秒、公開版が約78秒です。
- 確認していないのは、音声の実際の聞こえ方、OAC の POST でヘッダーがない場合の挙動、
InvokeFunctionの権限の要否です。
不要になったときは、次のコマンドで作成したリソースを削除できます。
cd infra && npx aws-cdk@latest destroy
ハヤトの体験から変えたところ
最後に、冒頭で触れた『VIVANT』のAIハヤトの体験と、今回作ったものを比べます。
- 同じにしたところ: ミッションをこなし、最後にAIが適性を数値で告げる流れ
- 変えたところ:
- キャラクター: ハヤトではなく、オリジナルの「カナメ-07」
- 声: 高山みなみさんの声はそのまま使えないため、ローカル版は VOICEVOX の青山龍星、公開版は Amazon Polly の Takumi
- 名前: 既存のものと被らないよう、コードネーム型
- 作らなかったところ: ハヤトの場面の映像づくり(TBS は Veo 3 と Nano Banana を使用)。今回のアプリに映像生成はありません
- 自分で考えたところ: イベントのミッションの中身は確認しておらず、今回の4つのミッションと講評の指示は自分で考えたものです
AIハヤトの体験のうち、参考にしたのは流れだけです。
TBS が Google の AI で作っている映像づくりの部分とは別に、診断と声の部分を、Amazon Bedrock、Amazon Polly、Amazon CloudFront、AWS Lambda で動くところまで作りました。
キャラクターと声は別のものにしています。
最後まで読んでいただきありがとうございました。
参考とクレジット
- 参考: VIVANT「ヴィヴァン イマーシブ ミッション」 | あみゅーぜん(イベントの内容、AIハヤトの声優)
- 参考: 「VIVANT」で生成AIが使われたのはこのシーンだ! | 調査情報デジタル(生成AIが使われた場面、Veo 3 と Nano Banana の使い方。本文は未確認で、要約で確認)
- 参考: TBS『VIVANT』制作陣が語る、Google の AI と描くエンタメの未来 | Gemini
- 参考: TBSテレビ、「Google Cloud Next Tokyo 26」に出展 | 電波タイムズ
- 参考: Google Cloud Next Tokyo 26 海賊ドライブスルー&VIVANT 変身レポート | iret.media
- 音声のクレジット: VOICEVOX:青山龍星
- 『VIVANT』は TBS の作品です。本記事は非公式の個人検証で、作品・イベントの制作側や出演者の方々とは関係ありません。
