GEMINI LABEN
CLI — 本日6/18、Gemini CLIとGemini Code Assist IDE拡張がAI Pro/Ultra・無料個人利用向けにリクエスト提供を終了。後継はAntigravity CLIですFLASH — Gemini 3.5シリーズが始動し3.5 Flashが提供開始。エージェントとコーディング向けにフロンティア級、長時間タスクに強いと説明されていますDEEPTHINK — Gemini 3 Deep ThinkがGoogle AI Ultra向けに展開中。数学・科学・論理・多段推論の最上位モードですAPP — GeminiアプリにDaily Brief、再設計UI、AI動画モデルGemini Omni、個人AIエージェントGemini Sparkが加わりましたDESIGN — 新デザイン言語Neural Expressiveで、よりリッチな視覚出力とモダリティ間の素早い切替に向けて再構築されていますULTRA — Google AI Ultraはモデル最上位アクセス・Deep Research・Veo 3動画生成・100万トークンコンテキストを束ねるプレミアム枠ですCLI — 本日6/18、Gemini CLIとGemini Code Assist IDE拡張がAI Pro/Ultra・無料個人利用向けにリクエスト提供を終了。後継はAntigravity CLIですFLASH — Gemini 3.5シリーズが始動し3.5 Flashが提供開始。エージェントとコーディング向けにフロンティア級、長時間タスクに強いと説明されていますDEEPTHINK — Gemini 3 Deep ThinkがGoogle AI Ultra向けに展開中。数学・科学・論理・多段推論の最上位モードですAPP — GeminiアプリにDaily Brief、再設計UI、AI動画モデルGemini Omni、個人AIエージェントGemini Sparkが加わりましたDESIGN — 新デザイン言語Neural Expressiveで、よりリッチな視覚出力とモダリティ間の素早い切替に向けて再構築されていますULTRA — Google AI Ultraはモデル最上位アクセス・Deep Research・Veo 3動画生成・100万トークンコンテキストを束ねるプレミアム枠です
記事一覧/開発ツール
開発ツール/2026-06-17上級

Gemini API のチャット履歴を Redis で運用する — 本番で会話状態を失わない実装メモ

Gemini のチャット履歴をプロセスメモリに置いたまま本番に出すと、再デプロイやスケールのたびに会話が消えます。Redis を受け皿にしつつ、トークン予算・同時送信・SDK 依存・障害時の縮退まで、実運用で踏んだ箇所を実装とともにまとめました。

gemini83gemini-api241redis3session-managementproduction87operations2

プレミアム記事

個人開発でチャット機能を載せたとき、最初に届いた不具合報告は「昨日の続きから話せない」でした。ローカルでは何の問題もなく動いていたのに、Cloud Run に乗せた途端、ユーザーがアプリを開き直すたびに AI が自己紹介からやり直してしまう。原因を追うと、ChatSession オブジェクトをプロセスのメモリに握ったままにしていただけのことでした。

Gemini API の chats.create(history=...) は、履歴さえ渡せば過去の文脈を踏まえた応答を返してくれます。公式サンプルの history=[] から始めるのは正しいのですが、その履歴を「どこに置くか」を決めないままスケールさせると、本番運用の初日に静かに壊れます。素朴な実装の落とし穴は、いずれも例外が出ないまま進行するのが厄介です。ここでは、私自身が個人開発のプロダクトで実際に運用しているチャット状態管理のうち、Redis を受け皿にする前提で「素朴な実装が破綻する箇所」と「そこをどう塞いだか」を、コードを添えて整理します。

なお、本稿のモデル指定は 2026 年 6 月時点で既定となった gemini-3.5-flash と、推論を厚くしたいときの gemini-3.5-pro を前提にしています。既定モデルが上がると出力の癖も変わるため、履歴フォーマットを後述のように SDK から剥がしておくと、移行のたびに保存層を書き換えずに済みます。

本番で監視している3つの壊れ方

インメモリ保持が本番で破綻する理由は、突き詰めると「コンテナの寿命とリクエストの寿命が一致しない」ことに尽きます。私が実運用で監視しているのは、次の3つの兆候です。

ひとつ目は、コンテナの短命さです。Cloud Run のインスタンスはトラフィックが途切れれば数分で停止します。再起動後にグローバル変数の中身は当然空になり、直前まで続いていた会話が消えます。ログ上は例外も出ないため、ユーザー報告で初めて気づくのが厄介なところです。

ふたつ目は、水平スケール時の振り分けです。同じユーザーの 1 通目と 2 通目が別インスタンスに届けば、それぞれが独立したメモリを見ているので履歴が繋がりません。Sticky セッションで同じインスタンスに固定する逃げ道もありますが、スケールの自由度を捨てることになり、結局はステートを外に出すのが筋だと考えています。

みっつ目は、クライアントの再接続です。モバイルアプリを落として数時間後に再開したとき、サーバー側に状態が残っている保証はありません。「セッションはいつでも消えうる」という前提で組むほうが、結果的に堅牢になります。

Redis はこの受け皿としてよく噛み合います。ミリ秒オーダーの読み書き、TTL による自動失効、必要なら Pub/Sub でのリアルタイム通知まで揃っているため、チャット状態の中継地点として扱いやすいのです。

まず「とりあえず動く」版と、その綻び

最初に、最小限の実装を見てから、何が足りないかを順に潰していきます。

# requirements: google-genai, redis
import json
import os
import redis
from google import genai
 
r = redis.Redis.from_url(os.environ["REDIS_URL"], decode_responses=True)
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
 
def chat_once(session_id: str, user_message: str) -> str:
    raw = r.get(f"chat:{session_id}")
    history = json.loads(raw) if raw else []
 
    chat = client.chats.create(model="gemini-3.5-flash", history=history)
    response = chat.send_message(user_message)
 
    new_history = [
        {"role": msg.role, "parts": [{"text": p.text} for p in msg.parts]}
        for msg in chat.get_history()
    ]
    r.set(f"chat:{session_id}", json.dumps(new_history))
    return response.text

これは動きます。ただ数日運用すると、次の綻びが順番に顔を出しました。

第一に、履歴が無限に伸びます。1 往復ごとに数百〜数千トークンが積み上がり、私の手元では雑談寄りのセッションで 1 週間ほどで 10 万トークンを超えました。応答レイテンシが目に見えて悪化し、入力トークン課金も線形に膨らみます。

第二に、TTL がありません。書き込みっぱなしなので、使われなくなったセッションが残り続けます。Redis のメモリ使用量が右肩上がりになり、maxmemory と eviction ポリシー次第では新規セッションの保存に失敗し始めます。

第三に、同時書き込みで履歴が壊れます。同じユーザーが二つのタブから送ると、片方の保存がもう片方を丸ごと上書きします。UI 側のオプティミスティック更新と重なると、画面表示と実体がずれて再現の難しいバグになります。

第四に、Gemini SDK の内部構造に依存していますchat.get_history() の戻りをそのまま直列化しているので、SDK のバージョンアップで構造が変わると古いデータが読めなくなります。

ここからは、それぞれの落とし穴への対処を順に見ていきます。

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

この記事の続きを読む

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

この記事で得られること
会話履歴をプロセス外に逃がす設計と、再デプロイ・水平スケールで履歴が消える3つの壊れ方の見分け方
スライディングウィンドウ+要約でトークンを実測ベースに抑える実装と、要約に軽量モデルを充てる理由
Lua による安全なロック解放・SDK 非依存の保存フォーマット・Redis 障害時の縮退まで、本番スケルトンの全体像
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

開発ツール2026-06-17
廃止予定の Gemini モデルを CI で検知する ― 連続するデッドラインを取りこぼさない仕組み
廃止・停止が重なる時期に、リポジトリ内の古い Gemini モデル指定を CI で機械的に検知する仕組みの作り方を、廃止レジストリ・走査スクリプト・残り日数による段階警告まで実装ベースでまとめます。
開発ツール2026-06-15
Firestore × Gemini Embeddings の RAG が静かに劣化する — 埋め込み世代交代に耐える再埋め込み設計
Firestore のネイティブベクトル検索と Gemini Embeddings で組んだ RAG は、埋め込みモデルの世代交代でベクトル空間がずれ、検索品質が静かに落ちます。ドリフトの検知、無停止の再埋め込み移行、取得コストの抑え方を実装で詰めます。
開発ツール2026-04-17
Google Cloud Workflows × Gemini API 本番オーケストレーション:タイムアウト・リトライ・コスト制御まで
Google Cloud WorkflowsでGemini APIを長時間・多段階処理するための本番設計を徹底解説。YAML定義・タイムアウト設定・自動リトライ・コスト予算管理まで、動作確認済みコード付きで解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →