LeeScot's Web
GCP Vertex AI Batch 設定 Step by Step:用 Gemini 批次處理大量文字
GCPVertex AIGeminiBatch Prediction

GCP Vertex AI Batch 設定 Step by Step:用 Gemini 批次處理大量文字

|
- 次瀏覽

先講結論:Batch Prediction 是什麼、為什麼要用

你有一批文字要丟給 Gemini 處理。可能是幾百筆客服記錄要分類,可能是上千篇文章要摘要。

一筆一筆送 API?可以。但你會撞到 rate limit,程式要處理重試邏輯,還要自己管狀態。

Batch Prediction 讓你把所有 request 打包成一個 JSONL 檔案,丟上去,等它跑完,一次撈回所有結果。不用管 rate limit,不用寫 retry,費用還比 online prediction 便宜。

代價是等待時間。Batch Job 不是即時的,排隊加處理可能幾分鐘到幾小時。但如果你不急,這是最省事的做法。


整體流程

啟用 API → 建 Bucket → 設權限 → 產金鑰 → 設環境變數
    → 準備 JSONL → 上傳 GCS → 建 Batch Job → 輪詢狀態 → 下載結果

前半段只做一次,後半段每次跑新 Job 都會用到。


1. 前置準備

你需要:

  • Google 帳號
  • GCP 專案(已有或新建都行)
  • 本機開發環境(Node.js 18+ 或 Python 3.10+)

登入 GCP 控制台後,先記下兩個東西:

項目範例值在哪裡看
專案 IDmy-gcp-project-123控制台頂部的專案選擇器
服務帳戶 Emailvertex-batch-sa@my-gcp-project-123.iam.gserviceaccount.comIAM → 服務帳戶頁面

2. 啟用 Vertex AI API

方法一:Console(推薦新手)

前往 Vertex AI Studio:

https://console.cloud.google.com/vertex-ai?project=YOUR_PROJECT_ID

頁面會提示啟用所需 API,點「同意並繼續」。需要啟用的 API:

  • Vertex AI API(aiplatform.googleapis.com
  • Cloud Storage(storage.googleapis.com
  • Dataform API(Vertex AI 相依)

等 1~2 分鐘,頂部警告橫幅消失就好。

方法二:gcloud CLI

gcloud services enable aiplatform.googleapis.com --project=YOUR_PROJECT_ID
gcloud services enable storage.googleapis.com --project=YOUR_PROJECT_ID

3. 建立 Cloud Storage Bucket

Batch Job 需要一個 GCS Bucket 來放輸入的 JSONL 和輸出結果。

Console 建立

前往 Cloud Storage → 點「建立 bucket」:

欄位建議值說明
名稱vertex-batch-YOUR_PROJECT_ID全球唯一,建議加上專案 ID
位置類型Region單一區域,費用最低
區域asia-east1(台灣)價格與 us-central1 相同,有地利之便
儲存空間級別Standard適合頻繁存取
存取控制統一(IAM)安全性較高
公開存取禁止公開存取必選

gcloud CLI

gcloud storage buckets create gs://vertex-batch-YOUR_PROJECT_ID \
  --project=YOUR_PROJECT_ID \
  --location=asia-east1

Bucket 區域要跟 GCP_LOCATION=global 一致嗎?

不用。 兩件事不要搞混:

  • GCP_LOCATION=global → Vertex AI API endpoint 的設定,決定你呼叫哪個 API 入口
  • Bucket 的 location=asia-east1 → 資料實際儲存的地方

global endpoint 可以存取任何 region 的 Bucket,asia-east1 對台灣用戶延遲最低,價格與 us-central1 相同,兩者完全相容。

Bucket 不要設成 multi-region
multi-region storage 費用約為 regional 的 2 倍,對 Batch Job 沒有效能優勢。regional bucket 在同區域存取延遲更低,價格更便宜。

Lifecycle Policy:自動清理舊檔案

GCS 的檔案不會自動過期,放多久就收多久的錢。費用其實很低(Standard Storage $0.020/GB/月),但養成好習慣仍然值得。

設定方式:Bucket →「生命週期」頁籤 →「新增規則」→ 動作選「刪除物件」→ 條件選「存在時間 30 天」。

或用 CLI:

cat > lifecycle.json << 'EOF'
{
  "lifecycle": {
    "rule": [{
      "action": { "type": "Delete" },
      "condition": { "age": 30 }
    }]
  }
}
EOF

gcloud storage buckets update gs://YOUR_BUCKET_NAME --lifecycle-file=lifecycle.json

4. 設定 IAM 服務帳戶權限

服務帳戶需要三個角色,分別在專案層級Bucket 層級設定。

專案層級

前往 IAM 頁面,找到你的服務帳戶,點右側鉛筆圖示,新增:

角色用途
Vertex AI 使用者roles/aiplatform.user提交 / 查詢 Batch Job
BigQuery 使用者Vertex AI 內部相依(通常系統會自動加)
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member="serviceAccount:YOUR_SA_EMAIL" \
  --role="roles/aiplatform.user"

Bucket 層級

前往 Bucket 的「權限」頁籤 → 「授予存取權」→ 填入服務帳戶 Email → 角色選 Storage 物件使用者roles/storage.objectUser)。

gcloud storage buckets add-iam-policy-binding gs://YOUR_BUCKET_NAME \
  --member="serviceAccount:YOUR_SA_EMAIL" \
  --role="roles/storage.objectUser"

權限總覽

角色層級用途
Vertex AI 使用者專案提交 / 查詢 Batch Job
Storage 物件使用者Bucket上傳 JSONL / 下載結果
BigQuery 使用者專案Vertex AI 內部相依

權限不足是最常見的卡關原因
如果遇到Permission denied,第一步就是回來對這張表。


5. 產生服務帳戶 JSON 金鑰

本地開發需要 JSON 金鑰來驗證身份。

  1. 前往「IAM 與管理」→「服務帳戶」→ 點你的 SA → 上方「金鑰」頁籤
  2. 若還未建立服務帳戶,先建一個
  3. 點「新增鍵」→「建立新的金鑰」→ 類型選 JSON → 建立
  4. 瀏覽器會下載一個 .json 檔案
  5. 移到安全目錄:
mkdir -p ~/.gcp
mv ~/Downloads/my-gcp-project-*.json ~/.gcp/service-account.json

金鑰洩漏 = 別人用你的 GCP 帳戶花你的錢
~/.gcp/.env.local 加進 .gitignore,不要上傳到任何公開平台。


6. 設定環境變數

在專案根目錄建立 .env.local

# 服務帳戶 JSON 金鑰的完整路徑
GOOGLE_APPLICATION_CREDENTIALS=/Users/yourname/.gcp/service-account.json

# GCP 專案 ID
GCP_PROJECT_ID=my-gcp-project-123

# Vertex AI 區域 — 重要:用 global
GCP_LOCATION=global

# Bucket 名稱(不含 gs:// 前綴)
GCS_BUCKET_NAME=vertex-batch-my-gcp-project-123

各變數說明

變數名稱取得方式備註
GOOGLE_APPLICATION_CREDENTIALS步驟 5 下載的 JSON 檔案路徑用絕對路徑
GCP_PROJECT_IDGCP 控制台頂部專案選擇器
GCP_LOCATION固定填 global見下方說明
GCS_BUCKET_NAME步驟 3 建立的 Bucket 名稱不含 gs:// 前綴

GCP_LOCATION 為什麼要用 global

這是最容易踩到的坑。

Preview 模型(如 gemini-3.1-pro-preview)的 Batch Prediction 只有 global 才能用。設成 asia-east1us-central1 會收到:

The PublisherModel gemini-3.1-pro-preview does not exist

模型名稱完全正確,但 location 不對就是 NOT_FOUND。穩定版模型在 asia-east1 等特定 region 也能跑,但為了統一,直接用 global 省得換模型還要改 location。


7. 準備 JSONL 輸入檔案

JSONL 是什麼?

JSONL(JSON Lines)= 每行一個獨立的 JSON 物件。每行代表一筆要送給 Gemini 的 request。

基本格式

{
  "request": {
    "systemInstruction": {
      "parts": [{ "text": "你是文字處理助手。請用繁體中文回覆。" }]
    },
    "contents": [{
      "role": "user",
      "parts": [{ "text": "[REQUEST_ID:req_001]\n請摘要以下文章:..." }]
    }],
    "generationConfig": {
      "thinkingConfig": { "thinkingBudget": 8192 }
    }
  }
}

實際 JSONL 中每筆 request 必須壓成一行
上面是格式化顯示,方便閱讀。

怎麼識別每筆 request?

Vertex AI Batch Prediction 沒有頂層 key 欄位來標記資料。解法是把識別碼嵌入 prompt 開頭:

[REQUEST_ID:req_001]
請摘要以下文章...

結果回來後,從 response 內容中找到這個標記,就能對應回原始資料。

用 Node.js 產生 JSONL

import * as fs from 'fs';

interface RequestItem {
  id: string;
  text: string;
}

function buildJsonl(items: RequestItem[]): string {
  return items.map(item => {
    return JSON.stringify({
      request: {
        systemInstruction: {
          parts: [{ text: '你是文字處理助手。請用繁體中文回覆。' }]
        },
        contents: [{
          role: 'user',
          parts: [
            { text: `[REQUEST_ID:${item.id}]\n${item.text}` }
          ]
        }],
        generationConfig: {
          thinkingConfig: { thinkingBudget: 8192 }
        }
      }
    });
  }).join('\n');
}

const jsonl = buildJsonl([
  { id: 'req_001', text: '請摘要以下文章:...' },
  { id: 'req_002', text: '請分類以下客服記錄:...' },
]);
fs.writeFileSync('./input.jsonl', jsonl);

8. 建立並執行 Batch Job

上傳 JSONL 到 GCS

Console:前往 Bucket → 點「上傳檔案」→ 選 input.jsonl。建議放在子資料夾中,例如 batch-jobs/job_001/input.jsonl

gcloud CLI

gcloud storage cp ./input.jsonl gs://YOUR_BUCKET_NAME/batch-jobs/job_001/input.jsonl

Node.js SDK

import { Storage } from '@google-cloud/storage';

const storage = new Storage({
  keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
  projectId: process.env.GCP_PROJECT_ID,
});

const bucket = storage.bucket(process.env.GCS_BUCKET_NAME!);
const file = bucket.file('batch-jobs/job_001/input.jsonl');
await file.save(jsonlContent, { contentType: 'application/jsonl' });

建立 Batch Prediction Job

curl(REST API)

PROJECT_ID="my-gcp-project-123"
LOCATION="global"
MODEL="gemini-2.5-pro"
BUCKET="vertex-batch-my-gcp-project-123"
ACCESS_TOKEN=$(gcloud auth print-access-token)

curl -X POST \
  "https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/batchPredictionJobs" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"displayName\": \"text-processing-batch-001\",
    \"model\": \"publishers/google/models/${MODEL}\",
    \"inputConfig\": {
      \"instancesFormat\": \"jsonl\",
      \"gcsSource\": {
        \"uris\": [\"gs://${BUCKET}/batch-jobs/job_001/input.jsonl\"]
      }
    },
    \"outputConfig\": {
      \"predictionsFormat\": \"jsonl\",
      \"gcsDestination\": {
        \"outputUriPrefix\": \"gs://${BUCKET}/batch-jobs/job_001/output/\"
      }
    }
  }"

回傳會包含 name 欄位(Job 的完整路徑),後續查詢狀態要用。

Node.js SDK

import { v1 } from '@google-cloud/aiplatform';

const client = new v1.JobServiceClient({
  keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
  apiEndpoint: `${process.env.GCP_LOCATION}-aiplatform.googleapis.com`,
});

const [job] = await client.createBatchPredictionJob({
  parent: `projects/${process.env.GCP_PROJECT_ID}/locations/${process.env.GCP_LOCATION}`,
  batchPredictionJob: {
    displayName: 'text-processing-batch-001',
    model: 'publishers/google/models/gemini-2.5-pro',
    inputConfig: {
      instancesFormat: 'jsonl',
      gcsSource: {
        uris: [`gs://${process.env.GCS_BUCKET_NAME}/batch-jobs/job_001/input.jsonl`],
      },
    },
    outputConfig: {
      predictionsFormat: 'jsonl',
      gcsDestination: {
        outputUriPrefix: `gs://${process.env.GCS_BUCKET_NAME}/batch-jobs/job_001/output/`,
      },
    },
  },
});

console.log('Job 已建立:', job.name);

支援的模型

Model ID說明
gemini-2.5-pro穩定版,支援 asia-east1us-central1global
gemini-3.1-pro-preview最新 pro,需 global
gemini-3.1-flash-image-previewgemini-3-pro-image-preview 便宜,需 global

模型清單會隨 Google 更新
Vertex AI Model Garden 為準。


9. 查看 Job 狀態與下載結果

Console 查看

前往 Vertex AI 批次推論頁面:

https://console.cloud.google.com/vertex-ai/batch-predictions?project=YOUR_PROJECT_ID

curl 查詢

JOB_NAME="projects/my-gcp-project-123/locations/global/batchPredictionJobs/123456789"

curl -X GET \
  "https://global-aiplatform.googleapis.com/v1/${JOB_NAME}" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)"

Job 狀態對照表

狀態意思你該做什麼
JOB_STATE_QUEUED排隊中
JOB_STATE_RUNNING執行中
JOB_STATE_SUCCEEDED完成下載結果
JOB_STATE_FAILED失敗看 error 欄位
JOB_STATE_CANCELLED已取消重新送出

下載結果

# 列出輸出檔案
gcloud storage ls gs://YOUR_BUCKET_NAME/batch-jobs/job_001/output/

# 下載到本地
gcloud storage cp "gs://YOUR_BUCKET_NAME/batch-jobs/job_001/output/*.jsonl" ./results/

解析結果 JSONL

結果每行也是一個 JSON,包含 response 欄位:

{
  "response": {
    "candidates": [{
      "content": {
        "role": "model",
        "parts": [{ "text": "這篇文章的主要內容是..." }]
      },
      "finishReason": "STOP"
    }],
    "usageMetadata": {
      "promptTokenCount": 256,
      "candidatesTokenCount": 150,
      "totalTokenCount": 406
    }
  }
}

用 Node.js 解析:

import * as fs from 'fs';

const content = fs.readFileSync('./results/output.jsonl', 'utf-8');
const lines = content.split('\n').filter(Boolean);

for (const line of lines) {
  const result = JSON.parse(line);
  const text = result.response?.candidates?.[0]?.content?.parts?.[0]?.text;
  if (text) {
    const idMatch = text.match(/\[REQUEST_ID:(\w+)\]/);
    console.log(`${idMatch?.[1] ?? 'unknown'}: ${text.substring(0, 100)}...`);
  }
}

10. 配額與結算

查看配額

前往配額頁面,篩選條件輸入 GenerateContent,可以看到所有 Gemini 相關配額。需要提高的話,點右側「⋮」→「編輯配額」。

https://console.cloud.google.com/iam-admin/quotas?project=YOUR_PROJECT_ID

結算帳戶

確認帳單帳戶已連結專案、目前費用與預算。

https://console.cloud.google.com/billing?project=YOUR_PROJECT_ID

Vertex AI Batch Job 使用付費層級
需確保結算帳戶已啟用,否則會遇到配額限制錯誤。


11. 踩坑指南

JSONL 格式錯誤(最常見)

Vertex AI 的 JSONL 格式比一般 Gemini API 嚴格得多。很多在 online prediction 能跑的寫法,在這裡都會報錯。

錯誤訊息原因怎麼修
Please use a valid role: user, modelcontents 沒寫 role加上 "role": "user"
Cannot store struct with no fieldsgoogleSearch: {} 是空物件改成 { "exclude_domains": [] }
google_search_retrieval is not supported用了舊的 tool 名稱改用 googleSearch
no such field: 'dynamicRetrievalConfig'不是合法欄位移除,用 exclude_domains 代替
no such field: 'outputMimeType'Batch 不支援generationConfig 移除

先跑一筆
用只有 1 行的 JSONL 測試格式,確認通過再放大量資料。

模型找不到

The PublisherModel X does not exist

90% 的 NOT_FOUND 錯誤都是 GCP_LOCATION 沒設成 global。模型名稱對但 location 錯,就是找不到。

權限不足

錯誤訊息怎麼修
Permission denied: storage.objects.createroles/storage.objectUser(Bucket 層級)
Permission denied: batchPredictionJobs.createroles/aiplatform.user(專案層級)

確認目前權限:

gcloud projects get-iam-policy YOUR_PROJECT_ID \
  --flatten="bindings[].members" \
  --filter="bindings.members:YOUR_SA_EMAIL" \
  --format="table(bindings.role)"

從 Gemini Batch API 遷移過來?

兩套系統長得很像但有差異:

項目Gemini Batch APIVertex AI Batch Prediction
識別碼頂層 "key": "req_001"無頂層 key,嵌入 prompt
role 欄位可省略必須 "role": "user"
檔案上傳/下載Gemini File APIGCS
認證方式API KeyService Account JSON 金鑰

除錯心法

  1. 先跑單筆 — 1 行 JSONL 測格式,對了再放量
  2. 看 Job error — Console 點 Job 名稱,或 GET API 的 error 欄位
  3. 確認 global — NOT_FOUND 先改 location
  4. 檢查 GCS 輸出路徑 — Job 完成但看不到結果?去 Bucket 裡看 output 資料夾

12. 重要頁面 URL 整理

YOUR_PROJECT_ID 替換成你的專案 ID:

頁面URL
GCP 控制台首頁https://console.cloud.google.com/home/dashboard?project=YOUR_PROJECT_ID
Vertex AI Studiohttps://console.cloud.google.com/vertex-ai/studio/multimodal?project=YOUR_PROJECT_ID
批次推論https://console.cloud.google.com/vertex-ai/batch-predictions?project=YOUR_PROJECT_ID
Cloud Storagehttps://console.cloud.google.com/storage/browser?project=YOUR_PROJECT_ID
IAM 權限管理https://console.cloud.google.com/iam-admin/iam?project=YOUR_PROJECT_ID
服務帳戶https://console.cloud.google.com/iam-admin/serviceaccounts?project=YOUR_PROJECT_ID
配額https://console.cloud.google.com/iam-admin/quotas?project=YOUR_PROJECT_ID
結算帳戶https://console.cloud.google.com/billing?project=YOUR_PROJECT_ID

FAQ

Job 一直卡在排隊不動? Batch Prediction 不保證排隊時間,尖峰可能等數小時。超過 24 小時考慮 cancel 重送。

部分 request 成功、部分失敗? 結果 JSONL 中,失敗的 request 會有 error 欄位。挑出失敗的重組 JSONL 再送一次。

JSONL 很大(>100MB)? GCS SDK 上傳預設就是 resumable upload,確保網路穩定即可。Console 有檔案大小限制,大檔案用 CLI 或 SDK。

可以用 v1beta1 API 嗎? 可以。某些新功能(如 thinking config)可能只在 v1beta1 可用,把 endpoint 的 v1 改成 v1beta1 就行。


參考資源