🎯 はじめに:「同じ Batch API」と思って書き始めると確実にハマる
本記事はシリーズ 「Gemini AI プラットフォーム活用」の最終回(第 6 回) です。前回 Part 5: Vertex AI 移行ガイド(基本編) では「vertexai=True を加えるだけで移行完了」と書きました。これは Batch API を除いた話です。
⚠ 本記事の核心: AI Studio の Batch API(Part 3)と Vertex AI の Batch API は、同じ「Gemini Batch API」を名乗りながら入出力経路が完全に別物です。前者は Files API(Google が提供する一時ファイル領域)、後者は GCS(Google Cloud Storage)バケットを経由します。SDK 上で
client.batches.create()という同名のメソッドを呼ぶため、引数構造が全く違うことに気付かないままコピペすると、リクエスト失敗・JSONL スキーマ不一致・IAM 権限不足・ジョブ ID 形式の差異で立て続けに詰まります。
本記事は、その書き直しコストを最小化するための実装ガイドです。
🔍 AI Studio Batch vs Vertex AI Batch の決定的な違い
| 項目 | AI Studio Batch(Part 3) | Vertex AI Batch |
|---|---|---|
| 入力経路 | client.files.upload() で Files API にアップロード | GCS バケットに JSONL を gs:// URI で配置 |
| 出力経路 | client.files.download() で Files API から取得 | GCS バケットに JSONL が出力される |
| 認証 | APIキー | サービスアカウント / ADC(要 GCS + Vertex AI 権限) |
JSONL の request スキーマ | Gemini Developer API 形式 | Vertex AI 形式(generateContent 互換) |
| ジョブ ID 形式 | batches/xxx | projects/.../locations/.../batchPredictionJobs/xxx |
| 対応モデル | Gemini 全モデル + Embedding 系全モデル | Gemini LLM 系(Embedding 系は限定的、gemini-embedding-001 は未対応) |
| インフラ準備 | 不要(APIキーのみ) | GCS バケット作成、IAM ロール、リージョン整合 |
| 料金 | 通常の 50% オフ | 通常の 50% オフ(同等) |
| ジョブ管理 UI | AI Studio の Dashboard | Vertex AI コンソール → バッチ推論 |
💡 「同じ SDK で
vertexai=Trueを切り替えるだけ」が通じない数少ない領域。ここだけは設計時から Vertex AI 用に書き直す前提でいてください。
🚨 ハマりどころ Top 5
1. gemini-embedding-001 は Vertex AI Batch では未サポート
公式ドキュメント(Vertex AI Batch Embeddings)で明記されており、多言語対応の主力 Embedding モデルである gemini-embedding-001 は Vertex AI Batch API で使えません。
このため、エンタープライズ運用でも以下の二系統運用が現実解になります。
Embedding 系 → AI Studio Batch(Files API) ← 多言語 gemini-embedding-001 が使える
LLM 系 → Vertex AI Batch(GCS) ← gemini-2.5-flash 等、エンタープライズ要件
「将来 Vertex AI に統一」という雑な計画は破棄しましょう。少なくとも 2026 年時点では、日本語ワークロードの Embedding を Vertex AI Batch に寄せるのは不可能です。
2. JSONL スキーマが違う(コピペが効かない)
同じ「JSONL に 1 行 1 リクエスト」でも、request 配下のスキーマが異なります。
AI Studio Batch(Part 3 で扱った形式):
{
"key": "req_1",
"request": {
"contents": [
{ "parts": [{ "text": "商品Aを 1 行で説明して" }] }
]
}
}
Vertex AI Batch:
{
"request": {
"contents": [
{ "role": "user", "parts": [{ "text": "商品Aを 1 行で説明して" }] }
],
"generationConfig": { "temperature": 0.2, "maxOutputTokens": 256 },
"systemInstruction": { "parts": [{ "text": "あなたは EC のコピーライターです。" }] }
}
}
主な差分:
- AI Studio は
keyで結果突合、Vertex AI は 行順 orlabelsフィールドで突合 - Vertex AI は
roleを明示する必要がある(user/model/system) generationConfig/systemInstructionは Vertex AI 形式準拠(generateContentAPI と同じスキーマ)
⚠ JSONL を生成するコードは別関数として書く。共通化して
if vertex: ... else: ...で分岐させると、後でモデル機能差分(thinking、grounding、function calling など)が増えるたびに分岐が腐ります。
3. GCS バケットのリージョンは Vertex AI のリージョンと一致必須
NG: GCS バケット = us-central1, Vertex AI = asia-northeast1
→ ジョブ作成時に "Bucket region must match" エラー
OK: GCS バケット = asia-northeast1, Vertex AI = asia-northeast1
マルチリージョンバケット(asia など)も NG のケースがあります。シングルリージョンで揃えるのが安全。
4. IAM ロールの組み合わせを間違えると無限リトライする
最低限必要な権限:
| 主体 | 必要なロール | 用途 |
|---|---|---|
| 実行サービスアカウント | roles/aiplatform.user | Vertex AI ジョブ投入 |
| 実行サービスアカウント | roles/storage.objectAdmin(対象バケット限定) | 入力 JSONL 読込 + 出力 JSONL 書込 |
| Vertex AI サービスエージェント(自動作成される) | roles/storage.objectAdmin(対象バケット限定) | バックエンド側で GCS にアクセス |
⚠ 3 番目のサービスエージェント権限を付け忘れる事故が頻発します。「自分のサービスアカウントには付けたのに動かない」場合、これが原因。Vertex AI のジョブ実行は内部的に
service-{project_number}@gcp-sa-aiplatform.iam.gserviceaccount.comというエージェントが GCS を読み書きします。
5. ジョブ ID 形式が違うので、SDK の client.files.get() が使えない
AI Studio Batch のジョブ取得は batches/xxx(短い識別子)ですが、Vertex AI は projects/{p}/locations/{l}/batchPredictionJobs/{id}(長いリソース名)です。
# AI Studio
batch_job = client.batches.get(name="batches/abc123")
# Vertex AI(同じ SDK だが name の形式が違う)
batch_job = client.batches.get(
name="projects/my-project/locations/asia-northeast1/batchPredictionJobs/1234567890"
)
DB に保存するジョブ識別子は Provider 別に値オブジェクトで抽象化するのが安全です。例えば BatchJobId = "provider:raw_id" のような型を作っておけば、AI Studio / Vertex AI どちらの形式も同じカラムに格納でき、SAGA 状態遷移ロジックが Provider 中立になります。
🛠 実装テンプレート(Vertex AI Batch)
前提準備
# 1. GCS バケット作成(Vertex AI と同リージョン)
gcloud storage buckets create gs://your-vertex-batch-bucket \
--location=asia-northeast1 \
--uniform-bucket-level-access
# 2. サービスアカウントへの権限付与
PROJECT_ID="your-gcp-project"
SA="your-sa@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${SA}" \
--role="roles/aiplatform.user"
gcloud storage buckets add-iam-policy-binding gs://your-vertex-batch-bucket \
--member="serviceAccount:${SA}" \
--role="roles/storage.objectAdmin"
# 3. Vertex AI サービスエージェントへの権限付与(忘れがち)
PROJECT_NUM=$(gcloud projects describe "${PROJECT_ID}" --format='value(projectNumber)')
gcloud storage buckets add-iam-policy-binding gs://your-vertex-batch-bucket \
--member="serviceAccount:service-${PROJECT_NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
Python 実装
from google import genai
from google.cloud import storage
import json
import time
# Vertex AI モードでクライアント作成(Part 5 のとおり)
client = genai.Client(
vertexai=True,
project="your-gcp-project",
location="asia-northeast1",
)
BUCKET = "your-vertex-batch-bucket"
INPUT_PATH = "input/req_2026-05-02.jsonl"
OUTPUT_PREFIX = "output/req_2026-05-02"
# 1. JSONL を作成して GCS にアップロード
storage_client = storage.Client()
bucket = storage_client.bucket(BUCKET)
requests_data = [
{
"request": {
"contents": [
{"role": "user", "parts": [{"text": f"商品 {i} を 1 行で説明して"}]}
],
"generationConfig": {"temperature": 0.2, "maxOutputTokens": 256}
}
}
for i in range(1000)
]
# JSONL を 1 行ずつ書き出し → GCS へ
local_path = "/tmp/batch_input.jsonl"
with open(local_path, "w") as f:
for req in requests_data:
f.write(json.dumps(req, ensure_ascii=False) + "\n")
bucket.blob(INPUT_PATH).upload_from_filename(local_path)
print(f"アップロード完了: gs://{BUCKET}/{INPUT_PATH}")
# 2. Batch ジョブ作成(src / dest が gs:// URI)
batch_job = client.batches.create(
model="gemini-2.5-flash",
src=f"gs://{BUCKET}/{INPUT_PATH}",
config={
"display_name": "product-descriptions-vertex-batch",
"dest": f"gs://{BUCKET}/{OUTPUT_PREFIX}",
},
)
print(f"ジョブ作成: {batch_job.name}")
# → 例: projects/123/locations/asia-northeast1/batchPredictionJobs/9876543210
# 3. ポーリング(24 時間 SLO)
TERMINAL = {"JOB_STATE_SUCCEEDED", "JOB_STATE_FAILED", "JOB_STATE_CANCELLED"}
while batch_job.state.name not in TERMINAL:
time.sleep(60)
batch_job = client.batches.get(name=batch_job.name)
print(f"状態: {batch_job.state.name}")
# 4. 結果取得(GCS から)
if batch_job.state.name == "JOB_STATE_SUCCEEDED":
# 出力先プレフィックス配下の predictions.jsonl を読む
blobs = list(bucket.list_blobs(prefix=OUTPUT_PREFIX))
for blob in blobs:
if blob.name.endswith(".jsonl"):
content = blob.download_as_text()
for line in content.splitlines():
obj = json.loads(line)
# obj に request / response / status がそれぞれ入る
print(obj.get("response"))
Embedding は AI Studio 側で書く(再掲)
gemini-embedding-001 は Vertex AI Batch 未対応なので、Embedding だけは Part 3 / Part 4 の AI Studio 経路で書くのが正解です。
# Embedding は AI Studio で
ai_studio_client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
embed_batch = ai_studio_client.batches.create_embeddings(
model="gemini-embedding-001",
requests=[
{"content": "商品Aの説明..."},
{"content": "商品Bの説明..."},
],
config={"display_name": "embeddings-batch"},
)
🏗 二系統運用の設計パターン
Provider 抽象化(推奨)
実装上は 「どの Batch を使っているか」を呼び出し側に意識させない抽象化が効きます。
from abc import ABC, abstractmethod
class GeminiBatchProvider(ABC):
"""Gemini Batch API の Provider 抽象。AI Studio / Vertex AI を切替可能にする。"""
@abstractmethod
async def submit(self, jsonl_path: str) -> str: ...
@abstractmethod
async def get_status(self, job_id: str) -> str: ...
@abstractmethod
async def fetch_results(self, job_id: str) -> list[dict]: ...
class AiStudioBatchProvider(GeminiBatchProvider):
"""Files API 経路(gemini-embedding-001 など多言語 Embedding 向け)"""
# ...
class VertexBatchProvider(GeminiBatchProvider):
"""GCS 経路(LLM 系のエンタープライズ運用向け)"""
# ...
抽象化のメリット:
- 呼び出し側 SAGA / Workflow は「どの Provider を使うか」を Config で切替可能
- 将来 Vertex AI 側で
gemini-embedding-001がサポートされた時、Provider 1 つ差し替えで移行可能 - ジョブ ID 形式の違いも値オブジェクト(
BatchJobId = "provider:raw_id")で吸収
SAGA / 状態管理での冪等性
Batch API は最大 24 時間の非同期処理なので、SAGA パターン(Submit → Poll → Save の 3 段階)で状態を永続化するのが定石です。
[Submit Step]
├─ JSONL 作成 → GCS / Files API へアップロード
├─ batches.create() でジョブ投入
└─ DB に job_id, state=SUBMITTED で保存
[Poll Step]
├─ 1 分間隔で batches.get(name=job_id)
├─ TERMINAL 状態なら Save Step へ
└─ ポーリング上限超過 → state=TIMED_OUT で人手復旧待ち
[Save Step]
├─ 結果 JSONL を取得(GCS or Files API)
├─ DB に冪等保存(同じ job_id で再実行しても二重保存しない)
└─ state=SAVED で確定
💡 ポーリング上限を超えても課金は発生済み。タイムアウトしてデータが失われると、コストだけ払って結果なしの最悪パターンになります。SAGA で
TIMED_OUT状態を作り、job_id を指定して結果取得を再開できる手動復旧用 CLI コマンドを用意しておくのが運用の保険です。
🧰 ハマりどころチェックリスト
実装前に必ず確認:
- GCS バケットのリージョンは Vertex AI のリージョンと一致しているか?
- サービスアカウントに
roles/aiplatform.user+roles/storage.objectAdminを付与したか? - Vertex AI サービスエージェント(
service-{project_num}@gcp-sa-aiplatform...)に GCS 権限を付与したか? - JSONL の
requestスキーマは Vertex AI 形式(role必須、generationConfig、systemInstruction)か? - 使うモデルは Vertex AI Batch でサポートされているか?(特に Embedding 系は要確認)
- ジョブ ID を DB 保存するなら、Provider 別に値オブジェクトで抽象化したか?
- ポーリングタイムアウト時の復旧手段を用意したか?(24 時間 SLO 全使用時の対策)
- GCS バケットのライフサイクルポリシーを設定したか?(古い JSONL の自動削除)
🔄 二系統運用のコスト試算
「AI Studio + Vertex AI の両方を使う」運用で、月間コストはどう見えるか。Embedding を AI Studio、LLM を Vertex AI で運用するパターン:
仮定: 月 100 万件の商品を処理
Embedding: 1 件あたり 100 トークン入力
LLM: 1 件あたり 500 トークン入力 / 200 トークン出力
[AI Studio Batch(Embedding)]
Embedding: 100M トークン × $0.075 / 1M = $7.50
[Vertex AI Batch(LLM、gemini-2.5-flash)]
入力: 500M トークン × $0.075 / 1M = $37.50
出力: 200M トークン × $0.30 / 1M = $60.00
小計: $97.50
合計: $105/月(約 16,000 円)
💡 100 万件規模で月 1.6 万円。Batch API(50% オフ)を使わない場合は倍の 3.2 万円になります。書き直しコストは 1〜2 日でも、年間で約 20 万円の差。Vertex AI 側は GCS 経路の書き直しが必要ですが、その投資はすぐ回収できます。
✅ 第 6 回まとめ・シリーズ完結
- AI Studio Batch(Files API)と Vertex AI Batch(GCS)は 同名の API でも別物。
vertexai=True切替では足りない gemini-embedding-001は Vertex AI Batch 未対応 → Embedding は AI Studio 固定の二系統運用が現実解- JSONL スキーマも別物(
role必須、generationConfig/systemInstructionは Vertex AI 形式) - GCS バケットのリージョン整合と Vertex AI サービスエージェントの IAMが頻発するハマりどころ
- ジョブ識別子は形式が違うので
BatchJobId = "provider:raw_id"のように Provider 別値オブジェクトで抽象化するのが安全 - Batch API は 24 時間 SLO の非同期処理 → SAGA パターン(Submit / Poll / Save)で状態管理
- 二系統運用でも年間コストメリットは大きい(Batch 50% オフが両系統に効く)
シリーズ全 6 回を通じて、個人 PoC レベルから本番マルチテナント運用までの Gemini 活用の全体像を扱いました。各回の振り返り:
📚 シリーズ記事
| # | タイトル | 内容 |
|---|---|---|
| 1 | Google AI Studio 入門 | 課金・APIキー・料金体系・無料枠・予算アラート |
| 2 | モデル選定とマルチモーダル活用 | Flash / Pro / Flash-Lite、画像・動画・音声・PDF |
| 3 | Batch API で 50% 安く使う | 非同期バッチ、JSONL、Flash-Lite × Batch(AI Studio 編) |
| 4 | Python 実装ガイド | テキスト・Embedding・Vision・モデル切替 |
| 5 | Vertex AI 移行ガイド(基本編) | エンタープライズ運用、Provisioned Throughput |
| 6 | Vertex AI Batch API 編(本記事) | GCS 経路、AI Studio Batch との違い、二系統運用 |
🔗 関連リソース
- Vertex AI Batch Prediction 公式ドキュメント
- Vertex AI Batch Embeddings 公式 —
gemini-embedding-001未対応の根拠 - GCS バケット作成・IAM 公式
- Vertex AI Service Agents 公式