直訂策略的邏輯成立,但邏輯成立不等於迴路閉合。OTA 平台的抽成遠比外界想像的高,只要有一部分訂單走自家官網,財務結構就不同。但直訂要能持續運作,業主必須每週知道:哪些廣告活動帶來真實詢問、哪些頁面讓訪客看了就走、這週的流量下滑是季節因素還是廣告出了問題。這些問題需要有人定期去回答。
四家恆春民宿。每家都設好了轉換追蹤——電話點擊、LINE 聯絡、訂房表單送出全部進了 GA4,也在反推 Google Ads 的出價邏輯。業主沒有在看任何東西。訂房和預算決策還是靠感覺在跑:上個連假有沒有忙、哪個親戚說最近觀光客在跑。
問題不在儀錶板難不難用。GA4 需要你主動去開、找到對的報告、記得這週要看什麼、再去解讀數字。對一個早上忙早餐換房、下午還要回訊息的業主,這個流程沒有外力推一把就不會發生。
Push 打敗 Pull
做出來的是一套每週一 09:37(台北時間)觸發的 GitHub Action。它同時向 GA4 Data API 發起 26+ 個查詢、拉 Google Search Console 的曝光和點擊資料、透過 GA4 連結抓 Google Ads 各廣告活動的花費和 CPC,接著把合併後的資料集送進三階段 pipeline 處理。輸出兩路:一封 HTML 週報直接寄進業主信箱,一份 Markdown commit 進 repo 當審計記錄。業主不需要登入任何地方,我不需要每週手動跑任何東西。
三個階段是刻意分開的。Stage A(fetch)只做 API 呼叫和正規化,把結果寫成結構化 JSON,完全不碰推論。Stage B(analyzer)讀取那份 JSON,呼叫 LLM 產出 Zod schema 驗證的洞察陣列——每條洞察包含信心等級、引用數字的來源路徑、是否可行動的旗標。Stage C(render)從驗證過的洞察陣列生成中文週報。
這樣分的原因:報告說錯了,要問的問題有三個,而且彼此獨立。資料拉錯了?模型的推論錯了?還是文字生成出了問題?三個階段各自留下一份 artifact,三個問題才能分別追查。合在一起,一份爛報告就只是一份爛報告,找不到根因。
GA4 的 Measurement ID 是公開的
GA4 的 Measurement ID 放在每個頁面的原始碼裡,任何人都可以從任何 hostname 向這個 property 送 hit。localhost 的開發測試、Railway 的預覽部署、甚至惡意的模擬流量,全部都會進來。不加 hostname filter,每個 GA4 查詢都含這些雜訊。本機測試觸發的轉換事件會直接污染核心 KPI,預覽部署的流量會灌高 session 計數。
解法是在每個 GA4 查詢裡加上 hostname 的 AND 條件,把流量過濾到只留正式網域。實作是 query builder 裡的 8 行。
這類問題會靜默累積。報告看起來合理,數字方向正確但系統性地偏高,不會觸發明顯的警報。要發現它,需要知道要去找它。
算術不屬於 LLM
Stage A 在資料送進模型前,把所有的 week-over-week、month-over-month、year-over-year 差值都在程式裡算好。模型收到的是 { current: 142, wowDelta: -18 },不是兩個獨立數字。
這不主要是為了省 token。是為了縮小模型的推論範圍。給預算好的差值,模型的任務是解讀「發生了什麼變化」;給原始數字,模型要先建構比較、再解讀結果,兩個步驟各自可能出錯,而且錯誤會疊加。預算差值也幫助隔離失誤模式:報告對某個趨勢說錯了,算術不在嫌疑名單上。
樣本量限制放在資料裡,不放在 Prompt 指令裡
規則可以寫進 system prompt:「這個轉換事件本週不到 10 次,不要下結論。」問題是,當一個小樣本的數字帶著一個看起來有趨勢的方向進來,模型的預設行為是找解釋,而不是先核查樣本量。
Stage A 的做法:把 "sample_status": "low_sample" 直接 stamp 進每一條低於門檻的資料切片,讓它跟著數字一起進模型。模型看到的是一個結構化欄位,而不是一條它可能遵守也可能略過的文字規則。強制執行,不是寄望它記得。
模型知道數字,但不知道台灣
早期的輸出:「本週流量較上週下滑 12%。」技術上正確,沒有操作意義。
更糟的是一份農曆新年週報說得很篤定:有機搜尋流量下滑,是 SEO 表現衰退的跡象。農曆新年期間台灣幾乎所有網站的有機搜尋流量都會下滑——這是全國性的行為現象,不是內容品質訊號。模型沒有這個背景,用手上最合理的商業解釋補上了空白。
system prompt 現在是三層。第一層 engine-rules.md:通用規則,反幻覺、樣本量門檻、禁止術語裝飾。第二層 lodging-interpretation.md:適用所有住宿業者的領域知識,例如 OTA referral 流量的解讀方式、Smart Bidding 調整出價後需要 11 天觀察期才能看到效果、下游房型頁面的停留時間長不等於有轉換潛力。第三層 sites/<site>/business-context.md:這家民宿的專屬事實,追蹤哪些轉換事件、哪些代表認真詢問、哪些只是點一點、這個地區的旺淡季怎麼分、廣告帳號結構是什麼。
住宿業解讀知識獨立一層,是因為四個站不應該各自維護一份相同知識的副本、然後各自慢慢漂移出同步。引擎規則幾乎不動。住宿解讀有新的失誤教訓就更新,四個站立刻共享那份學習。業務脈絡隨業主的運營決策更新,和其他站無關。
多一個站,只需要一份設定檔
新站點上線:複製一個設定資料夾,更新 business-context.md(GA4 property ID、轉換事件清單、KPI 優先順序),放好憑證。引擎不動。現在四個站。第五個的邊際工程成本是一份設定檔。
這個數字的重要性不在工程效率,在服務能不能成立。如果每個新站點都要額外的工程投入,這套分析服務在恆春民宿這個規模根本撐不住。業主在評估廣告預算時,另一端比的是 OTA 平台遠高於多數人預期的抽成比例。LLM 分析層讓這件事的成本低到可以作為服務提供。沒有它,不是存在一個比較便宜的替代方案——是根本不存在。
Booking tracker 整合(從 Supabase 拉已確認訂單數,對比 GA4 的點擊代理指標)目前只在一個站上線,另外三個站還只有點擊訊號。分析模型偶爾在我想要清楚方向性判斷的地方過度保守。報告還不夠完美。但那個缺了太久的迴路,現在每週一早上都在閉合,用中文,不需要任何人登入任何地方。
讓業主根據報告做出第一個真實的運營決策,花的時間比把整套系統寫完還要長。