{"code":0,"data":{"id":546,"title":"OpenClaw 心跳优化：从每天 $300 降到 $30 的实战记录","content":"## 背景\n\n我用 OpenClaw 跑了一套 AI agent 系统。主 agent（未然）用 Claude Opus，每 15 分钟触发一次心跳巡检。某天看 GoodVision 账单吓了一跳：\n\n| 日期 | 花费 |\n|------|------|\n| 3/5 | $184 |\n| 3/6 | $303 |\n| 3/7 | $298 |\n| 3/8 | $100 |\n| **优化后 3/9** | **$69** |\n| **优化后 3/10** | **$27** |\n\n三天烧了 $700 多，96% 都花在 Opus 的 input tokens 上。\n\n## 根因分析\n\n### 1. 心跳和对话共享 session\n\nOpenClaw 的心跳设计是跑在 **main session** 里的。这意味着：\n\n- 心跳每 15 分钟往主 session 塞一轮对话（含 tool calls、HEARTBEAT.md 读取等）\n- Telegram 的日常对话也在同一个 session 累积\n- session 文件膨胀到 **8.9MB**\n- 每次 LLM 调用都要发送完整上下文 → Opus $5/MTok input，一天下来几百刀\n\n### 2. `/new` 对心跳无效\n\n用户用 `/new` 只能重置 Telegram DM 的 session，心跳绑定在系统的 `agent:main:main` session 上，不受影响。所以 session 一直在膨胀。\n\n### 3. 心跳本身不需要 Opus\n\n心跳干的事就是：读 HEARTBEAT.md → Jira checkin → 看评论 → 判断是否拾取任务。这些用 Sonnet 甚至纯脚本就够了。\n\n## 优化方案\n\n### 第一层：脚本预检替代 LLM 心跳\n\n核心思路：**cron 脚本做零成本巡检，只在发现异常时唤起 LLM。**\n\n```bash\n# heartbeat-precheck.sh — 每 30 分钟跑一次\ncron 脚本检查：\n1. watchdog 日志有无 CRASH LOOP\n2. jira-cli checkin — 有无新任务/新评论\n3. 积分余额变化\n4. 股票异动 (>5%)\n\n# 全部正常 → exit 0（零成本）\n# 发现异常 → openclaw system event --mode now\n```\n\n关键改动：\n- 禁用 OpenClaw 内置心跳\n- crontab `*/30` 跑预检脚本\n- 脚本里自动关闭 boss 已确认的 review 任务（纯 API 操作，不需要 LLM）\n- todo 任务按优先级排序后传给 LLM，减少 LLM 自己再查一遍\n\n### 第二层：心跳配置优化\n\n当 LLM 被唤起时，也做了优化：\n\n```json\n\"heartbeat\": {\n  \"every\": \"1h\",\n  \"model\": \"goodvision/claude-sonnet-4-6\",\n  \"lightContext\": true,\n  \"session\": \"heartbeat-main\"\n}\n```\n\n- `model: sonnet` — 心跳用便宜模型（$0.6/MTok vs $5/MTok）\n- `lightContext: true` — 心跳不加载完整 session 历史，只注入 HEARTBEAT.md\n- `session: heartbeat-main` — 心跳独立 session，不污染 Telegram 对话\n\n### 第三层：Jira Webhook 实时触发\n\n脚本预检有最多 30 分钟延迟。对于 boss 的操作，需要实时响应：\n\n```javascript\n// server.js — Jira 后端\nfunction triggerSystemEvent(text) {\n  // 调用 OpenClaw /hooks/wake 注入 system event\n  const data = JSON.stringify({ text, mode: 'now' });\n  http.request({\n    hostname: 'host.docker.internal',\n    port: 18789,\n    path: '/hooks/wake',\n    // ...\n  });\n}\n\n// Boss 创建任务 → 立即唤起\nif (creator === 'boss') {\n  triggerSystemEvent(`Kiyor 创建了新任务 #${id}: \"${title}\"`);\n}\n\n// Boss 发评论 → 立即唤起\nif (author === 'boss') {\n  triggerSystemEvent(`Kiyor 在 #${taskId} 发了新评论: \"${content}\"`);\n}\n\n// Agent 评论 → 不触发（零成本）\n```\n\n### 第四层：Session 膨胀防护\n\n```json\n\"session\": {\n  \"maintenance\": {\n    \"mode\": \"enforce\",\n    \"pruneAfter\": \"7d\",\n    \"maxEntries\": 200,\n    \"rotateBytes\": \"10mb\"\n  }\n}\n```\n\n加上每天 4:30 AM 的 session 大小检查脚本，超 5MB 自动开 Jira ticket 上报（去重 + 重复检测追加评论）。\n\n## 架构对比\n\n```plantuml\n@startuml\n!theme plain\nskinparam backgroundColor #FEFEFE\n\ntitle 优化前\n\nparticipant \"Cron\\n(15min)\" as C\nparticipant \"OpenClaw\\nHeartbeat\" as H\nparticipant \"Opus LLM\\n($5/MTok)\" as L\nparticipant \"Jira API\" as J\n\nC -> H : 触发心跳\nH -> L : 发送完整 session\\n(8.9MB 上下文)\nL -> J : checkin\nJ --> L : 无新任务\nL --> H : HEARTBEAT_OK\nnote right : 每次 ~$1\\n一天 96 次 = ~$100-300\n@enduml\n```\n\n```plantuml\n@startuml\n!theme plain\nskinparam backgroundColor #FEFEFE\n\ntitle 优化后\n\nparticipant \"Cron\\n(30min)\" as C\nparticipant \"预检脚本\\n(零成本)\" as S\nparticipant \"Jira API\" as J\nparticipant \"Sonnet LLM\\n($0.6/MTok)\" as L\nparticipant \"Jira Webhook\" as W\n\n== 定时巡检 ==\nC -> S : 执行脚本\nS -> J : jira-cli checkin\nJ --> S : 无异常\nS -> S : exit 0\\n(不唤起 LLM)\n\n== Boss 操作 ==\nW -> L : triggerSystemEvent\\n(lightContext)\nL -> J : 处理任务\nnote right : 仅有事才花钱\\n一天 5-10 次\n@enduml\n```\n\n## 效果\n\n| 指标 | 优化前 | 优化后 |\n|------|--------|--------|\n| 日均 LLM 心跳调用 | ~96 次 (Opus) | 5-10 次 (Sonnet) |\n| 日均花费 | $100-300 | $20-30 |\n| 心跳响应延迟 | 固定 15min | Boss 操作实时，其余 30min |\n| Session 膨胀 | 无限增长 | enforce 自动清理 + 监控告警 |\n| 心跳模型 | Opus ($5/MTok) | Sonnet ($0.6/MTok) |\n\n## 踩过的坑\n\n1. **心跳和对话共享 main session** — 这是 OpenClaw 的设计意图（\"Heartbeat runs periodic agent turns in the main session\"），但会导致上下文无限膨胀。用 `session` 字段可以分离。\n\n2. **`/new` 不影响心跳** — `/new` 只重置 Telegram DM session，心跳绑在 `agent:main:main`。\n\n3. **Docker 容器里没有 `openclaw` CLI** — Jira 后端跑在 Docker 里，最初想用 `child_process.exec('openclaw system event ...')`，但容器里没装 CLI。改用 HTTP 调用 `/hooks/wake`。\n\n4. **`lightContext: true` 是关键** — 即使换了便宜模型，如果每次都送完整上下文，token 量还是很大。lightContext 让心跳只带 HEARTBEAT.md，input tokens 从几十万降到几千。\n\n5. **去重要用本地状态文件** — 纯靠 API 查询去重不够可靠（网络延迟、查询条件限制），加一个 JSON 文件记录 `session → ticket ID` 映射更稳。\n\n## 总结\n\n核心原则：**能不调 LLM 就不调，必须调时用最便宜的模型和最小的上下文。**\n\n这套方案适用于任何跑定时巡检的 AI agent 系统——先用脚本做预检过滤，只在真正需要判断力的时候才唤起大模型。90% 的心跳是 \"一切正常\"，不需要花 $1 让 Opus 告诉你这件事。","acl":"public","tags":["OpenClaw","AI","Agent","成本优化","LLM","SRE"],"category_id":4,"category":{"id":4,"name":"系统可靠性工程","description":"关于SRE实践、工具和技术的文章。","color":"#2B1B43","created_at":"2025-06-19T04:29:04.749+08:00","updated_at":"2025-06-19T04:29:04.749+08:00"},"author":{"id":1,"username":"kiyor","email":"cai@kiyor.com"},"enable_variables":false,"created_at":"2026-03-10T09:59:45.425+08:00","updated_at":"2026-03-10T09:59:45.425+08:00"}}
