GEMINI LABEN
SUNSET — 画像生成モデルの停止は本日8月17日です。imagen-4.0-generate-001・ultra・fast と Gemini 3 Image 系が対象で、以降の呼び出しはハードエラーになりますSCALE — 8月11日に Gemini の月間アクティブユーザーが10億に到達しましたASSISTANT — 9月4日から Android と Wear OS で Google アシスタントが Gemini へ置き換わります。完了まで数週間かかる見込みで、一度切り替わると元には戻せませんDEVICES — 対象は Android スマートフォン・タブレット、Wear OS、アシスタント対応ヘッドフォン、投影型 Android Auto です。Google ビルトイン搭載車は継続しますSPARK — 8月13日から Gemini 3.7 Flash が Gemini Spark のエンジンになりました。AI Pro・Ultra 契約者向けに160か国超で提供されていますPRICE — Gemini 3.7 Flash の導入価格は100万トークンあたり入力 0.75ドル・出力 3.75ドルで、12月31日までです。以後は 1.50ドル・7.50ドル になりますSUNSET — 画像生成モデルの停止は本日8月17日です。imagen-4.0-generate-001・ultra・fast と Gemini 3 Image 系が対象で、以降の呼び出しはハードエラーになりますSCALE — 8月11日に Gemini の月間アクティブユーザーが10億に到達しましたASSISTANT — 9月4日から Android と Wear OS で Google アシスタントが Gemini へ置き換わります。完了まで数週間かかる見込みで、一度切り替わると元には戻せませんDEVICES — 対象は Android スマートフォン・タブレット、Wear OS、アシスタント対応ヘッドフォン、投影型 Android Auto です。Google ビルトイン搭載車は継続しますSPARK — 8月13日から Gemini 3.7 Flash が Gemini Spark のエンジンになりました。AI Pro・Ultra 契約者向けに160か国超で提供されていますPRICE — Gemini 3.7 Flash の導入価格は100万トークンあたり入力 0.75ドル・出力 3.75ドルで、12月31日までです。以後は 1.50ドル・7.50ドル になります
記事一覧/API / SDK
API / SDK/2026-08-17中級

generated_images が消えたあとの受け取り方 — 戻り値から画像ファイルになるまでの3つの分岐

generate_content へ書き換えたあと、保存の行で落ちるのは戻り値の形が変わっているからです。画像 part が0件のとき・枚数が揺れるとき・拡張子を決めるときの3分岐と、そのまま使える受け取り関数をまとめます。

Gemini API212画像生成10モデル移行12Python41エラー対処4

8月17日の午前に、壁紙用の生成スクリプトを流しました。引数の書き換えは前日までに済ませてあったので、あとは走らせるだけのつもりでいました。

止まったのは保存の行です。

AttributeError: 'GenerateContentResponse' object has no attribute 'generated_images'

引数の対応表ばかり眺めていて、返ってくる側が別の形になることを最後まで手当てしていませんでした。移行作業の重心は入口にあると思い込んでいたのですが、実際に手が止まったのは出口でした。

同じところで足を止めている方に向けて、出口側で分かれる3つの分岐を書き出しておきます。

保存まで引き受けてくれていたのは、旧 API のほうでした

以前の呼び出しは、画像オブジェクトが save() を持っていました。ファイルに落とすところまで SDK の担当だったわけです。

# 以前: 保存まで SDK 側にありました
result = client.models.generate_images(
    model="imagen-4.0-generate-001",
    prompt=prompt,
    config=types.GenerateImagesConfig(number_of_images=4),
)
for i, gen in enumerate(result.generated_images):
    gen.image.save(f"out_{i}.png")

移行後は、テキスト生成と同じ経路を通ります。返ってくるのは候補(candidate)の列で、その中に part が並びます。画像はその part のひとつとして、inline_data にぶら下がって届きます。

# 移行後: 返るのは part の列で、保存は呼び出し側の責任になります
response = client.models.generate_content(
    model="gemini-3.1-flash-image",
    contents=prompt,
    config=types.GenerateContentConfig(
        response_modalities=["TEXT", "IMAGE"],
    ),
)

ここで response.candidates[0].content.parts[0] を画像だと決め打ちすると、後で必ず刺さります。モデルは短い説明文を先に返してくることがあり、その場合の 0 番目はテキストの part です。

part を順に見て、inline_data を持つものだけを拾う。これが出口側の最初の一歩になります。

def image_blobs(response):
    """画像の part だけを取り出します。無ければ空リストを返します。"""
    blobs = []
    for candidate in response.candidates or []:
        content = getattr(candidate, "content", None)
        if content is None:
            continue
        for part in content.parts or []:
            blob = getattr(part, "inline_data", None)
            if blob is not None:
                blobs.append(blob)
    return blobs

contentgetattr で受けているのは、安全側の判定で止まったとき候補そのものは返るのに中身が空になる場合があるからです。ここを素直にドットでたどると、本来の失敗理由が AttributeError に化けて見えなくなります。

分岐1 — 画像の part が1件も返らないとき

一番静かに壊れるのがこれでした。

例外は出ません。ループが0回まわって終わり、保存されたファイルは0件、スクリプトの終了コードは 0 です。私自身、最初の1回はログを見るまで成功したと思っていました。

0件だったときは、その場で理由を添えて落とすようにしています。

blobs = image_blobs(response)
if not blobs:
    feedback = getattr(response, "prompt_feedback", None)
    reasons = [c.finish_reason for c in (response.candidates or [])]
    raise RuntimeError(
        "画像 part が0件です "
        f"/ block_reason={getattr(feedback, 'block_reason', None)} "
        f"/ finish_reason={reasons}"
    )

block_reason はプロンプト側で止まった場合に入り、finish_reason は生成の途中で打ち切られた場合の理由が入ります。どちらを見るかで手当てが変わるので、両方を同じ行に出しておくと後追いが速くなります。

プロンプト側で止まっていれば文言の見直し、生成側で止まっていればリトライや別モデルへの退避、という分け方です。

分岐2 — 枚数が揺れるとき

旧 API では number_of_images を渡していたので、返る枚数は呼び出し側で確定していました。移行後は枚数の指示もプロンプトに寄るため、4枚のつもりが3枚で返ることがあります。

これが個人開発の実務でどう効くかというと、後段でずれます。

壁紙は端末の解像度ごとに束で作っていて、生成した順番と後続のカテゴリ分類の行が1対1で対応しています。束のうち1枚が欠けたまま次へ渡すと、分類結果が1行ずつずれて別の画像に貼り付きます。落ちてくれるならまだ良いほうで、静かにずれたまま App Store 用の素材まで進んでしまうと、気づくのは目視の段になります。

期待枚数との突き合わせを、保存の前に置きました。

EXPECTED = 4
if len(blobs) != EXPECTED:
    raise RuntimeError(f"期待 {EXPECTED} 枚に対して {len(blobs)} 枚でした")

たった3行ですが、この3行があるかどうかで後段の信頼度がまるで変わります。枚数を固定できない設計であれば、少なくとも実際の件数をログに残し、後続処理をインデックスではなくファイル名で結び直しておくほうが安全です。

分岐3 — 拡張子は mime_type から決めます

.png の決め打ちも移行時に見落としがちなところです。返ってくる mime_type は必ずしも image/png とは限りません。

mime_type付ける拡張子備考
image/png.png透過が要る素材はこちら
image/jpeg.jpg写真寄りの出力で返ることがあります
image/webp.webpそのまま配信に載せる場合の候補

標準ライブラリの mimetypes.guess_extension() に任せたくなりますが、image/jpeg に対する戻り値は Python のバージョンによって揺れた経緯があります。素材の命名規則が実行環境で変わるのは避けたいので、私はこの対応表を明示的に持つ側を選びました。

EXT_BY_MIME = {
    "image/png": ".png",
    "image/jpeg": ".jpg",
    "image/webp": ".webp",
}

もうひとつ、inline_data.data はすでにデコード済みの bytes です。REST の生レスポンスを見たことがあると base64 の文字列を想像しますが、SDK を通した時点で復号は済んでいます。

with open(path, "wb") as f:
    f.write(blob.data)  # base64 のデコードは不要です

ここに base64.b64decode() を足すと、運が良ければ例外、運が悪ければ壊れたファイルが黙って出来上がります。移行のついでに保存処理を触るときは、この一行だけ先に確認しておくと安心です。

3つの分岐をまとめた受け取り関数

上の3つを1か所に寄せると、こうなります。既存のスクリプトの保存部分を、この関数の呼び出しに置き換える形で使えます。

from pathlib import Path
 
EXT_BY_MIME = {
    "image/png": ".png",
    "image/jpeg": ".jpg",
    "image/webp": ".webp",
}
 
 
def save_images(response, out_dir: Path, stem: str, expected: int) -> list[Path]:
    """response から画像を取り出して保存し、書き出したパスを返します。"""
    blobs = []
    for candidate in response.candidates or []:
        content = getattr(candidate, "content", None)
        if content is None:
            continue
        for part in content.parts or []:
            blob = getattr(part, "inline_data", None)
            if blob is not None:
                blobs.append(blob)
 
    if not blobs:
        feedback = getattr(response, "prompt_feedback", None)
        reasons = [c.finish_reason for c in (response.candidates or [])]
        raise RuntimeError(
            "画像 part が0件です "
            f"/ block_reason={getattr(feedback, 'block_reason', None)} "
            f"/ finish_reason={reasons}"
        )
 
    if len(blobs) != expected:
        raise RuntimeError(f"期待 {expected} 枚に対して {len(blobs)} 枚でした")
 
    out_dir.mkdir(parents=True, exist_ok=True)
    saved = []
    for i, blob in enumerate(blobs):
        mime = blob.mime_type or ""
        if mime not in EXT_BY_MIME:
            raise RuntimeError(f"未知の mime_type です: {mime!r}")
        path = out_dir / f"{stem}_{i}{EXT_BY_MIME[mime]}"
        path.write_bytes(blob.data)
        saved.append(path)
    return saved

呼び出し側はこれだけです。

paths = save_images(
    response,
    out_dir=Path("build/wallpapers"),
    stem="sunrise",
    expected=4,
)
print(f"{len(paths)} 枚保存しました")

expected を引数にしているのは、枚数の期待値を関数の外に出しておきたかったからです。素材の種類によって束の大きさが違うので、呼び出し側が決めるほうが自然でした。

なお、引数側の対応関係についてはgenerate_images から generate_content へ書き換えるとき、引数は素直に移りませんにまとめてあります。入口と出口を続けて直すなら、そちらを先に読むほうが順番としては早いです。

今日のうちに足しておきたい1行

3つの分岐のうち、いま1つだけ足すなら枚数の突き合わせです。

0件と mime_type は、いずれ例外なり見た目なりで気づきます。枚数の欠けだけは、後段まで静かに進んでしまいます。既存のスクリプトを開いて、保存ループの直前に len(blobs) を確認する行を1つ入れてみてください。

まだ移行前で、そもそもどのコードが停止対象を呼んでいるか掴めていない場合は、8月17日の停止対象を自分のコードから洗い出すの棚卸し手順から始めると迷いが減ります。

配布済みのアプリ側にモデル名が焼き込まれていて、停止日をまたいだ切り戻しを押してよいか迷っている場合は、判断の立て方を配布済みアプリに残るモデル名と、停止日をまたいだ切り戻しの設計で扱っています。

移行の最後の一歩で止まっている方の助けになれば嬉しいです。お読みいただきありがとうございました。

シェア

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

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

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

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

関連記事

API / SDK2026-08-15
generate_images は2027年まで残ります。それでも8月17日に画像生成は止まります
google-genai 2.18.1 には generate_images がまだ残っており、SDK の非推奨メッセージが指す期限は2027年です。一方で imagen-4.0 系のモデルは8月17日に止まります。2つの期限の違いと、手元で確認できること・できないことを実測で整理しました。
API / SDK2026-08-14
generate_images から generate_content へ書き換えるとき、引数は素直に移りません
8月17日の Imagen 停止に向けて generate_images を generate_content へ書き換えるとき、17個の引数のうちそのまま移せるのは5個でした。SDK を実際に読んで確かめた対応表と、呼び出し側を止めないための互換レイヤー、API キーなしで形だけ検証する方法をまとめます。
API / SDK2026-06-11
Gemini 3.2 API 実装ガイド — 正しいモデルID・3.1 からの移行と本番チェックポイント
Gemini 3.2 を API から呼び出すための正しいモデルID、Gemini 3.1 からの移行時の変更点、Python・TypeScript の実装例、本番環境への移行チェックリストをまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →