Gemma 4でRPGストーリー自動生成アプリを作った ― 品質は粗い。でも改善策は見えている

ゲーム制作技術
結論:Gemma 4(2GBモデル)でRPGのストーリーをスマホ上でリアルタイム生成するデモアプリを作った。NPCのセリフ、ストーリー展開、選択肢の全てをAIが生成する。品質は「それっぽいけど粗い」のが正直な感想。ここでは現時点の実力と、品質を上げるための具体的な改善策を考察する。

前回の記事でGemma 4を使ったAIオセロ対戦アプリを紹介した。今回は「ゲームAI」の次のステップとして、RPGのストーリー自体をAIに生成させるアプリを作った。

何を作ったか

「PuzzDot AI Story Demo」——Gemma 4がRPGのストーリーをリアルタイムで紡ぐデモアプリだ。

PuzzDot AI Story Demo タイトル画面

仕組みはシンプル:

  • 町を選ぶ → ストーリーが始まる
  • NPCが喋る → セリフはGemma 4がリアルタイム生成
  • 選択肢が出る → 選択肢もAIが生成。選んだ結果で展開が変わる
  • 全てがスマホ上で完結。サーバー不要、オフラインで動く
項目スペック
モデルGemma 4 E4B
モデルサイズ約2GB
動作環境スマートフォン(オフライン)
町の数6つ
生成内容NPCセリフ + ストーリー展開 + 選択肢

6つの町が選べる

町選択画面 - 6つの町

はじまりの村、森の村、港町、山岳都市、王都、魔法学院都市。それぞれにピクセルアートのマップが用意されている。町を選ぶとストーリーが始まる。

実際のストーリーを見てみる

ストーリー1:「神聖な使者と古の紋章」— ガルド(長老)

ガルド(長老)のセリフ

村の長老ガルドが使者の到来について語る。セリフは重厚:

「風の音を遮りながら、静かに使者を迎え入れる。その瞳には深い懸念の色が浮かんでいる)遠き者よ、来たか。神聖なる光を纏うとは、その言葉に重みがある。しかし、貴殿が提示する『紋章』の事柄は、我々の村の静寂を根底から揺るがすものだ。」

ストーリー1の選択肢

選択肢もAIが生成する:

  • A: 予言を信じ、村の静寂を破って真実を探る
  • B: 使者の言葉だけを鵜呑みにし、安全を優先して慎重になる

ストーリー2:「濁る井戸の謎」— ローザ(宿屋女将)

ローザ(宿屋女将)のセリフ

「勇者様、本日はお越しいただきありがとうございます。ですが、顔色が悪いのですね。どうか、お顔を上げてください。今、村の心臓である井戸の水が、みるみるうちに濁り、枯れ始めています。」

ストーリー2の選択肢

勇者の返答も生成される:

「古の泉ですか。危険な旅になりそうですが、村の命を救うためには試さなければなりません。」

ストーリー3:「古の鍵の囁き」— リオ(少年)

リオ(少年)のセリフ

「(目をキラキラさせながら、勇者に向かってささやくように)勇者様!聞いてください!使者が、とても大切なことを教えてくれました。村の古い井戸の底に、魔法の鍵が隠されているって!本当に、すごい秘密ですよ!」

リオは少年らしい好奇心と興奮で話す。キャラの年齢設定がセリフに反映されている点は評価できる。

ストーリー4:「夜の使者と囁かれる予兆」— ハンナ(野菜売り)

ハンナ(野菜売り)のセリフ

「(夜の闇を背景に、少し声を潜めて)勇者さん、この静けさ、いつもと違うんじゃないかしら?噂じゃ、王国から使者が来るって。大きな変化が起きるって囁かれてるわね。」

前回のオセロ記事のハンナと同じキャラが、今度はRPGの住人として登場。野菜売りという庶民的な立場から噂話を語るのは自然だ。

正直な評価:品質はまだ粗い

率直に言う。ストーリーの品質は「それっぽいけど浅い」。

良い点

  • キャラの肩書きに合ったトーンが出ている(長老は重厚、少年は興奮、女将は丁寧)
  • 選択肢が文脈に合っている(的外れな選択肢は出なかった)
  • ト書き(「目をキラキラさせながら」等)が自然に入る
  • 毎回違うストーリーが生成される(再現性がない=飽きにくい)

課題

  • 展開がテンプレ的:「魔王復活の予兆」「古の封印」「神聖な使者」——ファンタジーRPGの定番要素を並べているだけ
  • キャラの差別化が弱い:名前と肩書きが違うだけで、根本的な性格の違いが薄い
  • 選択肢が「勇気 vs 慎重」の二択に偏る:もっと意外な選択肢がほしい
  • ストーリー間の繋がりがない:ストーリー1の選択がストーリー2以降に影響しない
  • 文章が冗長:2GBモデルの限界として、簡潔な表現が苦手

品質を上げるには? ― 5つの改善策

現時点の「粗さ」はモデルの限界ではなく、プロンプト設計とシステム設計で改善できる部分が大きい。具体的な改善策を考察する。

改善1:キャラクターシートを詳細にする

現状はおそらく「ガルド(長老)」程度の情報しかAIに渡していない。これを以下のレベルまで詳細化する:

名前: ガルド
役職: はじまりの村の長老(72歳)
性格: 慎重で保守的。村を守ることが最優先。
   外部の人間を信用しない。
口調: 「〜じゃ」「〜であろう」古風な話し方。
   決して「!」は使わない。
秘密: 30年前に村を襲った災厄を知っている唯一の生存者。
弱点: 孫のリオのことになると判断が鈍る。

「秘密」と「弱点」が重要だ。これがあるとストーリーに深みと伏線が生まれる。AIは与えられた設定を反映する能力は高い。情報が足りないだけだ。

改善2:Few-shotプロンプトで「良いセリフ」を教える

AIに「このレベルのセリフを出してくれ」という具体例を数個入れるだけで品質が跳ね上がる。

// 良い例(具体的、感情が見える)
ローザ: 「あんた、またここに来たの?……いいわ、座りなさい。
    今日のスープは少し塩が強いけど、文句は言わないでね。」

// 悪い例(テンプレ的、誰が言っても同じ)
ローザ: 「勇者様、ようこそいらっしゃいました。
    何かお困りのことはございますか?」

現状のセリフは「悪い例」寄りだ。Few-shotで具体例を見せれば、AIは「ああ、こういうトーンか」と理解する。

改善3:選択の結果を記憶させる(コンテキスト管理)

今のアプリは各ストーリーが独立している。ストーリー1で「予言を信じる」を選んでも、ストーリー2には影響しない。

改善策:プレイヤーの選択履歴をプロンプトに含める。

これまでのプレイヤーの行動:
- ガルドの予言を信じて調査を開始した(勇敢な選択)
- ローザの頼みで古の泉に向かった(村人を助ける傾向)

→ NPCはプレイヤーを「行動力のある勇者」として扱うこと

これだけで「あの時助けてくれた勇者様」「あんた、前も無茶したでしょ」みたいなセリフが生まれる。ストーリーが「点」から「線」になる。

改善4:世界設定をRAGで渡す

「魔王復活」「古の封印」のようなテンプレ展開になるのは、AIが独自の世界設定を持っていないから。汎用的なファンタジー知識で埋めてしまう。

解決策:独自の世界設定をJSON/テキストで用意し、プロンプトに含める。

  • この世界では魔法は「歌」で発動する
  • 王国は3つに分裂しており、統一戦争の最中
  • モンスターは存在しない。敵は全て人間

世界設定が具体的であるほど、AIは「テンプレ」から外れた独自のストーリーを生成する。2GBモデルでもコンテキスト内の情報は正確に使える。

改善5:出力フォーマットを構造化する

現状はAIに自由に喋らせているが、JSON形式で出力を構造化すると安定性が上がる。

{
  "speaker": "ガルド",
  "emotion": "不安",
  "action": "杖を握りしめながら",
  "dialogue": "この紋章は……30年前に封じたはずじゃ。",
  "choices": [
    {"text": "30年前に何があったのか聞く", "tone": "探究"},
    {"text": "紋章を見せずに立ち去る", "tone": "回避"}
  ]
}

emotion(感情)とaction(動作)を分離することで、ゲーム側でキャラのアニメーションや表情を連動させることもできる。Gemma 4は関数呼び出し・JSON出力に公式対応しているので、この構造化は実用的だ。

プログラム側で整合性を保証する仕組み

上の5つはプロンプトの改善策だが、もう1つ重要なのがプログラム(ゲームエンジン)側でストーリーの一貫性を管理する仕組みだ。AIに全てを任せると矛盾が起きる。プログラムが「骨格」を握り、AIは「肉付け」だけを担当する設計が現実的だ。

ストーリーグラフ:展開の骨格をプログラムが管理する

AIに「次の展開を自由に考えて」と任せると、前のシーンと矛盾したり、突然脈絡のないイベントが発生する。解決策はストーリーの大枠をグラフ構造で事前に定義し、AIにはその枠内でセリフと演出だけを生成させること。

// ストーリーグラフ(プログラム側で定義)
シーン1: "村の長老が異変を告げる"
  → 選択A → シーン2a: "井戸を調査する"
  → 選択B → シーン2b: "旅人に情報を聞く"

シーン2a: "井戸の底で紋章を発見する"
  → シーン3: "紋章の意味を長老に聞く"

// AIの仕事:各シーンのセリフ・描写・選択肢の文面を生成
// 「何が起きるか」はプログラムが決める
// 「どう語るか」はAIが決める

こうすれば「井戸を調査したのに、次のシーンで井戸のことを知らないNPC」みたいな矛盾が起きない。

状態管理:フラグとパラメータで世界の状態を追跡

RPGのプログラマーなら馴染み深いフラグ管理をAIストーリーにも適用する。

// ゲーム状態(プログラムが管理)
{
  "well_investigated": true,
  "crest_found": true,
  "gardo_trust": 3, // ガルドの信頼度(0-5)
  "rosa_quest_done": false,
  "player_reputation": "cautious" // 慎重派
}

// この状態をプロンプトに渡す
"プレイヤーは井戸を調査済みで紋章を発見しています。
 ガルドの信頼度は高め(3/5)。
 プレイヤーの行動傾向は「慎重派」です。
 ローザのクエストはまだ未完了です。
 これらを踏まえてセリフを生成してください。"

AIは毎回この状態を参照するので、「さっき井戸で見つけた紋章のことじゃが…」「あんた、慎重な人ね。でもたまには冒険も必要よ」といった文脈に沿ったセリフが自然に出る。

要約バッファ:過去の会話を圧縮して渡す

2GBモデルのコンテキストウィンドウは限られている。全ての会話履歴を渡すことはできない。解決策はプログラム側で過去の会話を要約し、コンパクトにしてプロンプトに含めること。

// 直近の会話:そのまま渡す(3ターン分)
ガルド: 「この紋章は30年前に封じたはずじゃ」
勇者: 「30年前に何があったのか教えてください」
ガルド: 「あの夜、黒い霧が村を覆った…」

// それ以前:プログラムが要約して渡す
"過去の要約: 勇者は村に到着し、ローザから井戸の異変を聞いた。
 井戸を調査して古い紋章を発見。ガルドに相談している。"

直近3ターンはそのまま渡して自然な会話の流れを維持し、それ以前は要約で渡してコンテキストを節約する。要約の生成自体もGemma 4にやらせることができる(セリフ生成とは別のAPIコール)。

キャラクター一貫性エンジン:口調と性格のブレを防ぐ

AIストーリー生成で最も崩れやすいのがキャラの一貫性だ。長老が突然若者言葉で喋ったり、慎重派のキャラが急に無謀な発言をする。これをプログラム側で防ぐ。

// キャラクター定義(プログラムが保持)
{
  "name": "ガルド",
  "must_use": ["じゃ", "であろう", "〜のう"],
  "must_not_use": ["!", "マジ", "ヤバい", "すごい"],
  "personality": "cautious",
  "max_excitement": 2, // 感情の振れ幅(0-5)
  "speech_length": "long" // 長老は長く語る
}

// リオの場合
{
  "name": "リオ",
  "must_use": ["!", "勇者様"],
  "must_not_use": ["じゃ", "であろう"],
  "personality": "curious",
  "max_excitement": 5,
  "speech_length": "short" // 少年は短く喋る
}

生成後にプログラムがチェックする:

  • must_useが1つも入ってない → 再生成(「じゃ」が入ってないガルドはガルドじゃない)
  • must_not_useが入ってる → 該当部分を置換 or 再生成
  • 感情の振れ幅がmax_excitementを超えてる(冷静なキャラが「!」を連発)→ 再生成
  • セリフ長が設定と大きく乖離 → 切り詰め or 再生成

さらに、直近のそのキャラのセリフ3つをプロンプトに含めることで、AIが自分の過去の口調を参照できるようにする。「さっき自分がこう喋ったから、次もこのトーンで」という一貫性が生まれる。

整合性チェッカー:AIの出力をプログラムが検証する

最後の砦として、AIが生成したセリフをプログラムが検証するレイヤーを入れる。

  • 登場してないキャラの名前が出たら → 再生成
  • 未発見のアイテムに言及したら → 再生成
  • 死亡済みキャラが喋ったら → 再生成
  • セリフが長すぎる(200文字超)→ 切り詰めて再生成

単純な文字列マッチングでチェックできるので、処理コストはほぼゼロ。AIの「暴走」をプログラムが抑える安全弁だ。

改善前後の品質イメージ

現状(改善前)改善後(想定)
セリフ品質テンプレ的、誰が言っても同じキャラ固有の口調・秘密が反映
ストーリー展開「魔王」「封印」の定番パターン独自世界設定に基づく展開
選択肢「勇気 vs 慎重」の二択意外性のある多様な選択
連続性各ストーリーが独立過去の選択が影響する
モデルサイズ2GB(変わらない)

重要なのは、これらの改善は全てプロンプトとシステム側の工夫であり、モデルを大きくする必要がないということだ。2GBのまま品質を大幅に上げられる余地がある。

筆者の本音:2GBモデルの「粗さ」こそが面白い

筆者(uc)が正直に感じたのは、「このストーリー、面白い!」ではなかった。「2GBでここまで出るのか」という技術的な驚きの方が大きかった。

ガルドの重厚なセリフもローザの丁寧語もリオの興奮も、全部2GBのモデルがスマホ上で生成している。サーバーなし、通信なし。5つのストーリーが全て台本なしのリアルタイム生成。

品質が粗いのは事実だ。でもそれはモデルの限界ではなくプロンプト設計の未熟さだと考えている。キャラクターシートを充実させ、世界設定を渡し、選択履歴を管理すれば、同じ2GBモデルでも全く違うレベルのストーリーが出るはずだ。

前回のオセロアプリで「AIキャラが喋る」可能性を示した。今回のストーリーデモで「AIがストーリーを紡ぐ」可能性を試した。まだ粗削りだが、スマホ1台で動くAI RPGの原型は、もうここにある。

まとめ

  • Gemma 4(2GBモデル)でRPGストーリーをリアルタイム生成するアプリを作った
  • NPCセリフ、ストーリー展開、選択肢を全てAIが生成。台本なし
  • 6つの町×複数キャラ×分岐で毎回違うストーリーが展開
  • 品質は正直「それっぽいけど粗い」。テンプレ展開・キャラ差別化の弱さが課題
  • プロンプト改善策5つ:キャラシート詳細化、Few-shot、選択履歴、世界設定RAG、出力構造化
  • プログラム側の整合性管理:ストーリーグラフ、フラグ管理、要約バッファ、キャラ一貫性エンジン、整合性チェッカー
  • 全ての改善はプロンプトとプログラムの工夫で、モデルサイズは2GBのまま可能
  • スマホ1台で動くAI RPGの原型が、すでにここにある

参考ソース

コメント

タイトルとURLをコピーしました