沒錯,我整個夏天都沒有更新。
從六月中開始,Google 實習、三篇頂會論文、產學合作、再加上準備碩士畢業找工作,幾座大山全部疊在一起,我直接進入了長達四個月的自媒體冬眠期(或者該說夏眠)。
直到前幾天國慶連假,我下定決心要重啟部落格,開啟了全新的個人作品集紀錄(Day 1 與 Day 2 的 Spark on K8s 系列)。當我寫完繁體中文文章、滿心歡喜地 git push 上去,期待著五月底那篇全站自動雙語化實踐的大功臣自動幫我把中文翻成英文時,我點開英文介面一看——
文章根本沒有出現,整個英文版像被時間凍結在七月一樣。
更詭異的是,GitHub Actions 上的工作流程顯示著一排無辜而刺眼的綠色打勾(✓)。我以為 1010 的文章只是「正在排隊翻譯」,但事實證明,我的自動化系統早已在背後「無聲全滅」。
趁著深夜,我對整個網站做了一次徹底的大健檢,結果抓出了一連串教科書等級的 CI/CD、LLM 生命週期與權限陷阱。這篇文就是這場踩坑之旅的完整復盤。
陷阱一:被 try-except 吞掉的 402(CI 的假綠燈)
五月時,我寫了一支 auto_translate.py 腳本,透過 GitHub Actions 在每次 push 時自動呼叫 Gemini API 翻譯沒有 .md 對應檔的 .zh-tw.md。
這次當我發現文章沒有被翻譯時,我第一時間去翻 GitHub Actions 的 Run Log,赫然看見這段報錯:
Translating: content/posts/261010_2318.zh-tw.md
-> Error translating: 402 RESOURCE_EXHAUSTED.
Your prepayment credits are depleted. Please go to AI Studio...
Translating: content/posts/260612_1625.zh-tw.md
-> Error translating: 402 RESOURCE_EXHAUSTED...
Done. Translated 0 files.
原來我绑定的 Gemini API 預付額度早在夏天就見底了!
但為什麼 Actions 會亮綠燈?問題出在腳本裡:
try:
t_title, t_subtitle, t_desc, t_body = translate_content(...)
except Exception as e:
print(f" -> Error translating: {e}")
return False
當遇到 API 拋出例外時,腳本默默抓住了錯誤、印了一行字,然後以 exit code 0 順利結束。GitHub Actions 看到程式碼以 0 結束,就開開心心地打了一個綠色勾勾。
教訓:在自動化流程中,關鍵依賴(特別是外部付費 API)的失敗千萬不能默默吞掉。要嘛累積錯誤並以 sys.exit(1) 亮紅燈警示,要嘛必須設置告警機制,否則「假綠燈」只會讓人誤以為一切正常。
陷阱二:幾個月不見,模型退役了(404 NOT_FOUND)
既然知道是 API 額度問題,解法很直覺:換一把新的 API Key。
但當我在本機環境跑腳本測試時,迎面而來的卻不是成功的翻譯,而是另一個全新的錯誤:
404 NOT_FOUND.
{'error': {'code': 404, 'message': 'This model models/gemini-2.5-flash is no longer available to new users. Please update your code to use models/gemini-3.8-flash for the latest features and improvements.'}}
看到這行訊息時我愣了一下:才短短幾個月沒看,我腳本裡硬編碼的 gemini-2.5-flash 竟然已經停止對新專案服務,全線換成了 gemini-3.8-flash!
AI 技術演進的速度實在太快,這正是不折不扣的 LLMOps(LLM 生命週期維護) 現實:
- 提示詞工程會老化。
- 模型版本會退役。
- 程式碼裡硬編碼的模型字串隨時可能變成過期代碼。
於是,我一口氣將翻譯腳本以及每週自動抓取趨勢的 weekly_github_ai.py 全部升級至 gemini-3.8-flash。更換後實測,翻譯速度甚至比以前更快,文意語感也更加自然。
陷阱三:核心死因——GitHub Actions 的「防遞迴保護機制」
修正了模型與 API Key 後,我把文章翻譯了出來。但我隨即發現了最根本的謎團:
「即使以前 API 有通的時候,為什麼每次 push 新的中文文章,英文版網站都不會立刻更新,而是要等下一次我推別的 commit 才會出現?」
這也是導致我這次極度混亂的元兇。深入檢查 .github/workflows/ 後,我終於找出了架構上的真正死結:
原本我的專案有兩個獨立的 workflow:
hugo.yml:偵測main分支 push,負責編譯靜態網頁並發布到 GitHub Pages。auto_translate.yml:偵測main分支 push,負責呼叫 Gemini 翻譯,並使用stefanzweifel/git-auto-commit-action將英文檔推回 repo。
這引發了兩個嚴重的架構漏洞:
1. 競態條件(Race Condition)
當我 push 了一篇繁中文章時,這兩個 workflow 會同時被觸發執行。 Hugo 跑得非常快,在短短幾十秒內就完成了 checkout 與 build;而此時翻譯 workflow 才剛開始連線翻譯! 換句話說,Hugo 部署的是尚未生成英文檔的網站。
2. GITHUB_TOKEN 的安全限制(關鍵)
那麼,等翻譯 workflow 跑完、將英文文章 commit 並 push 回 main 之後,難道 Hugo 不會再觸發一次嗎?
答案是:完全不會!
這是 GitHub Actions 的一項核心安全規則:
為了防止自動化腳本意外陷入無窮遞迴(Commit 觸發 Action,Action 又產生 Commit),GitHub 預設規範:任何使用
GITHUB_TOKEN所產生的 push 事件,絕對不會觸發其他 Workflow!
這意味著:
- 你 push 中文版。
- Hugo 部署了「純中文網站」。
- 翻譯腳本把英文版 commit 進了 GitHub repo。
- Hugo 完全沒有收到通知,也根本不知道有新 commit,因此沒有重新部署。
- 英文版就這樣靜靜躺在 Git 倉庫裡,在線上永遠是隱形的,直到某天你心血來潮手動推了另一個 commit。
解決方案:工作流鏈式呼叫(Chaining)
解法其實非常優雅。既然 git-auto-commit-action 是由 auto_translate.yml 掌控的,我們只要在偵測到有翻譯變更並 commit 成功後,手動觸發 Hugo workflow 即可!
我們給予 Action actions: write 權限,並在 commit 步驟後加上:
- name: Commit and push changes
id: auto_commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore(i18n): auto-translate new Chinese content to English"
branch: main
file_pattern: 'content/**/*.md'
- name: Trigger Hugo Deploy
if: steps.auto_commit.outputs.changes_detected == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run hugo.yml --ref main
利用 git-auto-commit-action 自帶的 changes_detected 輸出,只有在確實有新增翻譯檔案時,才會透過 GitHub CLI 主動呼叫 hugo.yml 重新發布。
如此一來,時序變成了:
繁中文章 Push ➔ 翻譯 Action 執行 ➔ 自動 Commit 英文檔 ➔ 主動呼叫 Hugo 部署 ➔ 雙語同步上線!
額外收穫:全站細部健檢與清理
既然大修了管線,我也順手把整個網站的積年雜質清了一遍:
- 消除幽靈重複文章:發現了一篇檔名為
250727_1908.md的歷史檔案,因為 2026 年誤打成 2025 年,導致英文文章列表永遠有兩篇一模一樣的「暫休公告」並排,果斷刪除重複檔。 - 清除
public/快取污染:過去不小心把public/目錄下的favicon.ico、sitemap.xml等編譯產出追蹤進了 Git。這次執行git rm -r --cached public/,將它還給.gitignore,徹底根絕本機 build 造成的 dirty git status。 - 原生搜尋列雙語化:原本頂部搜尋列的 placeholder 在英文介面下依然寫著中文「🔍 搜尋你想要找尋的關鍵字…」,這次利用 Hugo 模板語法做了條件判斷,英文介面下正式換上
🔍 Search articles...與No results found。 - 更友善的語言切換顯示:在
hugo.yaml啟用displayFullLangName: true,讓原本冷冰冰的Zh-Tw與En,變成了親切的中文與English。 - 語言切換不再「粗暴回首頁」:PaperMod 預設在 header 的切換鈕寫死的是
site.Home.Translations,導致讀者在任何一篇文章裡點切換語言時,都會被直接甩回該語言的首頁,閱讀體驗非常差。這次在layouts/partials/header.html中改寫了切換邏輯:優先比對當前頁面的$currentPage.Translations,若有對應翻譯就精準跳轉到同一篇文章的另一種語言版本;只有在當前頁面無翻譯時,才優雅地 fallback 回首頁。
結語
自動化系統就像盆栽一樣,放著不管幾個月,水管會堵住、泥土會乾涸、連原本跑得好好的模型都會換代。
但這次除錯的過程非常過癮。它讓我重新檢視了個人部落格看似簡單的技術堆疊下,隱藏著多少有趣的工程細節——從 API 的錯誤處理、LLM 的版本更迭,到 CI/CD 的狀態機設計與權限邊界。
現在,管線修好了,中英文文章同步了,Day 1 與 Day 2 也都好好地躺在線上。
重新點燃(Rekindle)的不只是作品集專案,還有這個陪伴我記錄思考的小天地。