系列:30 天用 Google AI 打造臺灣防災速報 App(Day 26/30)
大雨天,同一個縣市常常同時有好幾則示警。Day 5 看過 8 月 23 日大雨那天的資料,水庫放流、淹水感測、土石流等水利示警同時出現。
很自然會想到:讓 Gemini 當代理,看到同一區的豪雨特報、水庫放流、道路封閉,就合成一則「整合通報」推出去。我們最後沒有這樣做。今天寫為什麼、實際用了什麼做法,以及一個寫好查詢、但依使用條款沒有接上的雨量預報工具。
把三則示警合成一則,要先回答三件事:
第 2、3 點是錯了代價最高的地方。推播出去的一句「屏東山區豪雨已觸發水庫放流」,看起來像官方判斷,但沒有任何機關這樣說過;萬一推論錯了,使用者也無從查證。
還有時間的問題。要合成,就得等相關的示警都到齊;第一則重大示警反而晚送出。速報晚幾分鐘,比多收一則通知更糟。
Day 25 已經用兩條確定性的規則處理「通知太多」:
通知的內文是單一則示警的 AI 摘要(Day 13);沒有摘要時,中文用該則示警的標題,外語用等級名稱。不會把幾則合在一起。所以從判斷要不要推、到推出去的文字,沒有任何一步需要模型決定「這幾則示警有關係」。
全貌還是有人需要,例如「屏東現在有哪些警報?」。這個情境交給問答:Gemini 呼叫 get_active_alerts 查生效中的示警,再整理成一段回答(工具呼叫的做法見 Day 10)。
和推播比,這裡讓模型整理多則示警比較安全:是使用者主動問的,回答下面會列出資料來源,可以前往來源網站核對。
還有一條守門規則寫在程式裡:模型只查了示警、而且每一次都查到 0 筆時,回答不交給模型,由程式直接給固定的一句。
# answer_with_tools() 的結尾(簡化)
if totals and set(totals) == {"get_active_alerts"} \
and all(t == 0 for t in totals["get_active_alerts"]):
answer = NO_ALERTS[lang]
NO_ALERTS 的中文是:「目前查不到相關的生效示警。這不代表當地一定沒有示警,請以中央氣象署、地方政府等官方發布為準。」查無資料不等於沒有危險,這句話不讓模型自由發揮,由程式固定。
問答的兩個工具查的是已發布的示警與地震報告,沒有未來雨量。大雨天還缺一塊:雨還會下多久?Google DeepMind 的 WeatherNext 3 天氣預報模型可以補這一塊,所以我先寫好查詢、量過成本,再決定要不要接進問答。
資料表給的是 64 組預報的統計值:p50 是中位數,p90 代表約九成的預報不高於這個雨量,可以當偏保守的估計。評估時看的是 p90。
設計上不在問答時查 BigQuery,而是由 Cloud Scheduler 每小時觸發一支函式:先取臺灣範圍內 24 小時內最新的初始化時刻,再用一次查詢算出 22 個縣市未來 12 小時的逐時雨量,寫進 Firestore 的 rain_forecast/{縣市代碼},問答工具只讀 Firestore。這支排程函式沒有部署,下面的數字是實際跑查詢量到的。縣市範圍用內政部國土測繪中心的縣市界線,以參數傳進查詢,每縣市取範圍內格點的最大值。
WITH county AS (
SELECT c.iso, ST_GEOGFROMTEXT(c.wkt, make_valid => TRUE) AS geog
FROM UNNEST(@counties) AS c)
SELECT county.iso AS county, f.time, f.hours,
ROUND(MAX(f.total_precipitation_1hr_p50) * 1000, 1) AS rain_mm_p50,
ROUND(MAX(f.total_precipitation_1hr_p90) * 1000, 1) AS rain_mm_p90
FROM `ai-watchtower-tw.weathernext_3.weathernext_3_0_0_0p1deg` AS t
JOIN county ON ST_INTERSECTS(t.geography_polygon, county.geog)
CROSS JOIN UNNEST(t.forecast) AS f
WHERE t.init_time = @init_time
AND ST_INTERSECTS(t.geography_polygon, ST_GEOGFROMTEXT(@taiwan))
AND f.time >= @start AND f.time < TIMESTAMP_ADD(@start, INTERVAL @hours HOUR)
GROUP BY county, f.time, f.hours
三個要注意的地方:
ST_INTERSECTS 後降到 34 MB;22 縣市的雨量查詢 dry run 上限 390 GB,實際計費 45 MB。也不能設 maximum_bytes_billed,它比對的是 dry run 上限,設了就被擋。照這樣算,一輪約 80 MB,一天約 1.9 GB。設計上由函式在每次查詢後讀取計費量,單次超過 1 GB 或當天累計超過 5 GB 就停止後續查詢(已執行的那次照樣計費)。
WeatherNext 的資料分兩種授權:描述一小時以前的資料是 CC BY 4.0;描述一小時內與未來的資料,適用它的即時資料使用條款。未來 12 小時的雨量全屬後者。條款有兩項使用限制,加上一項產品風險:
前兩項就讓公開的防災 App 過不了。條款允許任何內部用途,所以評估與測試都合規;對外的 App 不顯示任何 WeatherNext 資料。WeatherNext 團隊回覆不提供法律意見、建議自行請法律顧問判斷;沒有 Google 的書面同意,就不接。
資料聲明(依條款第 4 節):本節的查詢成本數字來自 BigQuery 上的 WeatherNext 3 資料集。© 2024-6 Google LLC, whose machine learning models were used to create the experimental data made available under the following licence terms https://storage.googleapis.com/weathernext-public/terms-of-use.pdf. This data is intended for experimental modelling only and is not intended, validated, or approved for real world use.
人機協作研究近年有一條主張:AI 輔助決策不該停在「給建議、人接受或拒絕」(Recommendation),而要讓人與 AI 把各自理由攤開、針對分歧討論、再更新判斷(Deliberation)。同組的 siliconcrystal 在系列 Day 06、07 整理過這條脈絡(引自 CHI 2025 的 Human-AI Deliberation 研究),結論之一是:討論本身有認知成本,只該在高風險、高不確定的情境觸發。
速報把這件事逼到極端:警報湧入時,使用者沒有時間跟 AI 討論。所以這個 App 依風險分三層:
討論發生在設計期,發生在我和規則表之間;到了使用者面前,只剩下照規則跑出來的結果。
明天 Day 27:AI 輸出怎麼評估。