前言:AI Agent 的維護成本問題
大家都在聊怎麼讓 AI agent 更聰明,很少人聊怎麼讓 agent 更省。
真實數字:我的 OpenClaw agent 一開始全用 LLM heartbeat,每小時燒 token 檢查「有沒有事」。一天 24 次 LLM call,90% 的回覆都是 HEARTBEAT_OK——什麼事都沒發生。
問題不是 LLM 太貴,是用 LLM 做不需要 LLM 的事。
這篇記錄了一個 AI agent 從「LLM 做所有事」進化到「只做該做的事」的過程——heartbeat 系統三次重構,self-improvement 系統上線,以及一個反直覺的結論:AI agent 成熟的標誌不是用了多少 AI,而是把多少東西從 AI 移出去。
演進一:全 LLM Heartbeat(失敗)
最初的架構很直覺:OpenClaw 內建 heartbeat 機制,每小時叫醒 agent 做檢查。檢查清單包括 email、calendar、版本更新、sub-agent 殘留、cron 狀態等。
想法很美好:讓 agent 主動發現問題。
實際跑起來的問題:
- Token 浪費:agent 醒來要讀 context,花 token。90% 的時間回
HEARTBEAT_OK(沒事)。 - Session 衝突:偶爾 heartbeat cron 跟使用者對話搶 session,誰也進不去。
- Heartbeat skip:當 main session 閒置太久,OpenClaw 會跳過 cron,導致監控失效。
最致命的是:這是一個「越有用越浪費」的系統。監控項目越多,每次 heartbeat 的成本越高,但有事的機率並沒有相應增加。
演進二:bash + python + LLM 混合(不穩定)
第二版的想法:先用腳本收集資料,有異常才叫 LLM。
架構:routine-checks.sh → Python 分析 → 有事才 spawn isolated LLM session。
邏輯上說得通,但實作有問題:
- Python crash:型別不一致導致崩潰(JSON 裡的 epoch int vs ISO string,Python 處理時型別錯誤)
- 腳本邏輯散落各處:每次加新檢查都要改腳本 + 改 LLM prompt,維護困難
- 錯誤處理不一致:bash 的錯誤處理跟 Python 的錯誤處理邏輯不同,出錯時很難 debug
這個版本跑了幾週就因為 Python crash 壞掉,得手動重啟。對於一個「監控系統」來說,這是不可接受的。
演進三:Rust Binary + Type A/B 區分(目前)
第三版重新思考了核心問題:什麼事需要「理解力」,什麼事不需要?
核心原則:能固定就固定,不需要理解力的事不用 LLM。
把所有監控任務分為兩類:
Type A 監控型(固定邏輯判斷):
- Calendar 衝突檢測:比較時間範圍,有重疊就報警
- Sub-agent 殘留檢測:掃描 session 清單,超過 X 小時就清理
- Repair loop 偵測:同一錯誤短時間內重複出現
這些都是固定邏輯,不需要 LLM「理解」什麼。有異常直接發 Telegram 通知。
Type B 分析型(需要理解力):
- Email 分析:哪些 email 重要?需要立即回覆嗎?
- Self-improvement 回顧:
.learnings/裡累積的問題哪些該升級到 MEMORY.md?
這些需要「判斷」和「理解」,先收集資料,再 spawn isolated LLM 分析。
最終實作是一個 461KB 的 Rust binary,無異常時 <100ms(vs 舊版 ~20s)。
為什麼選 Rust?穩定性、無 runtime 依賴、錯誤處理嚴格。監控系統自己不能是不穩定因素。
// 偽代碼示意
fn main() -> Result<()> {
let checks = vec![
TypeACheck::CalendarConflict,
TypeACheck::SubAgentFallback,
TypeACheck::RepairLoop,
TypeBCheck::EmailAnalysis,
TypeBCheck::LearningsReview,
];
for check in checks {
match check {
TypeACheck(check) => {
if check.has_issue()? {
telegram_notify(&check.alert_message())?;
}
},
TypeBCheck(check) => {
let data = check.collect_data()?;
if !data.is_empty() {
spawn_llm_analysis(data)?;
}
}
}
}
Ok(())
}
Cron 遷移:OpenClaw → System Crontab
更進一步:連 OpenClaw 自己的 cron 都不用了。
Session-based cron 有固有問題:skip、session 衝突、announce 互搶。解法是直接用 system crontab,繞過 OpenClaw 的 session 機制。
當前的 cron 清單:
# 固定腳本(不需 LLM)
05 * * * * routine-checks # Rust binary
05 08 * * * version-check.sh # 比版本字串
10 08 * * * morning-report.sh # 每日報告
00 09 * * * portfolio-report.sh # 持倉報告
02 20 * * * memory-janitor.sh # 過期記憶清理
# 先收集再決定(需 LLM)
02 * * * * memory-sync.sh # 有對話才整理
02 20 * * * daily-briefing.sh # 收集後分析
設計原則圖:
需要做的事
│
├─ 固定邏輯? → bash/Rust 腳本 → 直接執行
│
├─ 需要判斷,但可以先收集資料?
│ → bash 收集 → 有資料才 spawn LLM
│
└─ 需要即時互動? → 留在 main session
8 個 cron 裡,6 個完全不需要 LLM,2 個是先收集再決定。
Self-Improvement 系統
Routine-checks 解決了「怎麼省」,但 agent 還需要「怎麼學」。
想像一下:agent 犯了同一個錯誤三次,但每次都當作新問題處理,沒有「學習」。這不只是浪費,更是沒有進步。
Self-improvement 系統的核心是信號分類:
三級信號
🔴 repair — 需要修復的問題
- 指令/操作失敗 →
ERRORS.md - 使用者糾正(「不對」「其實應該…」)→
LEARNINGS.md - 之前修好的問題又壞了 →
ERRORS.md(regression)
🟡 optimize — 可以改進的地方
- 知識過時或錯誤 →
LEARNINGS.md - 發現更好的做法 →
LEARNINGS.md(best practice) - 手動重複操作 2+ 次(應自動化)→
LEARNINGS.md(manual repeat)
🟢 innovate — 新需求
- 使用者要求不存在的功能 →
FEATURE_REQUESTS.md - 想做但目前做不到 →
FEATURE_REQUESTS.md(capability gap)
格式規範
每條記錄格式:## [TYPE-YYYYMMDD-XXX] 標題 + Summary + Details + Suggested Action
關鍵欄位: recurring_count(同一模組/問題出現幾次,首次為 1)
提升規則: recurring_count ≥ 3 → 提升到 MEMORY.md 的 Agent Cases/Patterns
實際案例
案例一:Sub-agent 回傳機制
## [repair-20260228-001] Sub-agent 結果未送達使用者
**Summary:** Sub-agent 完成任務並 announce,但使用者沒收到結果
**Details:** Sub-agent 用 system event announce,但 main session
正在處理其他對話,announce 被排隊延遲
**Suggested Action:** 改用 sessions_send 直接回傳
**recurring_count:** 3
這個問題出現 3 次後,被提升到 MEMORY.md 的 Agent Patterns:「Sub-agent 結果必須用 sessions_send 回傳,不依賴 announce 機制」。
案例二:Edit tool 匹配失敗
## [optimize-20260301-002] Edit tool 頻繁匹配失敗
**Summary:** 使用 edit tool 時,oldText 匹配失敗率高
**Details:** 主要是空白字元不一致(tab vs space),或複製時漏了換行
**Suggested Action:** 先 read 確認實際內容,再 edit
**recurring_count:** 4
4 次重複後提升為操作規範:「Edit 前必須 read 確認 exact match」。
兩個系統的交集
Self-improvement 和 routine-checks 形成閉環:
- Self-improvement 偵測到
manual_repeat(手動重複 2+ 次) - 觸發自動化需求:這個流程應該腳本化
- Routine-checks 本身就是產物:從「手動檢查 email/calendar/版本」→ 記錄為 manual_repeat → 開發自動化腳本
結果:偵測問題 → 記錄 → 累積到閾值 → 程式化 → 減少 LLM 依賴。
最近的例子:「檢查 sub-agent 是否殘留」這個動作手動做了 4 次,記錄為 manual_repeat。結果:寫進 routine-checks binary,變成每小時自動檢查。
當前架構全貌
整個系統現在長這樣:
System Crontab
│
├─ routine-checks (Rust binary, 每小時)
│ ├─ Type A: 固定邏輯 → 直接 Telegram 通知
│ └─ Type B: 先收集 → 有事才 spawn LLM
│
├─ 其他固定腳本 (6個)
│ └─ 版本檢查、portfolio 報告等
│
└─ 條件性 LLM (2個)
├─ memory-sync: 有對話才整理記憶
└─ daily-briefing: 收集 email/社群後分析
Self-improvement 系統平行運行:
對話中偵測信號 → 記錄到 .learnings/
↓
recurring_count ≥ 3 → 提升到 MEMORY.md
↓
manual_repeat 類型 → 觸發自動化 → 進入 routine-checks
學到的事
反直覺的結論
AI agent 成熟的標誌不是用了多少 AI,而是把多少東西從 AI 移出去。
類比:好的工程師不是寫最多 code 的,而是知道什麼時候不該寫 code。
LLM 應該只做需要「理解力」的事——分析、創意、判斷。其他一切:grep、find、curl、Rust binary。
三個設計原則
- 能固定就固定:不浪費 LLM token 做 grep/find/比較
- 先收集再決定:資料收集用腳本,只在需要理解力時才呼叫 LLM
- Type A/B 區分:監控型(固定邏輯)vs 分析型(需 LLM)
為什麼這樣重要
不只是省錢,更是可靠性。LLM 再強,也會有 latency、rate limit、偶爾的奇怪回覆。對於監控系統來說,461KB 的 Rust binary 比任何 LLM call 都可靠。
Self-improvement 系統確保 agent 不只是「聰明」,還會「學習」。同樣的坑不踩第二次,同樣的手動流程不重複第三次。
下一步
目前的 routine-checks 還有提升空間:
- 預測性監控:不只是「有事才報」,而是「可能有事就預警」
- Agent health metrics:token 使用量、錯誤率、回應時間的趨勢分析
- 更細緻的 Type B 收集:針對不同類型的分析任務,收集更精確的資料
但核心原則不會變:盡量不用 AI,該用 AI 時才用 AI。
這是 OpenClaw 系列的第三篇。前一篇是記憶管理架構。Self-improvement 系統正在運作中。
