GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-05-12中級

Gemini 3.2 Flash のビジョン機能でアプリUIのリリース前チェックを自動化した実践記録

Gemini 3.2 Flash のマルチモーダル入力を使い、iOS/Android アプリのスクリーンショットを自動でビジュアルQAするスクリプトを実装した実践記録。個人開発者がリリース前に見落としがちなUIの崩れ・文字切れ・ダークモード差異を Python 30行で検出する方法を紹介します。

Gemini 3.2 Flashマルチモーダル24モバイルアプリ3QAスクリーンショット2個人開発91Python38

アプリをリリースするたびに、公開後にユーザーから「ボタンが切れてる」「ダークモードで文字が消える」と報告が届く——そういう経験を何度か重ねてきました。

わたしが個人で運営している壁紙・癒し系アプリは、2014年頃から少しずつ積み上げて、今では累計5,000万ダウンロードを超えるくらいのサイズになっています。機能はシンプルですが、iPhone の機種ごとに解像度が微妙に異なるため、スクリーンショットの見た目チェックがリリース工程の中で思いのほか時間を取るようになりました。

最初は Xcode の Simulator を手動で回して目視確認していたのですが、機種が増えるにつれて現実的でなくなってきた。かといって、スナップショットテストのライブラリを導入するほどの規模でもありません。

そのちょうど中間を埋めてくれたのが、Gemini 3.2 Flash のマルチモーダル機能です。スクリーンショットの画像を渡して「何か問題があるか判断して」と聞くと、驚くほど的確に答えが返ってきます。

何をチェックしたかったのか

わたしが自動化したかったのは、以下の3パターンです。

  • 文字の切れ・はみ出し: フォントサイズがデバイスの設定で大きくなっていたとき、ラベルがボタンの外にはみ出していないか
  • ダークモードの視認性: ライトモードで白い背景に白いテキストにはなっていないか(これを本番でやらかしたことがある)
  • レイアウト崩れ: セーフエリアを無視して要素が重なっていないか

これらを人間が目で確認しようとすると、機種 × ライト/ダーク × ロケールの組み合わせで数十枚のスクリーンショットを見ることになります。Gemini にまとめて渡して「おかしいところはあるか」と聞く方が、確実に早い。

基本的な実装

最初に作ったのは、スクリーンショット1枚を渡してレビューを返すシンプルなスクリプトです。

import google.generativeai as genai
from pathlib import Path
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
model = genai.GenerativeModel("gemini-3.2-flash")
 
def review_screenshot(image_path: str, context: str = "") -> dict:
    """
    スクリーンショットのUIを自動レビューする。
    context に画面の説明(例: 設定画面、ダークモード)を渡すと精度が上がる。
    """
    image_data = Path(image_path).read_bytes()
    
    prompt = f"""
以下はモバイルアプリのスクリーンショットです。
{f"画面の説明: {context}" if context else ""}
 
UIの品質を以下の観点でチェックしてください:
1. テキストの切れ・はみ出しがないか
2. 要素の重なり・レイアウト崩れがないか
3. テキストと背景のコントラストが十分か(特にダークモード)
4. ボタンやタップ領域が適切なサイズか
 
問題があれば箇条書きで、なければ「問題なし」と返してください。
出力はJSON形式: {{"status": "ok" or "issues_found", "issues": ["..."]}}
"""
    
    response = model.generate_content([
        prompt,
        {"mime_type": "image/png", "data": image_data}
    ])
    
    # レスポンスからJSONを抽出
    import json, re
    json_match = re.search(r'\{.*\}', response.text, re.DOTALL)
    if json_match:
        return json.loads(json_match.group())
    return {"status": "parse_error", "issues": [response.text]}
 
# 使い方
result = review_screenshot(
    "screenshots/settings_dark_iphone15pro.png",
    context="設定画面、ダークモード、iPhone 15 Pro"
)
print(result)
# {"status": "issues_found", "issues": ["第3行のラベルテキストが枠外にはみ出しています"]}

これだけで動きます。gemini-3.2-flashgemini-2.0-flashgemini-2.5-flash より視覚的な説明の精度が上がっていて、具体的にどの要素が問題かを指摘してくれます。

複数スクリーンショットをまとめてチェックする

単体のチェックが動いたら、次はリリース前のスクリーンショット一式をまとめて処理するスクリプトを作りました。

import google.generativeai as genai
from pathlib import Path
import json, re, time
from typing import NamedTuple
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
model = genai.GenerativeModel("gemini-3.2-flash")
 
class ScreenshotCheckResult(NamedTuple):
    file: str
    status: str
    issues: list[str]
 
def batch_review(screenshot_dir: str, config_json: str = None) -> list[ScreenshotCheckResult]:
    """
    ディレクトリ内のスクリーンショットをまとめてレビュー。
    config_json: {"ファイル名": "画面の説明"} の形式で各ファイルに説明を付与できる
    """
    configs = {}
    if config_json:
        with open(config_json) as f:
            configs = json.load(f)
    
    screenshots = sorted(Path(screenshot_dir).glob("*.png"))
    results = []
    
    for i, path in enumerate(screenshots):
        context = configs.get(path.name, "")
        print(f"[{i+1}/{len(screenshots)}] {path.name} をチェック中...")
        
        image_data = path.read_bytes()
        prompt = f"""
モバイルアプリのスクリーンショットのUIレビューを行ってください。
{f"画面の説明: {context}" if context else ""}
 
チェック項目:
- テキストの切れ・はみ出し
- 要素の重なり・レイアウト崩れ  
- テキストと背景のコントラスト不足
- ボタン/タップ領域のサイズ問題
 
JSON形式で返してください: {{"status": "ok" or "issues_found", "issues": ["..."]}}
"""
        
        try:
            response = model.generate_content([
                prompt,
                {"mime_type": "image/png", "data": image_data}
            ])
            json_match = re.search(r'\{.*\}', response.text, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
                results.append(ScreenshotCheckResult(
                    file=path.name,
                    status=data.get("status", "unknown"),
                    issues=data.get("issues", [])
                ))
        except Exception as e:
            results.append(ScreenshotCheckResult(
                file=path.name,
                status="error",
                issues=[str(e)]
            ))
        
        # レート制限対策: 1秒待機
        if i < len(screenshots) - 1:
            time.sleep(1)
    
    return results
 
def print_report(results: list[ScreenshotCheckResult]):
    """チェック結果をサマリーレポートとして出力"""
    ok_count = sum(1 for r in results if r.status == "ok")
    issue_count = sum(1 for r in results if r.status == "issues_found")
    
    print(f"\n{'='*50}")
    print(f"スクリーンショットQAレポート")
    print(f"{'='*50}")
    print(f"✅ 問題なし: {ok_count}件")
    print(f"❌ 問題あり: {issue_count}件")
    
    for r in results:
        if r.status == "issues_found":
            print(f"\n📱 {r.file}")
            for issue in r.issues:
                print(f"  → {issue}")
 
# 実行
results = batch_review("./release_screenshots/", "./screenshot_configs.json")
print_report(results)

screenshot_configs.json の中身はこういう形式です:

{
  "home_light_iphone15.png": "ホーム画面、ライトモード、iPhone 15",
  "home_dark_iphone15pro.png": "ホーム画面、ダークモード、iPhone 15 Pro",
  "settings_light.png": "設定画面、ライトモード",
  "settings_dark.png": "設定画面、ダークモード"
}

実際に使ってみて気づいたこと

3ヶ月ほど実際のリリースフローに組み込んで、いくつかのことが見えてきました。

精度は意外と高い: 「ラベルが2px はみ出している」のような微妙なケースは見逃すこともありますが、「ダークモードで白背景に白テキスト」のような致命的な問題は高確率で拾ってくれます。わたしの場合、本番事故の9割はこのレベルの見落としだったので、実用的には十分です。

コンテキストの説明が肝: 画面の説明を入れると精度が上がります。特に「ダークモード」「ライトモード」の情報は必ず入れてください。入れないと Gemini がライトモードとして解釈して「テキストのコントラストは問題なし」と誤判定するケースがありました。

Gemini 3.2 Flash を選ぶ理由: 2.5 Flash でも動きますが、3.2 Flash の方が「どの要素が、どのように問題か」の説明が具体的でした。スクリーンショット内の座標を含む回答が返ってきたこともあります。コストも Flash なので1回あたり数円程度で収まります。

1回のリクエストで複数画像を渡す場合の注意: Gemini API は1回のリクエストに複数の画像を渡せますが、「どの画像の話をしているか」が混在して精度が下がります。1画像1リクエストにした方が確実です。

CI/CD への組み込み

GitHub Actions への組み込みは、こういうステップで動かしています:

# .github/workflows/ui-qa.yml
name: UI Screenshot QA
 
on:
  workflow_dispatch:
  push:
    branches: [release/*]
 
jobs:
  screenshot-qa:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Build and capture screenshots
        run: |
          xcodebuild test \
            -scheme "MyApp" \
            -destination "platform=iOS Simulator,name=iPhone 15" \
            -testPlan "ScreenshotCapture"
          
      - name: Run Gemini Vision QA
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
        run: |
          pip install google-generativeai --quiet
          python scripts/qa_screenshots.py ./screenshots/ --config screenshot_configs.json
          
      - name: Upload QA report
        uses: actions/upload-artifact@v4
        with:
          name: qa-report
          path: qa_report.txt

push で自動実行すると、問題があったときだけ Artifact にレポートが上がります。

限界と向き合う

正直に書くと、このアプローチが向かないケースもあります。

アニメーションやインタラクションの問題は静止画では判断できません。スクロール途中の状態や、タップ後の遷移に問題がある場合は、XCTest や Espresso の方が確実です。また、アクセシビリティの VoiceOver 対応は、スクリーンショットでは確認できません。

「スクリーンショットを目で見て確認する工程」の一部を自動化する——そういう位置づけで使うのが現実的です。全てのUIテストを置き換えるものではありません。

次のステップ

このスクリプトを動かせる状態になったら、screenshot_configs.json に検証したい画面を追加していくところから始めてみてください。最初は5〜10枚程度から試すと、Gemini が何を「問題あり」と判断するクセが掴めてきます。

わたし自身、まだプロンプトを改善している途中ですが、個人開発の規模感では十分に実用的なツールになっています。

実装の参考になれば幸いです。


関連記事:

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-07-16
Gemini にスクリーンショットを採点させたら、全部78〜85点だった — 絶対スコアを捨ててペア比較に切り替えるまで
Gemini Vision に App Store スクリーンショットを100点満点で採点させると、良い案も崩した案も78〜85点に潰れます。弁別能を実測して絶対スコアを捨て、位置バイアスを除いたペア比較でランキングを作るまでの実装記録です。
API / SDK2026-06-28
Gemini API で動画を「時刻つき」で読む — 該当シーンだけを引き出す
画面録画やアプリのデモ動画から「あの操作はどこ?」を探すのは骨が折れます。Gemini API の動画理解を使い、タイムスタンプ付きで該当箇所だけを引き出す方法と、FPS・解像度でトークンを抑える設計をまとめます。
API / SDK2026-06-12
壁紙アセットの画像探しを File Search のマルチモーダル検索に任せてみた記録
数千枚の壁紙アセットから目当ての一枚を探す作業を、File Search のマルチモーダル検索(gemini-embedding-2)に置き換えられるか300枚で検証しました。カテゴリタグ運用との比較、つまずいた点、使い分けの結論を実装コードとともに残します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →