前六天把「為什麼」「防什麼」「放哪裡」「掛了怎麼辦」「怎麼分層」「拿什麼測」講完了。今天把兩條線的環境建起來,Week 2 開始所有文章都會有實際執行結果。
兩條線要能公平比較,所以環境設計的重點不是各自能跑,而是量測方式一致。
gcloud projects create guardrails-lab-2026 --name="Guardrails Lab"
gcloud config set project guardrails-lab-2026
gcloud services enable modelarmor.googleapis.com
gcloud services enable aiplatform.googleapis.com
gcloud services enable dlp.googleapis.com # Sensitive Data Protection
gcloud services enable logging.googleapis.com
【此處貼 API 啟用完成的終端輸出截圖】
Model Armor 是區域性服務,API 端點帶區域名稱。本系列統一用同一個區域,避免跨區延遲混進量測結果。
export MA_LOCATION=us-central1
export MA_ENDPOINT="modelarmor.${MA_LOCATION}.rep.googleapis.com"
【作者確認】發文前確認 Model Armor 目前支援的區域清單與端點格式,以官方文件為準。
gcloud iam service-accounts create guardrails-runner \
--display-name="Guardrails Lab Runner"
gcloud projects add-iam-policy-binding guardrails-lab-2026 \
--member="serviceAccount:guardrails-runner@guardrails-lab-2026.iam.gserviceaccount.com" \
--role="roles/modelarmor.user"
只給呼叫過濾 API 的權限,template 的建立與修改用另一個帳號。這不是本系列的重點,但寫 AI 資安的系列自己的實驗環境權限亂給說不過去。
gcloud model-armor templates create ma-baseline \
--location=$MA_LOCATION \
--basic-config-filter-enforcement=enabled
Template 的完整設定 Day 8 講,今天只要一個最基本的能回應。
curl -X POST "https://${MA_ENDPOINT}/v1/projects/guardrails-lab-2026/locations/${MA_LOCATION}/templates/ma-baseline:sanitizeUserPrompt" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{"userPromptData": {"text": "你好,請問今天天氣如何?"}}'
【此處貼第一次 sanitizeUserPrompt 的原始 JSON 回應截圖】
dgx-spark
├── llm-main ← 被保護的模型(vLLM 或 Ollama)
├── guard-l1 ← 規則引擎(Python,CPU)
├── guard-l2 ← ShieldGemma 推論(GPU)
├── gateway ← LiteLLM 閘道,串接上面三個
└── audit-sink ← 稽核日誌收集(先用檔案,Day 29 接 SIEM)
閘道用 LiteLLM 的原因:它有 pre-call / post-call hook,護欄可以用 sidecar 形式掛上去,不用改應用程式碼——這正是 Day 3 講的「位置 B」。
docker compose up -d llm-main gateway audit-sink
docker compose ps
【此處貼 docker compose ps 輸出截圖】
guard-l1 與 guard-l2 在 Week 3 才會加進來,今天先確認閘道能把請求原樣轉給主模型。
被保護的模型本身不是本系列重點,但要固定,否則攔截率會受模型本身對齊程度影響。本系列兩條線都用 Gemma 3 系列作為被保護的主模型:雲端線走 Vertex AI,地端線走本機推論。同一個模型家族,比較才有意義。
這是今天最重要的一段。
eval 分割。# bench/run.py
import json, time, statistics
def run(cases, guard):
results = []
for c in cases:
t0 = time.perf_counter()
verdict = guard.check(c)
dt = (time.perf_counter() - t0) * 1000
results.append({"id": c["id"], "expected": c["expected_action"],
"got": verdict.action, "latency_ms": dt})
lat = [r["latency_ms"] for r in results]
return results, {"p50": statistics.median(lat),
"p95": sorted(lat)[int(len(lat) * 0.95)]}
guard 是一個統一介面,雲端與地端各實作一次,評測腳本不變。完整版在 repo 的 bench/ 目錄。
所有程式碼、語料結構、評測腳本都會放在公開 repo,每天的文章對應一個 tag(day07、day08…),讀者可以 checkout 到任何一天的狀態。
【此處貼 repo 首頁與 tag 列表截圖】
| Day | 決定了什麼 |
|---|---|
| 1 | 護欄的範圍:OWASP 兩張表的主戰場 |
| 2 | safety 與 security 分開驗收 |
| 3 | 攔截點放閘道層,八條箭頭都要過 |
| 4 | 會動手的路徑一律 fail-closed |
| 5 | L1 → L2 → L3 分層,L3 流量壓在個位數 |
| 6 | tune / eval 分割,六類自製語料 |
| 7 | 兩條線同一個主模型家族、同一套量測腳本 |
Day 8 進入 Week 2:Model Armor 初探。Template 的結構、floor setting 是什麼、filter v3 與舊版的差異,以及為什麼 2026 年 11 月底之前你一定要處理版本升級。
更多 AI 資安筆記:aid3fend.com