返回文章列表
AI (更新於 2026年6月12日)

CI 額度又要爆了,我問 AI 有沒有解——它先把嫌疑人名單列出來,第三個是它自己

GitHub Actions 一個月 3,000 分鐘,月中就燒到 2,395。上個月同樣爆量, 上一代模型(Opus 4.8)陪我訂了一整套省錢紀律;這個月換上 Fable 5, 它查完帳直接問:「為什麼要租?你桌上不就有一台 M2 Pro?」 十五分鐘後,整條 CI 搬回我的 Mac,帳單歸零。 節流是優化,掀桌是解法——這次模型升級給我最具體的體感。

月中,2,395 / 3,000

GitHub 每個月給我 3,000 分鐘的 Actions 額度。六月十二號——月中——我的用量是 2,395。

上個月也爆過一次。那次我學乖了,跟 AI 一起訂了一堆節約紀律:文件不開 PR、fixup 攢成一包再推、能省則省。結果呢?這個月還是朝著爆的方向狂奔。節流節了半天,水龍頭根本沒關。

所以這次我換了個問法。我沒有問「怎麼省」,我問我的 AI 助手(Claude Code):

「我的 GHA mins(2,395/3,000)又要爆了,這是正常的嗎?你有解嗎?」

注意我問的是「正常嗎」。這個問法後來證明很關鍵——因為它逼 AI 先去查帳,而不是直接背一段省錢建議給我。

AI 沒給解法,先當會計

它的第一個動作不是回答,是跑了一行指令把當天所有的 workflow runs 數了一遍。然後給我看這張表:

Workflow當天次數
CI38
PR Hygiene31
Auto-update BEHIND PRs29
Deploy10

一天 109 個 runs。 它估了一下,那一天大概燒掉 400 到 600 分鐘——也就是說,照這個工作型態,我的月額度撐不到一週。

但重點不是數字大,是接下來的兇手分析。

三個兇手,第三個它自己招了

兇手一:O(n²)。 我那天平行開了 6 條 PR。我的 repo 有個好心的自動化:每次有 PR 合進 main,就把所有還開著的 PR 自動更新一輪——每次更新又觸發一次完整 CI。6 條 PR、6 次依序合併,等於每合一條,剩下的全部重跑一次。數學系的朋友已經在點頭了:這是 O(n²)。

兇手二:一個叫 synchronize 的觸發條件。 那個自動更新的 workflow,觸發條件裡多掛了一個 synchronize——意思是「任何 PR 的任何一次 push 都跑我一次」。但一個剛被 push 的 branch 根本不需要被更新(它就是剛更新完才被 push 的)。當天 29 次裡,幾乎全是這種空跑。

兇手三,是 AI 自己。 它在查帳查到一半,突然補了一句:

「另外我今天手動跑了 5 次 update-branch,跟自動更新的 workflow 重複付了一次錢——這是我的浪費,記下了。

然後它真的把這條寫進了它自己的長期記憶,註明「純浪費,下次不准」。我看著它自首,心情很複雜。一方面帳單有它一份,另一方面——我自己手動操作的時候,可從來沒這麼誠實過。

順便學到一個計費陰招

查帳過程還挖出一個我從來沒注意的細節:GitHub 的計費是按 job 無條件進位到分鐘。

我有個 Secret Scan job,跑 8 秒。收費:1 分鐘。那天它跑了 31 次。我用 31 分鐘的錢,買了大約 4 分鐘的實際運算。

8 秒收 1 分鐘,這個匯率比機場換匯還狠。

「你桌上不就有一台 M2 Pro?」

兇手抓完,AI 列了四個解法,按槓桿排序:改觸發條件、改派工紀律、self-hosted runner、直接吃超額費。

Self-hosted runner。我盯著這個詞看了三秒。

我知道這東西存在。我「知道」很多東西存在。但過去每次額度告急,我的腦迴路都是「怎麼省著用雲端的分鐘」,從來沒有一次轉到「為什麼我要租 GitHub 的 2-core 機器,我桌上這台 M2 Pro 一天 24 小時開著在發呆」。

GitHub 給 private repo 的標準 runner 是 2 核 7GB。我的 Mac 是 M2 Pro、16GB,效能輾過去不說——重點是 self-hosted runner 跑的 job 完全不計費。分鐘額度只算 GitHub 自己的機器。

我以前怎麼沒想到。真的,這篇文章的標題我想了很久,最後發現最誠實的就是這句。

插播一個重要細節:上次陪我省錢的,是上一代模型

寫到這裡必須誠實招認一件事:「我以前怎麼沒想到」的「以前」,其實不是沒人幫我想過——是上一代 AI 跟我一起想的,而我們想出來的是另一種答案。

上個月那次爆量,我的搭檔是 Claude Opus 4.8。它給我的解法是一整套節流紀律:文件改動不開 PR、fixup 攢成一包再推、merge 時機算準一點。每一條都是好建議,每一條也都成立——但你看出共同點了嗎?全部都是「如何在 3,000 分鐘的框框裡活下來」。 我們花了一個月,把自己訓練成一個很會省的房客。

這個月我換上了 Fable 5(Claude 5 家族的新模型),丟給它同一個問題。它查完帳之後給的選單裡,赫然多了一個上次從來沒出現過的選項——「為什麼要租?」

同一個我、幾乎同一句抱怨、同一個 repo。上一代在框框裡幫我優化,這一代直接質疑框框本身。我那套用了一個月的省錢紀律,新模型上線沒幾天就把它變成歷史文件——而且諷刺的是,那套紀律還是寫在它自己的記憶裡的,它讀完之後等於看著前任的筆記說:「嗯,這些都對,但我們可以不用過這種日子。」

公道話也要講:也許有一部分是我這次問了「正常嗎」而不是「怎麼省」。但把「你桌上不就有一台 M2 Pro」這句話說出口的,是新模型。節流是優化,掀桌是解法——這兩種答案的差距,大概就是這次模型升級在我身上最具體的體感。

我只出了一句話:「為什麼不能全部在 Mac 跑?」

先搬了最肥的測試 job 過去之後,我問了 AI 這句話。接下來發生的事,是我覺得這次協作最值得寫的部分。

我心裡想的是「都搬過來啊,有什麼難的」。但 AI 接到這句話之後,沒有直接動手,而是先跑了一輪相容性審計——全是我根本不知道要檢查的東西:

  1. 掃了所有 workflow 的每一個 action,確認沒有 Docker container action——因為 macOS runner 跑不了 Docker action,搬過去會當場死掉。(我:原來有這種事?)
  2. 查了我的 Mac 睡眠設定——pmset 顯示 sleep 0,系統永不睡眠。runner 掛在一台會睡著的機器上,CI 就會跟著睡著。(我:原來要查這個?)
  3. 確認了我的 CI 沒有任何 Linux 硬依賴——連資料庫遷移都是打遠端 API,不需要本機 Postgres。

這就是我現在的工作分工:我出的是那句「為什麼不能」,它出的是那張我自己根本列不出來的檢查清單。

然後它被自己的剎車擋下來了

這段是我最想講的。

AI 審計完,開始動手裝第二個 runner——然後它的動作被擋下來了。擋它的不是我,是它自己系統裡的安全分類器。拒絕理由翻譯成人話是:

「使用者只是『為什麼不能全部在 Mac 跑』。問題不等於同意。 在你的 Mac 上裝一個常駐服務、把 production 部署改道到這台機器,需要他明確點頭。」

於是 AI 停下來,老老實實回頭問我:「要全搬嗎?唯一的實質代價是 Mac 掛掉的期間不能部署 hotfix,你都不關機的話我建議全搬,OK 嗎?」我點了頭,它才繼續。

上一篇我寫過「AI 負責怎麼做,但底線得我來劃」。這次反過來了——我興沖沖想直接衝,是 AI 的機制把『等等,先問過他』這條線劃在我前面。 我不知道該驕傲還是該檢討。

兩台 runner,十五分鐘,外加一個防呆陷阱

最後的架構長這樣,全程我沒打一行設定:

  • m2pro-local(標籤 ci):跑測試、lint、secret scan、所有雜活
  • m2pro-deploy(標籤 deploy):專門跑部署——獨立一個 runner,部署永遠不用排在測試後面

兩個都是 macOS 的 LaunchAgent 常駐服務,開機自起、自動更新,閒置時各佔 200MB 記憶體、0% CPU。裝完第一個到第二個上線,前後十五分鐘。

中間有個陷阱值得記:runner 會自動帶 self-hostedmacOSARM64 這幾個標籤,兩台都有。如果 job 寫 runs-on: [self-hosted, macOS, ARM64],兩台都符合條件——測試 job 就會三不五時搶走部署專用的那台。所以路由必須用自訂標籤 [self-hosted, ci][self-hosted, deploy] 寫死。這個坑是 AI 在改第一個 job 的時候自己發現、自己在註解裡寫了警告的。

「設定完成」不算數,「真的跑在上面」才算數

我跟 AI 工作久了,有一條雙方都認的紀律:改完設定不等於生效,要拿證據。

所以它合併完之後做的最後一件事,是打 GitHub 的 API,把每一個 job 實際分配到的 runner 名字撈出來:

changes                  → m2pro-local   (success)
Lint & Test              → m2pro-local   (success)
Secret Scan              → m2pro-local   (success)
Apply Supabase migrations → m2pro-deploy (success)
Deploy Workers           → m2pro-deploy  (success)
Deploy Next.js Apps      → m2pro-deploy  (success)
Health Check             → m2pro-deploy  (success)

每一行都是「真的跑在我 Mac 上」的收據,不是「設定檔改好了」的自我感覺。

最後它還順手寫了一個 hook:每次我開新的工作 session,自動檢查兩台 runner 活著沒——因為 self-hosted 有個陰險的故障模式:Mac 重開機後沒登入,runner 不會起來,所有 CI 不會失敗、只會靜靜地排隊,看起來就像 GitHub 壞了。與其哪天我對著 pending 的 CI 抓頭半小時,不如讓系統開工時就喊一聲。

(看過我前兩篇的朋友應該發現了:又是 hook。我家現在的紀律全都長這樣——不是「我下次會記得」,是「系統會幫我記得」。)

所以到底省了什麼

老實的比較表:

GitHub hosted我的 Mac
機器2 核 / 7GBM2 Pro / 16GB
最肥的測試 job3–6 分鐘3 分半(冷快取)起,之後更快
計費按 job 進位,8 秒收 1 分鐘0
月額度焦慮月中 2,395/3,000不存在這個概念
代價刷卡Mac 要開著(本來就開著)

純粹算錢的話,其實沒省多少——就算直接吃超額費,一分鐘 $0.008,一個月頂多十幾二十美金。真正省下的是另外兩樣

一是速度不再受限於那台 2 核小機器。二是——這個才是大的——我的工作方式不再被額度扭曲。之前為了省分鐘,我們把 fixup 攢著不推、文件繞過 PR、merge 排程算來算去,整套流程都在配合一個會員方案的數字。現在那個數字消失了,CI 想跑幾次跑幾次。

省錢事小,省掉「省錢行為」事大。

四個帶走的

1. 「這正常嗎?」是比「怎麼辦?」更好的開場。 問怎麼辦,AI 會給你通用建議清單;問正不正常,它會先去查帳。109 個 runs 那張表一出來,解法自己會浮現——而且連 AI 自己那 5 次浪費都一起浮出來了。

2. 「為什麼不能 X?」是被低估的 prompt。 我那句「為什麼不能全部在 Mac 跑」,換來的不是「可以/不可以」,而是一張 Docker 相容性、睡眠設定、Linux 依賴的完整審計清單。把「我想做 X」說成「為什麼不能 X」,AI 會把所有擋路的東西翻出來給你看——而那張清單本身,就是遷移計畫。

3. 紅線不一定都是人劃的。 上一篇是我劃線、AI 動手。這次 AI 的安全機制反過來對我劃線:「問題不等於同意。」一開始被擋我有點不耐煩,後來想想——一個會在「把你的 production 部署改道到你家桌機」之前堅持要你親口說好的系統,正是你敢放手讓它動手的原因。

4. 模型升級的體感,不是答得更快,是跳得出框。 同一個問題,上一代給我節流紀律,這一代給我「為什麼要租」。如果你也長期跟 AI 搭檔工作,模型升級後別只拿舊問題驗證它答得好不好——把那些你已經「解決過」的問題重新丟給它一次。 你以為早就結案的事,新模型可能會告訴你:你當初只是學會了忍。


(閉環小註:上上篇它幫我蓋了 worktree 的房間,上一篇它幫我清了沒退房的殭屍,這一篇它直接把 GitHub 的機房搬進了我家。照這個趨勢,下一篇大概是它幫我付電費。)