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日に停止される予定です
記事一覧/高度な活用
高度な活用/2026-04-24上級

Gemini API のシャドウトラフィックで新モデル移行を安全に進める — 出力差分を本番で測る実装パターン

Gemini のモデル移行を「デプロイしてみないと分からない」から卒業するためのシャドウトラフィック設計。本番入力をコピーして新モデルに流し、出力差分を定量評価してから段階的にカットオーバーする実装パターンを、Python と Cloud Tasks のコード付きで解説します。

gemini102model-migration5shadow-trafficproduction105sre2canary2

プレミアム記事

Gemini 2.0 Flash が 2026 年 6 月に非推奨になる、という Google のアナウンスが出たとき、私の手元で動いていた本番アプリは 3 本ありました。真っ先に頭をよぎったのは「動作確認はしたけれど、本当に同じ品質が出るのか確信がない」という不安です。ステージング環境で数十件のテストケースを流すだけでは、実ユーザーの多様な入力に対する振る舞いは見えません。「デプロイして様子を見る」という賭けに出るのは、個人開発だとしても避けたいところです。

この不安を構造的に解消してくれるのが「シャドウトラフィック」という設計パターンです。本番の入力を新旧両方のモデルに送り、ユーザーには旧モデルの出力を返しつつ、新モデルの出力は裏側で収集して差分を計測します。すると「どのくらいの割合で品質が変わるのか」「どのケースで劣化が起きやすいのか」を、実トラフィックのデータで定量評価できます。

私は個人で複数の Gemini 製品を運用していますが、移行のたびにこの仕組みを組み直すのは大変なので、4 サイト共通で使える汎用的な実装パターンに落とし込みました。本番で運用しながら得た知見を、コードと一緒に共有していきます。

なぜモデル移行は「当たって砕けろ」になりがちなのか

LLM のモデル移行が Web API の移行と決定的に違うのは、「契約(API スキーマ)」と「挙動(応答品質)」が独立している点です。エンドポイントやリクエスト形式は互換があっても、同じ入力に対する出力が微妙に違う、ということが頻繁に起こります。

たとえば私が実際に遭遇したケースでは、旧モデルでは日本語で返ってきていた要約が、新モデルでは時折英語混じりで返るようになりました。システムインストラクションを少し強化すれば直るのですが、それに気づくのは本番ユーザーからのフィードバックを受けてから、というのでは遅すぎます。

ユニットテストや回帰テストで検出できるのは「想定したケース」だけで、実ユーザーが投入する自由入力の多様性まではカバーしきれません。ここに、実トラフィックで新モデルを試すシャドウトラフィックの価値があります。

シャドウトラフィックの基本設計

シャドウトラフィックの構造はシンプルです。プロダクション側のコード(以下「フロントエンド」と呼びます)は、ユーザーリクエストを受け取ると旧モデル(プライマリ)を呼び出し、その応答をユーザーに返します。ここまでは従来通りです。追加するのは「同じリクエストを新モデル(シャドウ)にも送る」という非同期パスです。

このときに絶対に守るべき原則が 3 つあります。

1つ目は「シャドウ呼び出しがユーザーのレイテンシに影響しないこと」です。シャドウを同期的に待つと、2 回の API 呼び出しが直列になって遅延が倍増します。私は最初これで失敗しました。

2つ目は「シャドウ呼び出しが失敗してもユーザー体験を損なわないこと」です。シャドウ側のエラーがプライマリの応答を汚染する設計は論外です。

3つ目は「シャドウの出力がユーザーに絶対に届かないこと」です。特に複数のワーカーやプロセスが絡む構成だと、応答が混線する事故が起こりやすいので注意します。

これらを満たす最小実装を示します。

# shadow_traffic.py
# プライマリ応答を返しつつ、同じ入力を非同期でシャドウへ送る最小実装。
# ユーザーのレイテンシに影響を与えず、シャドウ側の失敗はプライマリ応答に一切影響しない。
 
import asyncio
import logging
from google import genai
 
logger = logging.getLogger(__name__)
 
class ShadowTrafficClient:
    def __init__(
        self,
        primary_model: str = "gemini-2.5-pro",
        shadow_model: str = "gemini-3-pro",
        shadow_sample_rate: float = 0.1,  # 本番トラフィックの10%だけシャドウへ送る
    ):
        self.client = genai.Client()
        self.primary_model = primary_model
        self.shadow_model = shadow_model
        self.shadow_sample_rate = shadow_sample_rate
 
    async def generate(self, prompt: str, request_id: str) -> str:
        # 1. プライマリをまず呼び出してユーザーに返す準備をする
        primary_task = asyncio.create_task(
            self._call_model(self.primary_model, prompt, request_id, "primary")
        )
        primary_response = await primary_task
 
        # 2. サンプリング判定後、シャドウは「ファイア・アンド・フォーゲット」で実行
        import random
        if random.random() < self.shadow_sample_rate:
            asyncio.create_task(
                self._shadow_execute(prompt, request_id, primary_response)
            )
 
        return primary_response  # ユーザーへはプライマリのみを返す
 
    async def _call_model(self, model: str, prompt: str, req_id: str, role: str) -> str:
        try:
            response = await self.client.aio.models.generate_content(
                model=model,
                contents=prompt,
            )
            return response.text
        except Exception as e:
            # プライマリが失敗したら例外を投げる。シャドウは呼び出し元で握りつぶす。
            logger.warning(f"[{req_id}] {role} call failed: {e}")
            raise
 
    async def _shadow_execute(self, prompt: str, req_id: str, primary_response: str):
        # シャドウの失敗はユーザー体験に影響させない(例外を握りつぶして記録する)
        try:
            shadow_response = await self._call_model(
                self.shadow_model, prompt, req_id, "shadow"
            )
            await self._log_comparison(req_id, prompt, primary_response, shadow_response)
        except Exception as e:
            logger.warning(f"[{req_id}] shadow failed (ignored): {e}")
 
    async def _log_comparison(self, req_id, prompt, primary, shadow):
        # BigQuery / Firestore / S3 などへ差分記録。詳細は後述のセクションで実装。
        pass

ポイントは asyncio.create_task で作ったシャドウのタスクを await しないことです。これで「ユーザーへの応答速度はプライマリのみで決まる」という性質が保証されます。

私はこのパターンを初めて書いたとき、うっかり await shadow_task と書いて全ユーザーのレイテンシを倍にしてしまいました。fire-and-forget の実装は、言語仕様の癖を理解していないと意外と間違えるので、ここはレビューで必ず見るポイントにしています。

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

この記事の続きを読む

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

この記事で得られること
Gemini のモデル移行で「本番に出してから品質劣化に気づく」事故を未然に防ぐ、シャドウトラフィック基盤を今日設計できるようになります
ユーザーレイテンシを一切悪化させずに新モデルの出力を収集する非同期シャドウ実行パターンと、コストを抑えるサンプリング戦略のコードを手に入れられます
1% → 10% → 50% → 100% の段階的カットオーバーと、即時ロールバックを両立させる Feature Flag ベースのルーティング実装を持ち帰れます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

高度な活用2026-06-27
Gemini の完了イベントは二度届きます — Webhook と照合ポーラーを「実質1回」にする冪等な受け口
Gemini の長時間オペレーションを Webhook で受けつつ照合ポーラーで二重化すると、同じ完了イベントが二度届き、公開や課金が二度走ります。冪等キーの取り方と claim→実行→確定の三相で「実質1回」にする受け口を実装します。
高度な活用2026-06-14
Gemini の構造化出力を本番で信用するために — スキーマ設計・二重検証・抑制つきリトライの実装メモ
Gemini の構造化出力は「パースできるJSON」は保証しても「正しい値」までは保証しません。@google/genai での新しいスキーマ設計、propertyOrdering の効き目、Zod による二重検証、MAX_TOKENS 切れの扱い、抑制つきリトライの実装をまとめました。
高度な活用2026-06-14
画像生成モデルを乗り換えると品質は静かに落ちる — 人手レビューなしで検知する自動ゲートの設計
preview から GA への画像モデル移行で、出力品質が落ちていないかを目視に頼らず検証する3層ゲートの実装です。決定的プロパティ検査・マルチモーダル埋め込み類似度・Gemini によるブリーフ遵守スコアを Python のコードでつなぎ、しきい値とカットオーバー手順まで整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →