個人開発で出している壁紙アプリのストア説明文を各言語へ直すために、私は小さなスクリプトを走らせております。先週その設定ファイルを開いたとき、そこに書いてあるモデル ID を最後に見直したのがいつだったか、どうしても思い出せませんでした。
動いているうちは開かないファイルというものがあります。私にとっては、これがそれでした。
気になって公式の廃止表を開いたところ、そこで見た内容は、事前に読んでいた日本語の解説といくつも食い違っておりました。以下は、その食い違いと、二度と同じ確かめ方をしないために組んだ棚卸しの手順です。
「10月16日」は公式の廃止表に見当たりませんでした
先に結論をお伝えします。Gemini 2.5 Pro / 2.5 Flash / 2.5 Flash-Lite が 2026年10月16日に廃止されるという説明を複数の記事で見かけますが、Gemini API の deprecations ページでは、この3モデルはいずれも「No shutdown date announced(廃止日は未発表)」です。10月16日という日付は、この表のどこにも現れません。
実際に載っている近い日付は、別のモデルのものでした。2026年9月14日時点で確認できた分を書き出します。
| モデル ID | 停止予定日(最も早い場合) | 推奨の置き換え先 |
|---|---|---|
gemini-omni-flash-preview | 2026-09-30 | gemini-omni-1.1-flash |
gemini-2.5-flash-image | 2026-10-02 | gemini-3.1-flash-image-preview |
gemini-3.1-flash-lite | 2027-05-07 | gemini-3.5-flash-lite |
gemini-2.5-pro | 未発表 | — |
gemini-2.5-flash | 未発表 | — |
gemini-2.5-flash-lite | 未発表 | — |
食い違いの理由として、ひとつだけ確からしい説明があります。Gemini API と、Google Cloud 側の Gemini Enterprise Agent Platform では、廃止スケジュールの表が別々に管理されているということです。10月16日という日付が後者の表に由来する可能性は残ります。私はその表を一次情報で確認できておりませんので、断定はいたしません。
ただ、ここから引ける線はひとつはっきりしております。自分がどの経路で叩いているかを決めてから、その経路の表を読むようにしております。 経路を決めないまま検索結果の日付を拾いますと、今回のように、自分には関係のないかもしれない期限に一時間ほど振り回されることになります。
「廃止日は未発表」は「当分使える」という意味ではありません
表を読むときに、もうひとつ気をつけていただきたいことがあります。deprecations ページには、注記としてこう書かれております。表に載っている停止日は「最も早い場合の日付」であり、実際の停止日は事前に別途通知される、と。
つまりこの表は、いつ止まるかの確定した予定ではなく、それより早くは止めないという下限を示しております。カレンダーに入れるべきは、表の日付そのものではなく、その手前の余白のほうです。
逆方向の落とし穴もあります。「未発表」と書かれていても、提供が続くとは限りません。開発者フォーラムには、停止予定日より前に非推奨扱いになったという報告や、新規ユーザーには提供されなくなったという報告が上がっております。既存の呼び出しが通ることと、これから同じ構成を誰かが再現できることは、別の話なのです。
最初のうち、私は日付のほうを覚えようとしておりました。結果は芳しくありませんでした。表は更新されますし、記憶のほうは更新されません。いまは日付ではなく、突き合わせる場所と手順のほうを残すという線引きにしております。覚えるべきだったのは数字ではなく確かめ方だったのだと気づいたのは、ずいぶん経ってからでした。
呼んでいるモデル ID を、リポジトリから機械的に洗い出します
手順の一本目は、洗い出しです。設定ファイルに書いたモデル ID は、たいてい一箇所には収まっておりません。フォールバック先、テストのモック、README のサンプル、しばらく触っていないシェルスクリプト——散らばったまま忘れられます。
そこで、ディレクトリを丸ごと走査して ID を拾うだけの小さなスクリプトを置いております。
#!/usr/bin/env python3
"""呼び出しているモデル ID を、リポジトリ全体から洗い出します。"""
import argparse
import json
import pathlib
import re
import sys
# Gemini 系の ID に共通する形。世代が増えても拾えるよう緩めに取ります。
MODEL_RE = re.compile(
r"\b(?:gemini|imagen|veo|lyria|text-embedding|embedding)[a-z0-9.\-]*\b",
re.IGNORECASE,
)
SKIP_DIRS = {".git", "node_modules", ".next", "dist", "build", "__pycache__", ".venv"}
SCAN_SUFFIXES = {
".py", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".go", ".rb", ".sh",
".json", ".yaml", ".yml", ".toml", ".env", ".md", ".txt",
}
def iter_files(root: pathlib.Path):
for path in root.rglob("*"):
if not path.is_file():
continue
if SKIP_DIRS & set(path.parts):
continue
if path.suffix.lower() not in SCAN_SUFFIXES:
continue
yield path
def collect(root: pathlib.Path) -> dict:
found: dict[str, list[str]] = {}
for path in iter_files(root):
try:
text = path.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
for lineno, line in enumerate(text.splitlines(), 1):
for hit in MODEL_RE.findall(line):
model = hit.lower().rstrip(".-")
# "gemini" 単体など、版を含まない語は落とします
if "-" not in model and "." not in model:
continue
found.setdefault(model, []).append(f"{path.relative_to(root)}:{lineno}")
return found
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("root", help="走査するディレクトリ")
ap.add_argument("--json", action="store_true", help="JSON で出力します")
args = ap.parse_args()
root = pathlib.Path(args.root).resolve()
found = collect(root)
if args.json:
print(json.dumps(found, ensure_ascii=False, indent=2, sort_keys=True))
return 0
if not found:
print("モデル ID は見つかりませんでした")
return 0
for model in sorted(found):
places = found[model]
print(f"{model} ({len(places)} 箇所)")
for place in places[:3]:
print(f" {place}")
if len(places) > 3:
print(f" … ほか {len(places) - 3} 箇所")
return 0
if __name__ == "__main__":
sys.exit(main())正規表現を緩めに取っているのには理由があります。厳密に gemini-\d+\.\d+-(pro|flash) のような形で書くと、次の世代で命名が少し変わったときに、黙って何も拾わなくなるからです。棚卸しの道具が無音で失敗するのは、最も避けたい壊れ方です。緩く拾って、版を含まないものだけを後から落とす順序にしております。
手元で試すときは、モデル ID を書いた小さなディレクトリを作って、拾えることを先に確かめてください。
python3 model_inventory.py ./samplegemini-2.0-flash-001 (1 箇所)
src/app.py:2
gemini-2.5-flash (1 箇所)
src/app.py:1
gemini-2.5-flash-image (1 箇所)
conf/agent.yaml:2
gemini-3.5-flash (1 箇所)
conf/agent.yaml:1
text-embedding-004 (1 箇所)
src/app.py:3洗い出した ID を、手元の台帳と突き合わせます
二本目は突き合わせです。一次表をその場で取得して解析する作りにはしておりません。ページの構造が変わった日に、棚卸しのほうが先に壊れてしまうからです。代わりに、表から書き写した CSV を手元に置き、書き写した日を自分で管理するかたちにしております。
model,shutdown,replacement
gemini-3.8-flash,,
gemini-3.5-flash,,
gemini-2.5-pro,,
gemini-2.5-flash,,
gemini-2.5-flash-image,2026-10-02,gemini-3.1-flash-image-preview
gemini-2.0-flash-001,2026-06-01,gemini-3.6-flash
gemini-omni-flash-preview,2026-09-30,gemini-omni-1.1-flash
text-embedding-004,2026-01-14,gemini-embedding-2
gemini-embedding-001,2028-05-14,gemini-embedding-2空欄は「未発表」を表します。空欄と、そもそも行が無いことを、意識して区別しております。
#!/usr/bin/env python3
"""洗い出したモデル ID を、手元の廃止台帳と突き合わせます。"""
import argparse
import csv
import datetime as dt
import json
import pathlib
import sys
SOON_DAYS = 60
def load_table(path: pathlib.Path) -> dict:
table = {}
with path.open(encoding="utf-8", newline="") as fh:
for row in csv.DictReader(fh):
model = (row.get("model") or "").strip()
if not model:
continue
raw = (row.get("shutdown") or "").strip()
table[model] = {
"shutdown": dt.date.fromisoformat(raw) if raw else None,
"replacement": (row.get("replacement") or "").strip(),
}
return table
def classify(model: str, table: dict, today: dt.date) -> tuple[str, str]:
if model not in table:
return "UNLISTED", "台帳に無い — 一次表で確認してください"
entry = table[model]
shutdown = entry["shutdown"]
if shutdown is None:
return "NO_DATE", "廃止日は未発表(提供継続の保証ではありません)"
days = (shutdown - today).days
repl = f" → {entry['replacement']}" if entry["replacement"] else ""
if days < 0:
return "PAST", f"{shutdown} に停止予定日を過ぎています{repl}"
if days <= SOON_DAYS:
return "SOON", f"{shutdown}(あと {days} 日){repl}"
return "SCHEDULED", f"{shutdown}(あと {days} 日){repl}"
ORDER = ["PAST", "SOON", "UNLISTED", "SCHEDULED", "NO_DATE"]
FAIL_ON = {"PAST", "SOON", "UNLISTED"}
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("inventory", help="model_inventory.py --json の出力")
ap.add_argument("table", help="廃止台帳 CSV")
ap.add_argument("--today", default=None, help="基準日(省略時は本日)")
args = ap.parse_args()
today = dt.date.fromisoformat(args.today) if args.today else dt.date.today()
inventory = json.loads(pathlib.Path(args.inventory).read_text(encoding="utf-8"))
table = load_table(pathlib.Path(args.table))
buckets: dict[str, list[str]] = {key: [] for key in ORDER}
for model, places in sorted(inventory.items()):
status, note = classify(model, table, today)
where = places[0] if places else "?"
buckets[status].append(
f" {model:<32} {note} [{where} ほか {len(places)} 箇所]"
)
exit_code = 0
for key in ORDER:
if not buckets[key]:
continue
print(f"[{key}]")
print("\n".join(buckets[key]))
if key in FAIL_ON:
exit_code = 1
return exit_code
if __name__ == "__main__":
sys.exit(main())先ほどのサンプルに対して流すと、こうなりました。
python3 model_inventory.py ./sample --json > inventory.json
python3 check_shutdown.py inventory.json shutdown_dates.csv[PAST]
gemini-2.0-flash-001 2026-06-01 に停止予定日を過ぎています → gemini-3.6-flash [src/app.py:2 ほか 1 箇所]
text-embedding-004 2026-01-14 に停止予定日を過ぎています → gemini-embedding-2 [src/app.py:3 ほか 1 箇所]
[SOON]
gemini-2.5-flash-image 2026-10-02(あと 18 日) → gemini-3.1-flash-image-preview [conf/agent.yaml:2 ほか 1 箇所]
[NO_DATE]
gemini-2.5-flash 廃止日は未発表(提供継続の保証ではありません) [src/app.py:1 ほか 1 箇所]
gemini-3.5-flash 廃止日は未発表(提供継続の保証ではありません) [conf/agent.yaml:1 ほか 1 箇所]ここで意図的にそうしている点を、ふたつ書き残します。
ひとつは、台帳に無い ID を UNLISTED として失敗扱いにしていることです。知らない ID が増えたときに黙って通してしまうと、棚卸しの意味がなくなります。台帳を更新するか、綴りを直すか、どちらかを人が判断する場所として残しております。
もうひとつは、終了コードを 1 にする条件を、期限切れ・60日以内・台帳に無い、の3つに絞っていることです。「未発表」を失敗にすると、ほとんどの実行が赤くなり、やがて誰も見なくなります。止めるべき数を絞るほうが、結果として止まってくれるのだと感じております。
棚卸しを、月に一度の短い作業に落とし込みます
道具ができたら、置き場所を決めます。私は次の3つだけを決めて、それ以上は増やさないようにしております。
| 決めること | 私の場合 | 理由 |
|---|---|---|
| 台帳を書き写す頻度 | 月に一度 | 表の更新は日次ではなく、週次でも追いつく速さのため |
| 突き合わせを走らせる場所 | 手元と CI の両方 | 手元だけだと忙しい月に飛ばすため |
| 赤くなったときの持ち時間 | その日のうちに置き換え先を決めるところまで | 差し替え自体は急がなくても、判断を先送りにすると忘れるため |
受託でお預かりしているサイトの保守でも、月末に同じことをしております。エージェント型のツールに任せられる範囲についても、ひと言だけ添えます。洗い出しと突き合わせの実行、置き換え候補の下調べ、影響範囲の一覧作り——このあたりは任せて構わないと感じております。一方で、どの表を一次情報とみなすかという選択だけは、自分の手元に残すようにしております。エージェントは検索結果の日付をためらいなく持ってきますし、それが今回の10月16日のような数字である可能性を、こちらから排除する手立てがないからです。
版はツールに追わせても、期限の出どころだけは自分で選ぶようにしております。 今回の食い違いを見たあとで、この一文を台帳の先頭にコメントとして書き添えました。
なお、既定のモデルが入れ替わっても壊れない書き方については、Antigravity の既定モデルが入れ替わっても壊れないのは、出力を先に狭めてある仕事ですのほうに、出力形式を先に固定しておく話をまとめております。廃止への備えとしては、こちらと対になる内容です。
まずは、いま動いている一番小さなプロジェクトに model_inventory.py を一度だけ流して、自分が何を呼んでいるのかを目で見るところから始めていただければと思います。私の場合、思い出せなかった設定ファイルのほかに、二年ほど触っていないシェルスクリプトが一本出てまいりました。
お読みいただきありがとうございました。同じように、開かないままのファイルを抱えている方の役に立てば嬉しく思います。