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-04-29上級

Gemini API × Inngest で耐障害性の高いAIワークフローを構築する本番運用ガイド

Gemini APIのレート制限・タイムアウト・部分失敗に強い「耐障害性の高い」AIワークフローを、Inngestのdurable executionで実装する本番運用パターンを解説します。

gemini-api279inngestdurable-executionworkflow5production105

プレミアム記事

「Gemini API を呼ぶ箇所まではうまくいくのに、その後の DB 書き込みや別サービスへの通知が落ちると、最初からやり直すしかない」——そんな相談を、ここ数ヶ月で何度か耳にしました。Gemini を本番に乗せる段階で多くの方がぶつかるのは、API 単体の使い方ではなく、その前後の処理を含めた長時間ジョブを「途中で落ちても安全に再開できる」ように作る部分です。

私は個人のアプリ開発で数年前から似た問題に悩まされてきました。AdMob 連携、画像生成、サーバーレス CRON、外部 API のリトライ。試行錯誤の末、現在は Inngest を使った durable execution(永続実行)に落ち着いています。Cloud Tasks や BullMQ と比べても、Gemini のような「途中で何が起きるか分からない外部 API」を組み込むワークフローには明確に向いている、というのが3週間ほど運用した上での率直な印象です。

このガイドでは、Gemini API と Inngest を組み合わせて「落ちても再開できる」AI ワークフローを設計するための具体的なコードと、本番で必ず必要になる落とし穴の回避策を、実装例とともに整理します。コピペで動くコードを並べるので、手元のエディタを開きながら読み進めていただけたら嬉しいです。

なぜ Gemini API には durable execution が必要なのか

「durable execution(永続実行)」は耳慣れない言葉ですが、要点は 「関数の途中の状態をエンジンが永続化し、失敗したステップだけを再実行できる」 という設計思想です。Inngest 以外にも Temporal や Restate が同じ思想で作られています。

Gemini API を本番で叩いていると、以下のような現実によく遭遇します。

  • レート制限(HTTP 429)が瞬間的に発生し、リトライしないと処理が止まる
  • ストリーミングが途中で切れ、レスポンスの最後の chunk だけが届かない
  • マルチモーダル入力(動画・PDF)の解析中にタイムアウトする
  • Function Calling の戻り値を使って外部 API を叩く部分で別の障害が発生する

ナイーブに try/catchsetTimeout でリトライを書くと、リトライのたびにトークン消費が発生する点が痛いところです。1万件の記事生成バッチで途中5,000件目で失敗したら、最初からやり直すと API コストが倍になります。これを「失敗したステップだけを再実行する」のが durable execution の本質です。

Inngest を選んだ理由は次の3つです。

第一に、ステップ単位での冪等性が言語レベルで強制されること。step.run('name', fn) で囲んだ処理は、成功すれば結果がキャッシュされ、再実行時はスキップされます。Gemini API 呼び出し1回ごとに step.run で囲むだけで、無駄な再呼び出しを避けられます。

第二に、ローカル開発体験が良いこと。npx inngest-cli@latest dev でローカルダッシュボードが起動し、実行中の関数のステップが時系列で可視化されます。Cloud Tasks のように「ローカルでは動かして本番でしか挙動が再現しない」という辛さがありません。

第三に、TypeScript ファーストで型がしっかり効くこと。イベントペイロードの型を定義すれば、関数側で補完が効き、本番でペイロードのキー名が変わったときにビルドエラーで気づけます。

Step 1: 最小構成の Gemini × Inngest ワークフロー

まずは「ユーザーから記事生成リクエストを受け付け、Gemini で本文を生成し、結果を DB に保存する」という最小フローを書きます。Next.js App Router での実装例です。

依存関係は次の通りです。@google/genai は2026年4月時点での Gemini 公式 SDK 名で、旧 @google/generative-ai は非推奨になっています。

npm install @google/genai inngest
npm install -D @types/node typescript

Inngest クライアントとイベント型の定義から始めます。

// src/inngest/client.ts
import { Inngest, EventSchemas } from "inngest";
 
type Events = {
  "article/generate.requested": {
    data: {
      userId: string;
      topic: string;
      lengthHint: "short" | "medium" | "long";
      requestId: string; // 冪等性キー
    };
  };
};
 
export const inngest = new Inngest({
  id: "gemilab-content-pipeline",
  schemas: new EventSchemas().fromRecord<Events>(),
});

requestId を必ず受け取る点が重要です。後段の処理で「同じリクエストに対する再試行か、別のリクエストか」を判別する基準になります。クライアント側で UUID v7 を発行する運用を私は好みます。

次に Gemini を呼び出す関数を書きます。

// src/inngest/functions/generate-article.ts
import { GoogleGenAI } from "@google/genai";
import { inngest } from "../client";
 
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
 
export const generateArticle = inngest.createFunction(
  {
    id: "generate-article",
    // 同じ userId からの並列実行を1件に制限(暴走防止)
    concurrency: { key: "event.data.userId", limit: 3 },
    // バックオフを伴うリトライ。Geminiの429は短期復旧することが多いため指数バックオフが効きやすい
    retries: 4,
  },
  { event: "article/generate.requested" },
  async ({ event, step, logger }) => {
    const { topic, lengthHint, requestId } = event.data;
 
    // ステップ1: アウトライン生成(失敗してもここだけ再実行される)
    const outline = await step.run("generate-outline", async () => {
      const res = await ai.models.generateContent({
        model: "gemini-2.5-pro",
        contents: `次のトピックの記事アウトラインをH2見出し5つで提案してください。\nトピック: ${topic}\n長さ目安: ${lengthHint}`,
        config: { temperature: 0.4, responseMimeType: "text/plain" },
      });
      return res.text ?? "";
    });
 
    // ステップ2: 本文生成(outlineが既に得られていれば、再実行時はステップ1をスキップ)
    const body = await step.run("generate-body", async () => {
      const res = await ai.models.generateContent({
        model: "gemini-2.5-pro",
        contents: `以下のアウトラインに沿って記事本文を書いてください。\n\n${outline}`,
        config: { temperature: 0.6, maxOutputTokens: 8000 },
      });
      return res.text ?? "";
    });
 
    // ステップ3: DB保存(外部APIなのでもちろん step.run で囲む)
    await step.run("save-to-db", async () => {
      await fetch(`${process.env.API_BASE_URL}/articles`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ requestId, topic, body }),
      });
    });
 
    logger.info("article generated", { requestId, topicLength: topic.length });
    return { requestId, bodyLength: body.length };
  },
);

ここで一番伝えたいのは、step.run で囲むかどうかが、本番で泣くか笑うかの分かれ道だということです。step.run を外して書くと、retry のたびに最初の outline 生成からやり直されます。Gemini 2.5 Pro のトークン単価は決して安くないので、これは数日でコストの問題として顕在化します。

最後に Next.js から Inngest 関数を公開します。

// src/app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "@/inngest/client";
import { generateArticle } from "@/inngest/functions/generate-article";
 
export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [generateArticle],
});

ローカルでは npx inngest-cli@latest dev を立ち上げ、http://localhost:3000/api/inngest を register URL として指定すれば、ダッシュボードからイベントを発火させてフローを目視で確認できます。

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

この記事の続きを読む

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

この記事で得られること
Gemini API の途中失敗・タイムアウト・レート制限で詰まっていた長時間ジョブを、Inngestの自動リトライとステップ単位の冪等性で安定稼働させられるようになる
fan-out / fan-in・人間による承認待ち・スロットリング・dead-letter といった本番ワークフローのパターンを、コピペ可能なTypeScriptコードで習得できる
コスト超過と暴走を防ぐ予算ガードレール、観測可能性、リプレイ可能なイベント駆動設計を、自社サービスに今日から組み込める
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

API / SDK2026-07-18
同じ gemini-flash-latest が、リージョンごとに違うモデルを指していた — 比較値が汚れる数日間の見つけ方
エイリアスの解決先はリージョン単位でロールアウトされます。同じ gemini-flash-latest を東京と us-central1 に投げると、数日だけ別のモデルが応答する。その間の比較値が無効になる理由と、1発のプローブで検知して比較を凍結する設計をまとめます。
API / SDK2026-07-06
Managed Agents を本番に上げる前に、固定シナリオで挙動を測る — 非決定的なエージェントに回帰スイートと段階昇格を敷く
公開プレビューの Managed Agents は隔離サンドボックスで自律的に動く分、プロンプトや設定を少し変えただけで挙動が静かにずれます。単発プロンプトのように出力を一度突き合わせるだけでは足りません。固定シナリオを何度も回して合格率で測る回帰ハーネスと、影→カナリア→全面の段階昇格、逸脱時の自動ロールバックまでを、動く Python とともに設計します。
API / SDK2026-07-05
使っているモデルの廃止予告だけを拾う — url-context に公式チェンジログを読ませる仕組み
画像モデルの停止を公開3日前に知って肝を冷やしました。url-context に公式チェンジログを直接読ませ、自分が実際に使っているモデルの廃止予告だけを構造化して差分通知する仕組みを、そのまま動く Python と運用で削った過検知の調整まで含めて組み立てます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →