Firebase Genkit 入門ついでに AI に短編ドラマ作らせてみた
こんにちは。tmassh です。
Google Developers Group Kwansai Advent Calendar 2024 8日目になります。
所用で2日分 (6, 7日分) サボってしまいました。。。またどこかで埋めます。
今日はしばらく忙しくて触れてなかった Firebase Genkit に入門ついでに短編アニメを作らせてみました。
Genkit とは
Genkit はアプリへの AI 機能の組み込みを支援するフレームワークです。2024年12月現在で単純な生成、チャット作成、エージェント作成、構造化出力、プロンプトテンプレートなどなどをサポートしています。Genkit はローカルで動作する GUI が用意されていて、AI 機能を持つコンポーネントを迅速にテストすることも可能です。
単に、アプリに組み込むための API だけでなく、こういった周辺ツールまで用意してくれているのは嬉しいですね。
Genkit のスタートガイドを見ながら進めると、@genkit-ai/googleai
をインストールすることになりますが、今回は Vertex AI プラグインを使いたいので、こちらのガイドを見ながら進めます。
画像を生成できるようにするまで
Firebase Genkit のドキュメントは基本的に Google AI を使用していることが前提で進められるのですが、画像生成のモデルは Vertex AI の SDK でしか選択ができませんでした。その際、インストールに少し迷ったので整理をしておきます。
今回は短編アニメを作らせたいので、画像生成してもらえるようにする必要があります。ドキュメントを確認して必要そうなものをインストールしていきます。
npm install genkit @genkit-ai/vertexai data-urls
npm i --save-dev @types/data-urls
gcloud
コマンドでプロジェクトの設定と認証情報を設定します。
# gcloud component のアップデート
$ gcloud components update
# 初期化とプロジェクトの設定
$ gcloud init
# 認証情報の設定
$ gcloud auth application-default login
これで "メディアの生成" のサンプルコードが動くようになります。なお、制限があるのか画像生成はできる時とできないときがありました。完全自動でアニメを作りきるのはもう少し先になりそうです。
シナリオとシーン・セリフを考えさせる
とりあえずこんなプロンプトを考えてみます。
あなたは優秀なシナリオライターです。要件を忠実に守ったシナリオを作り、シナリオの構成と1話目の内容を返します。シナリオ全体は12話程度の編成で1話あたりは8つのシーン構成に収めてください。シーンと登場人物のセリフは指定のフォーマットでまとめられます。シーンの情景はアニメ画家がすぐにペンを走らせることができる程度で詳細に、必ず「Draw the following scene in an anime style. 」から英語で指示をしてください。
ここではシナリオ全体の出来と1話目のクオリティを確認することにしました。コンテキストさえ渡せば2話目は繰り返しで作ってもらえるので、この段階では1話目のみで良いと思います。また、次の工程で生成してもらいたい絵のプロンプトも同時に出力させます。
画像生成は試したところ日本語のプロンプトだと出力がイマイチでした。なので、必ず Draw the following scene in an anime style.
から始めてもらいます。
Genkit に付属している local のツールを使えば素早くプロンプトの出力を確認することができます。プロンプトを保存することもできるので管理も楽ですね!
出力はこのような感じでした。
## 無人島サバイバル: ブラック企業戦士の逆襲
**概要:**
東京でブラック企業に勤めるサラリーマン、田中太郎は、会社の慰安旅行で乗った船が嵐に遭い、無人島に漂着してしまう。持ち物はスマホと会社の資料ファイルのみ。文明から切り離された過酷な環境で、田中は会社で培った強靭なメンタルと、驚くべき適応能力でサバイバル生活を始める。しかし、無人島には思わぬ危険が潜んでいた。
**構成:**
1. **漂流**: 嵐に遭い、無人島に漂着する。
2. **絶望**: 周囲を見渡して絶望する。
3. **サバイバル開始**: 食料、水、住居確保に奮闘する。
4. **孤独**: 孤独に耐え、精神的に追い詰められる。
5. **発見**: 島の秘密を発見する。
6. **決意**: 生き残るための決意をする。
7. **試練**: 予想外の試練に直面する。
8. **救助**: 漁船に助けられる。
9. **変化**: 島での経験を通して変化した田中。
10. **帰還**: 会社に復帰する。
11. **決断**: 会社を辞める決意をする。
12. **新たな道**: 新しい人生を歩み始める。
**1話: 漂流と絶望**
**シーン1: 会社の慰安旅行**
**場所:** 豪華客船のデッキ
**登場人物:** 田中太郎(30代、サラリーマン)、上司(40代、厳しい)、同僚A(20代、お調子者)、同僚B(20代、真面目)
**セリフ:**
田中: (上司にビールを注ぎながら) ああ、やっぱり海はいいですね!
上司: なんだそれ。仕事しろよ!
同僚A: (笑いながら) 田中さん、今日は飲みすぎないでね!
同僚B: (静かに) 嵐が来てるみたいですね。
**Draw the following scene in an anime style. **
A group of people are enjoying a cruise on a luxurious ship. The sky is blue and the sea is calm. The characters are dressed in casual clothes and are having drinks and snacks.
すでに神ドラマの予感がしますね
出力を json に限定する
Genkit では出力を json に限定することができます。結構複雑な構造でも忠実に出してくれるので便利です。今回はこんな構造を考えてみました。
構造![]
{
"firstStory": [
{
"lines": [
{
"nameWhoSaid": "{ 名前 }"
"line": "{ 台詞 }"
}
],
"scenery": "Draw the following scene in an anime style. ~~"
}
],
"scenerio": [
{
"description": "{ 概要 }",
"title": "{ タイトル }",
}
]
}
これを出力してもらうように指定します。
const scenerioSchema = z.object({
title: z.string(),
description: z.string(),
});
const lineSchema = z.object({
name: z.string(),
line: z.string(),
});
const storySchema = z.object({
scenery: z.string(),
lines: z.array(lineSchema),
});
const { output } = await ai.generate({
model: gemini15Flash,
system: "",
prompt: "{ プロンプト }",
output: {
format: "json",
schema: z.object({
scenerio: z.array(scenerioSchema),
firstStory: z.array(storySchema),
}),
},
});
console.log(output);
しっかり指定したフォーマットで返してくれます。
{
firstStory: [
{
lines: [Array],
scenery: 'Draw the following scene in an anime style. A man named Tanaka is lying on a white sandy beach. He is wearing a suit and is covered in seawater. He has a confused look on his face and he is looking around at the endless blue sea and sky. The sun is shining brightly and the waves are gently lapping at the shore.'
}...
],
scenerio: [
{
description: '都会で働くサラリーマン田中は、仕事に追われる毎日を送っていた。\n' +
'過酷な労働環境、理不尽な上司、終わらない残業…、\n' +
'田中は、心身ともに疲れ果てていた。\n' +
'そんなある日、取引先の接待で乗ったヨットが嵐に巻き込まれ、\n' +
'田中は、無人島に漂着してしまう。\n' +
'しかし、田中は、ブラック企業で培った強靭なメンタルで、\n' +
'この絶望的な状況を生き抜こうと決意する。',
title: '漂流サラリーマン'
}...
]
}
上記で指定したシーンではないですが、Imagen3 で画像も生成してみました。プロジェクトの制限なのか上手く画像が作れる時と作れない時がありましたが、絵はすごく良さそうに見えますね。
おわりに
Genkit を使うと AI を組み込んだ機能を簡単にプログラムすることができました。複数のモデルを簡単にスイッチできるところや、構造化などの出力形式の制限も楽につけられて、管理も楽なのでこれはどんどんいろんなものを作りたくなりますね。
Genkit にはフローの作成やデバッグといった機能もあるようなので、次はその辺りを試してみます!
Discussion