[CODE]

多站點 GA4 週報自動化:讓 AI 每週幫我看數據

建了一個多站點引擎,每週一自動拉 GA4 資料、餵給 Claude 分析、把 Markdown 報告 commit 到 repo。每個站點有自己的商業背景設定,分析結果不通用。

2 min read
ga4 claude typescript automation

我的客戶大多是台灣的傳統小型民宿,家庭經營,沒有 IT 人員,沒有數據分析師。他們在爭同一塊旅遊訂房市場,但這場競爭越來越難打——數位化程度低的業者,在搜尋排名、廣告投放、客戶分析這些地方都在被對手拉開距離。

問題不是他們不重視數字,而是 GA4 這個工具對他們來說門檻太高。「工作階段」、「轉換」、「頻道」這些詞對沒有數位背景的人來說就是外語。所以每次我幫客戶裝好 GA4、設定好轉換事件、接好 Google Ads,六週後回去問:「你有去看數據嗎?」——答案幾乎都是沒有。

數據在那裡,但沒有被用到。

我需要給他們一個不一樣的介面。

這套系統在做什麼

每週一早上,自動化引擎會替每家民宿拉 GA4 資料,送給 Claude 分析,產出一份用平實語言寫成的 Markdown 週報。報告自動進 repo,不需要客戶登入任何工具。他們不需要懂跳出率是什麼,他們收到的是圍繞他們真正在乎的問題所寫的摘要:這週有多少人到訂房頁面?清明假期有沒有影響流量?Google Ads 的錢有沒有在做事?

改變的不只是流程,而是習慣。以前,看數據是一件要主動去做的事,而且大多數人不做。現在,每週一的數據回顧變成了自動發生在他們身上的事。

引擎架構:cometrue-bnb_analysis_agent

這個引擎放在一個叫 cometrue-bnb_analysis_agent 的 repo 裡。名字是歷史遺跡,一開始只有一個站點,後來我把它通用化了。結構很單純:

sites/
  cometrue-bnb/
    business-context.md
    .env
  other-site/
    business-context.md
    .env
out/
  cometrue-bnb/
    data.json
    insights.json

每個站點資料夾放兩樣東西:一個 business-context.md,描述這個站點是什麼(GA4 property ID、轉換事件、KPI、季節特性、業主最想知道的問題);還有一個 .env 放 API 憑證。

引擎分三個階段:fetch → analyze → render

Fetch:拉資料

Fetch 階段呼叫 GA4 Data API v1alpha,有接 Search Console 和 Ads 的也一起拉。結果存到 out/<site>/data.json。這部分沒有特別神奇的地方,但關鍵是我不會把 GA4 的原始 JSON 直接丟給 Claude。我會先把資料正規化,包含 WoW(週同比)和 MoM(月同比)的差值,已經算好了才進下一階段。

interface WeeklyMetrics {
  sessionCount: { current: number; wowDelta: number; momDelta: number };
  conversionRate: { current: number; wowDelta: number; momDelta: number };
  topChannels: Array<{ channel: string; sessions: number; conversions: number }>;
  // ...
}

邏輯是:Claude 不應該幫我做加減法,它應該做的是解讀趨勢。如果我傳給它的是 { current: 142, wowDelta: -18 } 而不是兩個獨立數字,prompt 更短,模型可以把 token 花在推理上,而不是算數。

Analyze:讓 Claude 看數據

這裡才是 Claude 進場的地方。Analyze 階段會讀取該站點的 business-context.md,附上正規化後的 JSON,然後送進 API。

Prompt 的結構影響很大。一開始我拿到的輸出都是「流量較上週下降 12%」這種任何人看數字都能說的話,沒有用。我真正想要的是:「這週的下滑集中在自然搜尋,剛好是清明連假,付費流量沒有異常,所以不是廣告的問題。」

要到達這個程度,我必須:

  • 把台灣的節假日行事曆放進 system prompt
  • 當轉換事件數量低於門檻時,加入樣本量警告
  • 告訴 Claude 這個站點的轉換事件是什麼、對這門生意代表什麼意義
const systemPrompt = `
You are analyzing weekly performance for ${site.name}.
Business context: ${businessContext}

Rules:
- If conversion event count < 30, note sample size before drawing conclusions
- Taiwan public holidays this week: ${holidaysThisWeek.join(', ') || 'none'}
- Do not attribute channel changes to seasonality without checking holiday context first
- WoW and MoM deltas are pre-computed — use them directly
`;

假日感知那條是在某次報告信心滿滿地說「SEO 表現疲弱」之後加的,那週其實是農曆新年。從那之後我就開始把 Claude 當成需要背景資訊才能做好工作的初階分析師,而不是什麼都懂的神諭——跟你真的去雇一個分析師沒什麼兩樣。

business-context.md 是每個客戶的獨特知識存放的地方。有一家民宿最在乎訂房表單的送出;另一家最在乎電話點擊次數。有一家客源主要來自 Instagram 的熟客,另一家幾乎全靠 Google 搜尋。這些差異在解讀同樣的數字時至關重要,不能用一套通用邏輯帶過。

Render 與自動 Commit

Render 階段把 insights.json 格式化成 Markdown,存到 sites/<site>/reports/YYYY-Www.md。然後 GitHub Action 把它 commit 進去。

- name: Commit reports
  run: |
    git config user.email "actions@github.com"
    git config user.name "GA4 Bot"
    git add sites/*/reports/
    git diff --staged --quiet || git commit -m "chore(reports): weekly GA4 analysis $(date +%Y-W%V)"
    git push

我喜歡把報告放在 repo 裡而不是寄 email 或推 Slack。這樣歷史記錄就是 diff,你可以看到 AI 每週說了什麼,如果某週的報告寫得怪怪的,還可以回頭看當週的輸入資料。

不過對某些客戶來說,下一步是把 Markdown 轉成 LINE 通知或微信訊息——讓他們真正「看到」報告,而不是先解釋什麼是 GitHub repository。

新增站點有多快

我最滿意的地方是上線新站點的時間。要加一個新站點,就是複製一個現有的站點資料夾,把 business-context.md 改成新的 property ID 和 KPI,放進 .env,大概五分鐘搞定。引擎本身完全不用動。

這在業務上有實際意義:我不只是在幫自己建工具,我是在建立一個讓我可以多接一個客戶、卻不用等比例增加自己工作量的基礎設施。商業背景檔案是整個「新增客戶」流程的成本邊界。

目前狀況

這套東西跑了幾個月了。報告不是每次都完美,有時候 Claude 會在我想要明確結論的地方給一堆「可能」「或許」,格式偶爾也會再調整一下。但核心的事情做到了:原本被忽視的數據,現在以一個對業主有意義的格式被看見了。

這是一個跟工程問題不同性質的問題,某種程度上更難解。工程面花了幾天。讓一個民宿業主信任一份週報、進而根據它做決策——那需要更長的時間。