◉GEMINI LABEN
●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…●ARGON — gemini-4-argon が 10/13 に GA。複雑なコーディング・エージェント・数学・科学向けです●DEEP RES — deep-research-pro-preview-12-2025 は 10/23 に停止、残り6日。移行先は deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 が GA(10/06)。gemini-3.1-flash-image は非推奨で停止日は未発表です●MCP — ツール結果が 8.5万トークンに膨らんだ事例を、ADK で 310 トークンに包んで抑えた報告が出ています●FLASH — gemini-3.7-flash と 3.5-flash は新しい Flash モデルに自動で回されます(10/08)●NEW — クラッシュ報告を毎回ゼロから読ませない — 既知バグ早見表を先に grep し…
記事一覧/API / SDK
◈ API / SDK/2026-05-18上級

Gemini API asyncio 非同期処理の本番パターン——並列リクエストで処理時間を1/5に短縮した実装記録

Gemini API の asyncio 非同期処理を本番環境に組み込んだ実装記録。Semaphore によるレート制限対策、指数バックオフ、部分失敗の扱い方まで、個人開発アプリのバックエンドで実際に遭遇したパターンを整理して紹介します。

gemini-api287asyncio3python106非同期処理5本番環境6個人開発121

✦ プレミアム記事

同期処理で書いた分類バッチを走らせたまま、夜中に一度だけ様子を見に行ったことがあります。進捗の行はゆっくりとしか増えておらず、このまま朝まで置いても終わらないことだけは分かりました。個人開発で続けている壁紙アプリのコンテンツ更新で、Gemini API に画像のカテゴリ分類をさせようとしていたときのことです。

最初のバージョンは Python の同期処理で書いておりました。1 リクエストの応答が平均2秒、対象が1,000枚。画像の前処理とローカル I/O を足すと、推定で6時間を超える計算になります。カタログの更新はほぼ毎週発生しますので、この所要時間では運用に乗りません。

asyncio へ書き換えたところ、同じ枚数を81分で終えられるようになりました。ここでは、その書き換えで実際に効いた実装パターンと、本番で詰まった箇所を、なぜそう書くのかという理由まで含めて書き残します。

なぜ同期処理では限界が来るのか

Gemini API は HTTP ベースの REST API です。同期処理でリクエストを1件ずつ投げると、それぞれのネットワーク往復時間が積み上がっていきます。1リクエストあたりの応答時間が平均2秒だとすると、1,000件で2,000秒(約33分)。画像の前処理やローカルI/Oも含めると6時間という計算が現実に近くなります。

同期処理のコードは読みやすく、デバッグも簡単です。スケールしない以外は問題がなく、少量のデータを扱う間は問題なく動き続けます。しかしアプリのコンテンツが増え、分類対象の画像が数百件から数千件になる段階で、同期処理の限界は突然訪れます。

import google.generativeai as genai
 
# ❌ 同期処理の例(これでは遅い)
def classify_images_sync(image_paths: list[str]) -> list[dict]:
    model = genai.GenerativeModel("gemini-2.5-flash")
    results = []
    for path in image_paths:
        with open(path, "rb") as f:
            image_data = f.read()
        response = model.generate_content([
            '{"category": "...", "tags": [...]}の形式でJSONのみ返してください。',
            {"mime_type": "image/jpeg", "data": image_data}
        ])
        results.append({"path": path, "result": response.text})
    return results

asyncio を使えば、複数のリクエストを同時に飛ばすことができます。ネットワークの応答を待っている間、イベントループは別のリクエストを開始できます。ただし Gemini API には RPM(1分あたりのリクエスト数)と TPM(1分あたりのトークン数)の制限があるため、無制限に並列化するとすぐにレート制限エラーが返ってきます。「並列化しながらレート制限を守る」という二律背反を解決するのが、次に説明する Semaphore パターンです。この二律背反こそが asyncio 初学者が一番詰まるポイントです。レート制限を無視して並列数を上げすぎると 429 エラーが頻発し、レート制限を守りすぎると並列化のメリットが失われます。ちょうどよいバランスを見つけることが設計の核心です。

asyncio.to_thread() による同期SDKのラップ

本記事のパイプラインを組んだ2026年5月の時点では、google-generativeai の Python SDK は同期クライアントのみを提供しておりました。asyncio で使うには asyncio.to_thread() で同期関数をスレッドプールへ逃がすのが、最も安定した方法でした。

この前提は現在は変わっております。後継の google-genai には native async のクライアントがあり、to_thread() は不要になりました。移行時の注意点は後半の「asyncio.to_thread() の前提が変わりました」の節にまとめておりますので、これから新規に書く方はそちらを先にご覧ください。以下はレガシー SDK を使い続ける場合の、いまも有効な形です。

asyncio.to_thread() は Python 3.9 以上で使えます。内部的には loop.run_in_executor(None, func) と同等ですが、より直感的に書けます。これを使うことで、同期処理のAPIコールを「ブロッキングしないコルーチン」として扱えるようになります。

import asyncio
import google.generativeai as genai
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
model = genai.GenerativeModel("gemini-2.5-flash")
 
async def call_gemini_async(prompt: str, image_data: bytes, mime_type: str = "image/jpeg") -> str:
    """
    同期のGemini APIコールをスレッドプールで非同期実行する。
    asyncio.to_thread() でブロッキング処理をイベントループの外に逃がす。
    """
    def _sync_call():
        response = model.generate_content([
            prompt,
            {"mime_type": mime_type, "data": image_data}
        ])
        return response.text
 
    return await asyncio.to_thread(_sync_call)

この実装で重要な点は、genai.GenerativeModel のインスタンスを複数のスレッドで共有していることです。私の環境では、並列数30のスレッドプールで共有しても状態の競合は発生しませんでした。ただし genai.configure(api_key=...) はモジュールレベルで一度だけ呼ぶことが前提です。

これだけでは「非同期で実行できる」だけで、複数件を並列実行する仕組みがありません。次のステップが核心部分です。

✦

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦画像1,000枚の逐次処理で6時間かかっていたバックエンドが、asyncio 並列化でどう変わったかを追体験できる
✦Gemini API の RPM/TPM 制限に引っかかることなく高スループットを実現する Semaphore パターンのコードを入手できる
✦部分失敗・タイムアウト・指数バックオフを一つに組み込んだ本番用の asyncio ラッパーをそのまま使えるようになる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

◈ API / SDK2026-05-18
Gemini Vision で壁紙アプリの自動カテゴリ分類を実装した話
個人開発の壁紙アプリで Gemini Vision API を使い、画像の自動カテゴリ分類を実装した実体験です。精度改善のプロセスと、公式ドキュメントには載っていない落とし穴、GPT-4o Vision とのコスト比較までまとめました。
◈ API / SDK2026-05-17
git log から日英リリースノートを Gemini API で自動生成した — Beautiful HD Wallpapers v2.1.0 での実装記録
個人開発アプリのリリースノートを毎回手書きしていたコストに気づき、Gemini API で git log から日英自動生成するスクリプトを作った記録。Beautiful HD Wallpapers v2.1.0 のリリース作業で実証し、30言語対応への拡張方法も紹介します。
◈ API / SDK2026-05-15
Gemini API を asyncio で並列化したら、壁紙アプリの多言語説明文生成が12倍速になった
12言語の App Store 説明文生成を Gemini API の asyncio 並列化で13分から65秒に短縮した実装記録です。Semaphore によるレート制限対策と、google-generativeai から google-genai への移行で変わった点まで含めてまとめました。