iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0

1. 前言:做完才知道最難發現的坑看起來都像對的

從 Day 03 用 Terraform 建好環境開始,這個系列一路做了合成資料、星狀綱要、多觸點歸因、在 SQL 裡請 Gemini 診斷成效異常、讓 AI 看廣告圖、根據成效打素材草稿、會自己查資料庫的聊天助理、護欄、評測與成本儀表板,最後把助理部署上線,做到可以排程每天早上自己寫好日報再推進 Slack,整套系統呼叫模型與 BigQuery ML 建模型的費用,有逐次紀錄的部分加起來約新台幣 34 元。

今天回頭整理這 28 天踩過的坑,也照昨天的預告交代哪些設計後來證明是對的、哪些當初想得太簡單,整理完發現大部分的坑都是同一個樣子,結果看起來對數字也像真的,只有拿標準答案或後台資料去對才知道哪裡錯,會直接報錯的坑反而好處理。

先講一件會影響讀者的事,Google Cloud 已經公告 gemini-3.6-flash 在 2026 年 11 月 19 日停用,之後的請求會回 HTTP 404,這個系列從 Day 09 起有 12 個程式檔直接呼叫這個模型,11 月 19 日之後照著文章重做的讀者會在這些地方卡住,第 5 章整理怎麼一次找出來、該換成哪個模型、換的時候要注意什麼。

今日核心目標:

  1. 把 28 天的坑分成四類,每一類挑最有代表性的幾個,講清楚發生什麼事、怎麼發現、後來怎麼處理
  2. 整理後來證明是對的設計,讓讀者可以直接帶到自己的專案
  3. 交代 gemini-3.6-flash 停用的影響範圍與換模型時要注意的地方

2. 系統架構全景與設計理念

圖一:踩坑地圖,系統由下往上分成資料串接、分析、AI 與助理、雲端與成本四層,每張卡片是一個坑與發生的那一天,最右邊是後來補上的做法

整套系統由下往上是四層,坑也照這四層分布。

層 包含的篇章 代表性的坑
資料串接層 Day 04 到 Day 07 金流自訂欄位放中文會驗章失敗、手機開頭的 0 不見、GA4 金額存在整數欄位、UTM 只記在少數事件上
分析層 Day 08 到 Day 17 歸因規則一換功勞就換人、答案就放在分析資料旁邊、不分受眾直接比結論方向相反、K-means 每次分得不一樣
AI 與助理層 Day 18 到 Day 24 沒有工具就自己編數字、放手寫文案就冒出誇大用語、把個資交出去的是工具
雲端與成本層 Day 03、Day 10、Day 20、Day 26 預算警報在試用額度期間不會寄、只寫模型名稱就貴一成、模型不一定在當初以為的區域、助理被收掉就忘了前面的對話

回頭看這張表有一個共通點,這些坑幾乎都不會報錯,SQL 寫錯查詢當下就失敗,很快就會被修好,真正花時間的是查詢成功、結果也感覺很合理的那一種,越往上層越明顯,到了助理那一層模型回的是一段通順的中文,錯的時候也一樣通順,所以這個系列的檢查也一層一層往上加,從載入時的對帳到事先寫死的判準,一直到程式碼裡的護欄。


3. 核心技術深度拆解

3.1 行銷資料一接進來就有坑

Day 04 串綠界金流時,原本想把「加大 80x160 cm」這種尺寸文字放進自訂欄位,後來發現自訂欄位會被算進驗章,只要金流端對中文或半形空白做過任何轉換,整筆付款在感謝頁就會被判定失敗,而且極不容易被發現,最後改成只放尺寸的索引數字。

Day 06 把合成資料載入 BigQuery 時先用自動偵測綱要當對照,0949247891 這種手機號碼被猜成整數,開頭的 0 直接消失,照 GA4 格式產生的訪客 ID 被猜成浮點數,尾數被捨去,這些欄位看起來都有值,要到拿去比對顧客或串接時才會發現對不上,所以五張表最後都寫了明確的綱要。

Day 07 接 GA4 每日匯出時,Live Demo 站兩天 99 個事件就遇到四個沒預期到的狀況,金額 10 筆全部存在整數欄位、浮點數欄位是 0 筆,只讀浮點數欄位的話營收會悄悄歸零,沒有交易的事件 transaction_id 填的是字串 (not set) 而不是空值,99 個事件只有 2 個帶 UTM,要在同一個工作階段裡補齊來源,每日表也不是隔天一早就有,兩天都晚了一天以上才出現。

追蹤數字和後台對不上這件事,Day 05 刻意藏進合成資料當成考題,8 月 27 日網站追蹤到的購買掉到 0 筆,前七天平均是 43.9 筆,後台訂單卻照常有 37 筆,Day 09 請 Gemini 判讀時兩個模型都抓到這是追蹤碼失效而不是沒有生意,關鍵就是同時拿到了網站追蹤和後台訂單兩邊的數字,只看 GA4 的話這一天會被當成業績崩盤。

3.2 分析會洩題也會騙人

Day 08 用三種規則分配同一批訂單的功勞,google / cpc 用第一次接觸算分到 295 筆,用最後接觸算分到 670 筆,meta / paid_social 剛好反過來,第一次接觸 610 筆、最後接觸 385 筆,很多後台報表預設看最後點擊,看到搜尋廣告的成交多就把 Meta 的預算挪過去,在這份資料裡 Meta 是第一次接觸分到最多訂單的通路,歸因規則沒講清楚之前,任何一張通路報表都會帶著一個預設立場。

從 Day 11 起我一直對讀者說答案放在獨立的資料集 martech_gt,分析只讀 martech_dw,只要 SQL 裡沒有出現 martech_gt 就代表沒有偷看答案,期中回頭檢查時才發現 Day 07 建素材維度表時,把有沒有人物、按鈕位置、主色、文字多寡四個設計規格欄位一起帶進了 martech_dw,那正是 Day 16 要考 Gemini 的答案,等於考卷旁邊就放著解答,而檢查只認 martech_gt 這幾個字,所以完全沒有發現,Day 15 把這四欄搬進答案資料集,也在文章裡交代了這件事。

Day 17 把 AI 看圖讀出的特徵和轉換率放在一起比,不分受眾直接算時,按鈕放右下的轉換率倍數是 0.907,看起來放右下比較差,改成同通路、同受眾分層之後變成 1.133,方向整個相反,原因是按鈕放右下那一組的工作階段有七成七來自開發新客,沒有右下按鈕的那組只有五成八,開發新客的轉換率本來就比較低,分層後的 1.133 信賴區間是 0.94 到 1.37,也包含 1,兩個數字都不代表按鈕會影響轉換率,這裡要記住的是分不分層連方向都會不一樣,行銷人天天在比素材,混在一起比很可能做出完全相反的決定。

Day 11 的 K-means 分群每重跑一次分法就不一樣,發文前重跑時分法已經和前一天文章用的那一次不同,而那一次的模型已經被覆蓋找不回來,最後用 BigQuery 的時光回溯把當時的結果另存成一張正式表,從此多了一條規矩,會覆蓋共用物件的重建之前先另存副本,文章裡也老實寫了讀者重跑的結果可能和文章不同。

合成資料停在 2026 年 9 月 16 日,Day 22 示範「上週哪支廣告最貴」時真正的上週根本沒有資料,所以助理把資料最後一天的隔天 9 月 17 日當成今天,Day 27 的日報因此每天內容都一樣,Day 28 的流程再加上基準日參數 as_of,要示範某一天的狀況就重播那一天,才能拿 8 月 27 日追蹤碼失效的那一天來測警報。

3.3 AI 說得話看起來都像對的

Day 21 做了一個對照實驗,同一批六個問題分別在給工具與不給工具兩種情況下問 gemini-3.6-flash,不給工具時有幾題模型沒有說查不到,而是直接給出新台幣 125,000 元和 ROAS 3.5,另一題給了 1,250 與 980 筆這種看起來很具體的數字,資料庫裡根本沒有這些數字,更諷刺的是沒有工具的那一組用掉的思考 Token 反而比較多,3,364 對 907,等於多花錢編故事。

Day 18 讓 Gemini 根據成效打素材草稿時也是一樣,題目只寫盡量發揮創意、多強調機能的那一版,6 份草稿有 5 份用了極致、強效、黃金這類誇大用語,同一份題目加上四條規則之後是 0 份,模型照字面把多強調做到最好,規則寫進題目之後這些用語就沒有再出現。

Day 23 的實測裡模型本身比我想像中耐打,藏在活動備註裡的兩則注入指令模型自己都沒有照做,真正把五位顧客的姓名、email 和手機整份交出來的是沒有護欄的查詢工具,模型只是照著工具給的資料回答,所以個資的防線要放在程式碼裡,在進到模型之前就先遮掉,另外測不當內容的那一題 Safety Settings 四個類別一次都沒有擋下,它管的是另一種風險,也擋不住個資外洩。

替 AI 打分數也一樣,Day 24 用規則、兩個評分模型和人分別替 16 則回答打分數,有一則回答把錯的日期講得很肯定,規則和兩個評分模型都給了 1 分,我把它改成 0 分,因為講錯又講得很肯定比老實說查不到更糟,這種判斷要由人來下,再寫回評分標準讓評分模型照著做,這也是我認為最後一關一定要有人的原因。

3.4 雲端服務有自己的規則

Day 26 把助理部署到 Cloud Run 之後用瀏覽器實測,第一句答完 4 秒閒置的執行個體就被收掉,第二句由新的執行個體接手,前面的對話全部不見,另一次則是閒置 15 分鐘才被收掉,什麼時候收是個未知,用程式連續發問測試時沒有遇到,換成人打字的速度才遇到,後來改成記憶體只當暫存,找不到就從 BigQuery 的問答紀錄表接回同一個帳號 30 分鐘內的對話。

第二個常見的坑是模型不一定在以為的地方,gemini-3.5-flash-lite 沒有 us-central1 的版本,所以 Day 10 的快取只能建在 global,Day 20 的 gemini-3.1-pro-preview 在 us 位置叫不動,要改寫 global 端點的完整網址,Day 24 的評分服務也要走 global 端點才找得到模型,而 BigQuery 只寫模型名稱時會送到非 global 端點,單價比 global 貴一成,Day 10 發現之後 Day 14 起統一用非 global 單價,同一個系列因此出現兩套單價,Day 09 與 Day 13 的費用後來都更正過。

第三個坑藏在 Day 03,Terraform 建的預算警報原本設成扣掉抵免之後才計算金額,依官方文件的說法,用免費試用額度的讀者在抵免用完之前三段警報都不會寄,這是 10 月 3 日回頭檢查 Day 02、03 時找到的,沒有實際等到警報驗證,當天改成不扣抵免直接計算,也更新了文章。

3.5 後來證明是對的設計

  • 先把答案藏好再考:Day 05 在合成資料裡植入七個訊號、答案另外存放,之後的分析篇都有標準答案可以對,AI 說得再像也算得出對了幾成
  • 載入資料先對帳:Day 06 的本機與 BigQuery 兩邊對帳、Day 07 的列數對帳,資料一進倉儲就確認沒有少、型別沒有被改掉
  • 花錢前先印估價:每一篇的 run.sh 呼叫模型之前先印出最壞情況的金額,輸入 yes 才開始呼叫
  • 一張共用的用量表:Day 16 起拿得到 Token 用量的呼叫都寫進 ops_llm_usage,Day 25 的儀表板直接接這張表
  • 工具的 SQL 寫死只開放參數:Day 21 的模型能決定查什麼但改不了怎麼查,Day 23 的護欄才有地方放
  • 交出去之前讓另一個 AI 挑錯:這個系列的輔助 AI 是 Claude,每篇交出去之前另外開一個沒有參與撰寫的 Claude 只負責找錯,寫的一方讀到的是自己想表達的意思,另一方只看得到實際寫了什麼,前面提到的答案欄位破口就是 9/28 期中檢查時這樣抓到的

4. FinOps 成本防護實踐:三道防線體系

  1. 第一道防線:善用免費額度:BigQuery 的查詢與儲存、Cloud Run、Workflows、Secret Manager 這 30 天都靠每月免費額度,下表的費用來自呼叫模型,另外 Day 11、Day 12 建 BigQuery ML 模型沒有免費額度,各約 0.4 元也算在裡面
  2. 第二道防線:架構層被動成本防護:大量批次用 gemini-3.5-flash-lite、分類題用 thinking_budget 0 關掉思考、呼叫前先印估價、跑過的不重跑、助理服務每天有花費上限
  3. 第三道防線:Cloud Billing 預算警報:沿用 Day 03 的預算警報(新台幣帳戶 NT$ 300),並改成不扣抵免直接計算,依官方文件試用額度期間也會寄

圖二:Day 09 到 Day 28 每一篇的費用,四個階段用不同顏色區分,有逐次紀錄的部分合計約 34.4 元

階段 篇章 有紀錄的費用(新台幣) 花得最多的一篇
在 SQL 裡叫 AI Day 09 到 Day 15 約 8.7 元 Day 10 快取實測約 4.5 元
看圖與素材 Day 16 到 Day 20 約 16.9 元 Day 20 三個等級的模型評測 7.60 元
助理 Day 21 到 Day 24 約 5.8 元 Day 23 護欄測試 2.54 元
上線與自動化 Day 25 到 Day 28 約 3.0 元 Day 26 部署後實測 2.13 元
合計 約 34.4 元

Day 16 到 Day 24 用的是 Day 25 儀表板算出來的數字,和當天文章有 0.01 元的四捨五入差,另外有兩筆沒有逐次紀錄,Day 18 的一支 Veo 影片按秒計費,當天估 4 到 6 元,Day 24 的評分服務不回報 Token,估價上限 2.77 元,兩筆加上去約 38 到 43 元,實際金額以帳單為準。

回頭看這張表,費用高低和呼叫次數的關係不大,差別在給模型什麼和用哪個模型,Day 19 同一批比對給截圖花 3.56 元、給文字只要 1.96 元,Day 25 的報表裡 24 次 gemini-3.1-pro-preview 花的錢和走 global 端點的 85 次 gemini-3.6-flash 差不多,另一個要留意的是單價會變,gemini-3.6-flash 和替代它的 gemini-3.8-flash 現在都是到 2026 年底的導入期價格,2027 年起每百萬 Token 輸入與輸出都變成兩倍。


5. Cloud Shell 實戰演練:在 gemini-3.6-flash 停用前找出要換的地方

5.1 事前準備

  • 在 ~/ai-driven-martech-pipeline 執行 git pull
  • 官方的模型生命週期頁(Model versions and lifecycle)寫的是 gemini-3.6-flash 2026-07-21 發布、2026-11-19 停用,替代模型 gemini-3.8-flash,2026-09-02 發布、已經是正式版、還沒有公布停用日,官方頁列的支援區域有 global 與 us、eu 多區域,Google 的通知信另外也建議了 Gemini 3.5 Flash
  • 這兩個 Flash 都列在官方「可用期間較短」的那張表,新模型推出並公布停用日之後就開始倒數,官方承諾至少給 45 天移轉,gemini-3.5-flash-lite 則在「發布後至少可用 12 個月」的那張表,停用日最早是 2027-07-21
  • 3.8 Flash 到 2026-12-31 的單價和 3.6 Flash 相同(global 每百萬 Token 輸入 0.75、輸出 3.75 美元),2027-01-01 起變成 1.50 與 7.50

5.2 路線 A|一行指令找出所有呼叫的地方

cd ~/ai-driven-martech-pipeline && grep -rnE "(ENDPOINT|endpoint) *(=|=>) *'gemini-3\.6-flash'|MODEL *= *\"gemini-3\.6-flash\"|JUDGES\", \"gemini-3\.6-flash" --include=*.sql --include=*.py .

這行只找真的會呼叫模型的寫法,預期印出 14 行,分在 12 個檔案。

檔案 篇章 行數
diagnosis/diagnose.sql Day 09 1
acceptance/blind.sql Day 13 1
multimodal/describe.sql Day 14 1
structured/extract.sql Day 15 1
drafts/generate.sql Day 18 1
consistency/compare.sql Day 19 2
benchmark/bench.sql Day 20 2
agent/ask.py Day 21 1
agent/chat.py Day 22 1
agent/guard_test.py Day 23 1
evaluation/eval_run.py Day 24 1
agent/serve.py Day 26 起的助理服務,Day 27、28 的流程透過它呼叫 1

5.3 路線 B|分清楚四種出現的地方

直接搜尋 gemini-3.6-flash 會找到更多行,它們分成四種,處理方式不一樣。

種類 例子 停用後會怎樣 換模型時要做什麼
呼叫模型 5.2 找到的 14 行 回 HTTP 404,那一天的程式跑不下去 改成新模型
讀存好的結果 agent/tools.py 與 workflows/setup.sql 用 d.model = 'gemini-3.6-flash' 挑 Day 09 那一輪的判讀 判讀已經存在表裡,不受影響 用新模型重跑 Day 09 時才一起改
單價與估價 monitoring/price.sql、各篇的 cost.sql 與 report.sql、eval_run.py 的評分模型單價 不影響執行 加上新模型的單價
檢查與評分 benchmark/check.sql、benchmark/score.sql 寫死要比較的模型名稱 不影響執行 換模型重跑 Day 20 時一起改,否則檢查會對不上

看起來只要把 14 行換掉就好,仔細檢查換模型的流程之後又找到幾個會讓人卡住的地方,所以這一篇不叫讀者自己改,全系列完成之後我會在儲存庫一次換好、實跑確認再推上 GitHub。

  • 模型名稱寫死在程式裡:這是當初設計得太死的地方,Day 19、20、21、24 的 run.sh 為了確保實驗的題目與判準沒有在看完結果之後被改過,會檢查幾個檔案有沒有未 commit 的修改,有就直接停下,但模型名稱與端點也寫在這些檔案裡,讀者只是換個模型就會被擋下,.env.example 其實已經留了 GEMINI_MODEL_FLASH 卻沒有程式讀它,完賽後會改成從環境變數讀模型名稱,檢查也改成只提醒,並把當下的版本記進結果表
  • 單價表會被整張重建:Day 25 的 monitoring/run.sh 每次執行都會重建單價表,手動加進去的單價會被洗掉,而助理服務在執行個體第一次處理請求時讀單價,找不到剛好一列就不回答
  • 同名的遠端模型不會被覆蓋:Day 13 的 blind.sql 用 CREATE MODEL IF NOT EXISTS,Day 09 已經用 3.6 Flash 建過同名的遠端模型,只改 endpoint 不會生效
  • 模型名稱同時是紀錄的欄位值:SQL 那幾天把模型名稱同時寫成呼叫的 endpoint 和寫進紀錄的值,只改 endpoint 會讓紀錄記錯模型

5.4 驗證:確認自己的環境現在用的是哪個模型

bq query --use_legacy_sql=false "SELECT model, COUNT(*) AS calls, MAX(DATE(logged_at, 'Asia/Taipei')) AS last_day FROM martech_dw.ops_llm_usage GROUP BY model ORDER BY calls DESC"

這張表從 Day 16 起記錄每一次拿得到 Token 用量的呼叫,查詢在 BigQuery 每月免費額度內,看到 gemini-3.6-flash 的最後一天還在 11 月 19 日之後就代表還有地方沒有換到,沒有出現也不代表都換好了,Day 09 到 Day 15 不寫這張表,Day 21 到 Day 24 的程式只記成功的呼叫,所以還是要搭配 5.2 的指令一起看,5.2 的指令我在儲存庫上實際跑過,結果是 14 行、12 個檔案。

5.5 儲存庫更新之後

  • 先執行 git status,自己改過的檔案用 git stash 收起來或用 git checkout -- 檔案名稱 還原,再執行 git pull,否則 git 會因為本機的修改會被覆蓋而拒絕更新
  • 執行 bash monitoring/run.sh 重建單價表,讓新模型的單價進到表裡,再執行 bash scripts/deploy_assistant.sh 重新部署助理服務,Day 27 的日報流程與 Day 28 的 Slack 通知都是透過助理服務呼叫模型,不用另外改
  • 再跑一次 5.2 的指令,沒有任何輸出就代表已經換完

6. 工程實務避坑指南

如果要重新開始,以下這些項目會在事前就做好:

  1. 網站追蹤要和後台訂單對帳:追蹤到的購買掉到 0 不一定是沒生意,先對後台訂單再下結論,只看 GA4 會把追蹤碼失效誤判成業績崩盤
  2. 歸因規則先講清楚再看通路報表:同一批訂單換一種歸因規則,功勞就換一個通路拿,預設看最後點擊會低估帶顧客第一次進站的通路
  3. 行銷資料的型別要自己定:手機、訪客 ID 交給自動偵測會被猜成數字,GA4 的金額可能存在整數欄位、電商欄位沒有值時填的是字串 (not set),載入與攤平時都不要假設型別
  4. UTM 要補到整個工作階段:來源只記在少數事件上,在同一個工作階段裡補齊,來源、媒介、活動三個欄位要從同一個事件取值
  5. 比較素材之前先分層:不同受眾混在一起比,倍數的方向可能整個相反
  6. AI 要先有工具,個資擋在程式碼裡:沒有工具時模型會編出很具體的數字,顧客的姓名、email、手機在進到模型之前就要遮掉,品牌規則與禁用詞也要寫進題目

7. 總結與明日預告

這 28 天最大的收穫不是哪一個服務的用法,而是學會不相信看起來對的結果,行銷資料一接進來就可能少了開頭的 0、營收悄悄歸零,歸因規則一換功勞就換人,混在一起比的素材結論會整個相反,AI 會編出很具體的數字會照字面把誇大做到最好,雲端服務會照自己的規則收掉執行個體、把請求送到比較貴的端點,這些坑都不會報錯,能抓到它們靠的是和後台的對帳、事先藏好的標準答案還有先寫死的判準與程式碼裡的護欄。

也再提醒一次,gemini-3.6-flash 在 2026 年 11 月 19 日停用,照著這個系列重做的讀者可以先用第 5 章的指令找出自己環境裡用到的地方,換成 gemini-3.8-flash 的程式我會在全系列完成後一次更新到儲存庫並實跑確認。

明日預告:Day 30《整套系統開源交付之後,MarTech 還能怎麼走?》,最後一天交代整個儲存庫怎麼用、當初規劃的樣子和最後完成的樣子差在哪裡,以及這套系統接上真實資料之後還能往哪裡走。


上一篇
Day 28 | 廣告出狀況時,讓 Slack 機器人第一時間通知你
下一篇
Day 30 | 整套系統開源交付之後,MarTech 還能怎麼走?
系列文
AI-Driven MarTech:用 Google Cloud + Vertex AI 打造全自動廣告歸因與多模態素材分析系統 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言