有一天我意識到,我的部落格有留言系統,但我完全不知道有人留言了。
不是漏掉一兩則——是完全不知道。要等 comment-monitor agent 輪詢時才會「被動發現」,再靠一段文字通知告訴我「哦,有人留言,而且我幫你起草了一個回覆,但我不太有把握」。
這整個流程最慢可能延遲到下一個輪詢週期(幾十分鐘)才通知我。對用戶來說,等了半小時,博主毫無反應,下次大概不會再留言了。
這是留言互動的體驗黑洞。
問題拆解:70% 已有,缺的是最後那 30%
盤點了一下現有的基礎設施,其實狀況比我想像的好:
getLatestComments()— 輪詢拉取新留言,已有postReply()— 呼叫 Blog API 發出回覆,已有- 低信心度通知 — 有新留言且 AI 不確定怎麼回,會發一條純文字 Telegram 通知,已有
所以問題很具體:缺的是「推送端(push)」和「一鍵核准互動」。
現在的架構是 pull-based(bot 主動去問有沒有留言),我需要的是 push-based(有留言的瞬間,系統主動告訴我)。
第一個坑:D1 根本沒有原生 Trigger
我第一個想到的方案是資料庫層 trigger:用戶送出留言 → D1 寫入 → trigger 觸發 → 通知我。
簡潔,直覺。但查了文件之後發現:Cloudflare D1 的雲端環境不支援 CREATE TRIGGER。
這個語法在本地 wrangler dev 環境中可以用,一部署到 Cloudflare 就失效。這是個設計上的刻意選擇——D1 是分散式 SQLite,跨 PoP 的 trigger 語義本來就複雜,Cloudflare 現階段直接不支援。
所以 trigger 這條路死了。
正確做法是:在 Pages Functions(處理 POST /api/comments/:slug 的那隻 Worker)執行完 INSERT 之後,在同一個 request handler 裡手動 fetch() 呼叫 Telegram Bot API。同一個 fetch handler,同步完成,不需要額外排程。
1 | // Pages Functions: /functions/api/comments/[slug].js |
約 20 行。就這樣。
第二個關鍵:Inline Keyboard 讓核准變成一個按鈕
通知我有留言,只是第一步。更重要的是:我要怎麼核准 AI 草稿後自動發出回覆?
最笨的做法是:收到通知 → 打開後台 → 找到那條留言 → 編輯回覆 → 手動發送。這完全違背了「AI 幫我做事」的初衷。
Telegram 的 Inline Keyboard 正是為這種場景設計的:通知訊息裡直接附帶按鈕,點 ✅ 就核准,點 ❌ 就略過。
1 | { |
callback_data 裡帶著 comment ID,按下按鈕後 Telegram 把 callback_query 事件送給 bot,bot 就知道要對哪條留言做什麼。
Bot 側需要加一個 handler:
1 | // grammY 的寫法 |
整個核准流程,從收到通知到發出回覆,不需要離開 Telegram。
第三個決策:AI 草稿什麼時候生成?
這是整個設計裡最微妙的問題。
方案 A:在 Pages Functions 裡即時呼叫 Claude 生成草稿。優點是通知訊息一發出就附帶草稿和按鈕,用戶體驗最好。缺點是 Pages Functions 的執行時間有限制(免費版 10ms CPU time),呼叫 Claude 的延遲會遠超過這個上限,用戶的留言請求也會被卡住等待 AI 回應。
方案 B:先推送「有新留言」的即時通知,bot 的 comment-monitor 輪詢後跑完 Claude,再追發「AI 草稿 + 核准按鈕」。用戶體驗稍差(兩條訊息),但架構乾淨,不影響留言寫入的延遲,也不用擔心 Worker 超時。
我傾向方案 B。
理由很簡單:用戶送出留言後,最關心的是「有沒有成功提交」,不是「博主有沒有立即看到」。把即時通知和 AI 草稿解耦,讓每個步驟各司其職,更健壯。
暫存草稿:D1 比 KV 更合適
當 bot 生成了 AI 草稿,等待我核准,這段期間草稿要放在哪裡?
KV(Cloudflare Workers KV)看起來很方便,但它的索引能力不足——我只能根據 key 查詢,無法做「查詢所有 pending 狀態的草稿」之類的操作。
更好的方案是在同一個 D1 資料庫加一張 pending_replies 表:
1 | CREATE TABLE pending_replies ( |
telegram_msg_id 這個欄位特別有用:核准後可以用它呼叫 editMessageText(),把原本的通知訊息從「待核准」改成「✅ 已發出」,避免我看到一堆帶按鈕的舊通知搞不清楚狀態。
最後:完整的 Pipeline
把所有東西串起來,理想流程是這樣的:
1 | 用戶送出留言 |
現有 70% 基礎設施不動,新增的核心程式碼:Pages Functions 約 20 行,Bot handler 約 30 行,D1 建表一條 SQL。
還沒解決的問題
有幾個邊界情況我還在思考:
1. 並發留言洪水:短時間內多人留言,每條都發一個 Telegram 通知,訊息會淹沒 Telegram。需要 debounce 或批次策略(例如 5 分鐘內累積後送出摘要)。
2. 原子性:Pages Functions 裡 INSERT 成功但 fetch() 通知失敗,該怎麼處理?重試?還是接受「最終一致性」?目前傾向接受偶爾的通知遺漏,畢竟 comment-monitor 還在跑,頂多延遲被發現而已。
3. comment_id 時序:Pages Functions 需要先拿到 lastInsertRowid 才能存 pending_reply 和發通知,flow 順序要仔細確認,避免競態條件。
構建這種「人在迴路」(human-in-the-loop)系統的核心挑戰,不是技術,而是決策:什麼時候讓 AI 自動做,什麼時候讓人拍板。
留言回覆的場景裡,我選擇讓 AI 起草,讓人決定發不發。不是不信任 AI 的品質,而是這條訊息代表我作為博主的聲音——在我建立足夠的信心之前,這個按鈕還是應該讓我來按。
一見生財,2026-03-10
載入留言中...