Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Feb 8, 2026 - 日記

OpenClawハートビート: 通知をスパムにしない常時稼働エージェント設計

OpenClawハートビート: 通知をスパムにしない常時稼働エージェント設計 cover image

OpenClaw は、GitHubリポジトリ を中心に急速に注目を集めている常時稼働型のAIアシスタントです(2026-02-08時点で約15万スター)。

WhatsAppTelegramSlack など複数のチャネルで動作できますが、個人的に「常時稼働を現実にするための設計が入ってる」と感じたのがハートビート機能です。

本記事では、OpenClawのハートビート機能を、運用上の実用性(通知の出し方、文脈の分け方、コストの抑え方)に寄せて解説します。

ハートビートとは何か

OpenClawの「ハートビート」は、エージェントが定期的に「チェックリスト」を確認し、対応が必要かどうかを判断する仕組みです。一般的な「定期実行」とは異なり、以下の2つの応答パターンを持ちます。

  • HEARTBEAT_OK: 何も問題なし(通知不要)
  • アラート: 対応が必要な事象を検出(通知を送信)

デフォルトでは30分間隔(Anthropic OAuth/setup-token検出時は1時間)で実行されます(詳細は公式ドキュメント 参照)。

ハートビートの設定は、OpenClawの設定ファイルで行います。 設定ファイルの場所や形式は環境で異なるので、実際のファイルは公式ドキュメントの該当セクションを参照してください。

1
2
3
4
5
6
7
# config.yaml (example)
agents:
  defaults:
    heartbeat:
      every: "30m"
      target: "last"
      prompt: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK."

promptで指定されている通り、エージェントはHEARTBEAT.mdというファイルを読み込みます。 このファイルはエージェントのワークスペースに配置するチェックリストで、ハートビート実行時に何を確認すべきかを定義します。

用語メモ(初出だけ):

  • コンテキスト(context): LLMに渡す入力。会話履歴や追加ドキュメントを含みます
  • セッション(session): 会話履歴や状態を分ける単位(会話の名前空間)
  • 情報の読み戻し/圧縮: 必要な情報を取り出す、不要な詳細は要約して軽くする、といった運用上の工夫

HEARTBEAT.mdの実例

HEARTBEAT.mdの例を見てみましょう。

1
2
3
4
5
# Heartbeat checklist

- Quick scan: anything urgent in inboxes?
- If it's daytime, do a lightweight check-in if nothing else is pending.
- If a task is blocked, write down _what is missing_ and ask Peter next time.

このチェックリストに基づき、エージェントは以下のような判断を行います。

  • 受信トレイに緊急メールがある → アラート送信
  • 日中で特に問題なし → HEARTBEAT_OKを返す(通知なし)
  • タスクがブロックされている → 不足情報を記録してアラート

なぜ「ハートビート」が革新的なのか

従来のインタラクティブなAIエージェント(Claude Code など)は、「ユーザーがプロンプトを入力 → エージェントが応答」という対話型の動作しかできませんでした。

OpenClawが実現したのは、ユーザーの入力なしに自律的に動作するエージェントです。この飛躍を可能にしたのが、以下の2つのプリミティブです。

  1. 自律的な起動(Autonomous Invocation): 時間やイベントに基づいて自動実行
  2. 永続的な状態(Persistent State): 実行間で情報を保持

システム研究者のLaurent Bindschaedler氏の分析 でも、常時稼働エージェントの成立条件として「自律的な起動」と「永続的な状態」が強調されています。

ここから先は、Heartbeatの設定項目を“運用の部品”として捉えると理解しやすいです。

ハートビートを成立させる3つの要素

1. 起動と配送: いつ動いて、どこに通知するか

Heartbeatは定期的にエージェントを起動し、必要なら外部チャネルへ通知します。 このとき重要なのが「配送先」です。

  • every: 実行間隔
  • target: どのチャネルに送るか(last / none / 明示チャネル)
  • to: 送り先のID(電話番号やチャットIDなど)
  • accountId: マルチアカウント時の送り先アカウント

イメージはこんな感じです。

graph LR
    A[タイマー] -->|30分ごと| B[ハートビート起動]
    B --> C{セッション選択}
    C -->|main| D[メインセッション]
    C -->|ops| E[運用セッション]
    D --> F[HEARTBEAT.mdを読む]
    E --> F
    F --> G{判定}
    G -->|問題なし| H[HEARTBEAT_OK]
    G -->|要対応| I[アラート送信]

2. セッション分離: どの文脈で動かすか

Heartbeatは「勝手に動く」ので、どの文脈(= どのセッション)で動かすかが大事です。 雑にやると、関係ない会話を勝手に混ぜて通知がカオスになります。

以下は、OpenClawの設定ファイル(例: config.yaml)で複数エージェントを定義し、特定のエージェントだけにハートビートを設定する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# config.yaml (example)
agents:
  list:
    - id: "main"
      default: true
    - id: "ops"
      heartbeat:
        every: "1h"
        target: "whatsapp"
        to: "+15551234567"

この設定では、mainエージェントとopsエージェントが別々の文脈で動作します。 運用監視のHeartbeatが、普段の雑談や別タスクの文脈に混ざりにくい、というのがメリットです。

3. 状態の外部化: コンテキスト制約をどう扱うか

LLMには入力できる量(コンテキスト)に制約があります。 なので、常時稼働で使うなら「必要な情報を残す」「必要なときに取り出す」「長くなったら圧縮する」が必須になります。

graph TD
    A[LLMコンテキスト] -->|参照| B[直近の会話]
    B -->|保存| C[ノート/Markdownファイル]
    C -->|読み戻し| B
    D["/compact コマンド"] -->|要約して圧縮| C
    C -->|検索| E[関連情報の取得]

OpenClawは、会話履歴やメモを外部に保存し、必要に応じて読み戻します。 /compactコマンドは、会話が長くなったときに要約してコンテキストを軽くするための操作です。

コスト最適化: 2層アーキテクチャ

ハートビートの設計で特筆すべきは、LLM呼び出しを最小化する2層構造です。

第1層: ルールベースチェック(安価)

まず、モデルを回さなくても判定できる「状態の変化」を拾います。 ここで言う“安価”は、少なくともLLMのトークンコストを使わずに判断できる、という意味です。

ポイントは、HeartbeatとCronを用途で分けることです。

  • Heartbeatは「モデルを回すターン」なので、頻度を上げるほどトークンコストが増えます
  • 一方で、Cron等で回すチェックは、基本的にAPIの軽い呼び出しやファイル/キューの状態確認で済みます

なので、コストを抑えたい場合は、まず外側(Cron等)で次のような“信号”だけを集めます。

  • 失敗の有無: 最新のCIが失敗していないか、ジョブが落ちていないか
  • 変化の有無: 新しいPR/issueが増えていないか、キューが増えていないか
  • 緊急性: 期限が近い予定、返信待ちの重要スレッドがないか

そして「信号が出たときだけ」OpenClawを起こして、状況の要約や優先度付けを任せます。 公式ドキュメントでも、HeartbeatとCronの使い分け(どちらをいつ使うか)が整理されています(Heartbeat vs Cron )。

第2層: LLMによる要約(必要時のみ)

OpenClaw側を起動するタイミングになったら、今度はLLMの出番です。 ただし、常時稼働の運用で大事なのは「毎回賢くする」よりも「必要なときだけ賢くする」です。

そのための設計が、Heartbeatの応答契約(HEARTBEAT_OK なら抑制)と、モデル/配送先/可視性の制御です。

以下は、OpenClawの設定ファイル(例: config.yaml)で「Heartbeat時だけモデルを切り替える」例です(フィールドは agents.defaults.heartbeat.model)。

1
2
3
4
5
6
# config.yaml (example)
agents:
  defaults:
    heartbeat:
      model: "anthropic/claude-haiku-4-5" # example: use a cheaper model for heartbeats
      # prompt: "..." # customize the heartbeat prompt body if needed

この切り分けのポイントは、「チェック頻度」と「LLM呼び出し頻度」を同一視しないことです。 例えば、チェック自体は5分おきでも、OpenClawが起動して文章を生成するのは“異常があったときだけ”という運用にできます。

dev.toの実践記事 では、この設計を「ゲートキーパーパターン」と呼んでいます。

ハートビート間隔のトレードオフ

ハートビート間隔の選択は、応答速度とコストのトレードオフです。

短い間隔(5〜15分)

メリット:

  • 高速な問題検出
  • リアルタイム性の高い運用

デメリット:

  • LLM呼び出しが増えるとコスト増
  • 通知疲れのリスク

推奨ケース:

  • 高頻度のPR/CI実行
  • インシデント対応中
  • 「すぐに知りたい」要件がある場合

長い間隔(30分〜1時間)

メリット:

  • コスト削減
  • 通知の適度な抑制

デメリット:

  • 問題検出が遅れる
  • リアルタイム性が低い

推奨ケース:

  • ソロ開発者の日常監視
  • 安定したプロジェクト
  • 「見守る」程度の要件

アクティブ時間の設定

業務時間外の通知を避けるため、activeHoursを設定できます。

以下は、OpenClawの設定ファイル(例: config.yaml)で「実行する時間帯」を制限する例です。

1
2
3
4
5
6
7
8
9
# config.yaml (example)
agents:
  defaults:
    heartbeat:
      every: "30m"
      activeHours:
        start: "09:00"
        end: "22:00"
        timezone: "Asia/Tokyo"

この設定により、午前9時から午後10時の間のみハートビートが実行されます。

実装のベストプラクティス

1. HEARTBEAT.mdは小さく保つ

HEARTBEAT.mdは、毎回のハートビートでLLMのコンテキストに含まれます。大きすぎるとコストが増えるため、簡潔に保ちましょう。

良い例:

1
2
3
4
5
# Heartbeat checklist

- Check for failed CI runs
- Scan inbox for urgent emails
- Review blocked tasks

悪い例:

1
2
3
4
5
6
# Heartbeat checklist

- Check for failed CI runs in the last 24 hours, including all branches
- Scan inbox for urgent emails from the following domains: example.com, test.com, ...
- Review all blocked tasks and their dependencies, including...
[長大なチェックリストが続く]

2. ルールベースチェックを優先

LLMを呼ぶ前に、シェルスクリプトやAPIで判定できることは先に処理します。

ここでの狙いは「LLMに判断させなくてもいいものは、LLMに渡さない」ことです。 実際にやることは単純で、以下のような“観測値”を先に出します。

  • 件数: 未処理アイテムが増えた/減った
  • 状態: 失敗が出た/復旧した
  • 期限: 近いイベントがある

これだけでも、「何も起きてないから黙っていて良い」「今だけ見てほしい」の切り分けができます。

図にすると、こんなパイプラインです。

flowchart TD
  A[データソース
  - CIの成否
  - 受信箱の未処理件数
  - キュー長
  - 期限] --> B[ルールベースで集計
  - 件数の増減
  - 失敗/復旧
  - 期限が近い]
  B --> C{閾値を超えた?}
  C -->|No| D[何もしない
  or HEARTBEAT_OK]
  C -->|Yes| E[OpenClawを起こす
  - 要約
  - 優先度付け
  - 次のアクション提示]

大事なのは、LLMに渡す前の入力を「ログの束」ではなく「短い観測値」に潰すことです。 LLMは、観測値が小さいほど安く、意図通りに動かしやすくなります。

3. マルチアカウント構成を活用

複数のエージェントを異なるチャネルで動作させることで、用途を分離できます。

以下は、OpenClawの設定ファイル(例: config.yaml)で「用途ごとにエージェントを分け、配信先や間隔を変える」例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# config.yaml (example)
agents:
  list:
    - id: "personal"
      heartbeat:
        every: "1h"
        target: "whatsapp"
        to: "+15551234567"
    - id: "work"
      heartbeat:
        every: "30m"
        target: "slack"
        accountId: "work-bot"

OpenClawが示すエージェント設計の本質

OpenClawの面白さは、個々の機能の派手さよりも「常時稼働で破綻しないための、地味だけど効く設計」が揃っている点だと思います。

例えばHeartbeatは、次のような“運用上の詰みポイント”をちゃんと面倒見ています。

  • HEARTBEAT_OK を契約として扱い、不要な通知を抑制する(Response contract)
  • 送り先(target / to / accountId)と実行文脈(session)を分けて考えられる
  • activeHours で夜間の無駄な起動・通知を避けられる
  • チャネルごとの可視性(showOk / showAlerts / useIndicator)で「黙って監視」もできる

「常時稼働」と言うとモデルの賢さに目が行きがちですが、こういう“雑音を減らす仕組み”が揃っていることの方が効きます。

Laurent Bindschaedler氏も、自身のブログ でこう述べています。

「OpenClawが示したのは、概念的な差分は小さいが、誰かがその明快さを持って実装する必要があったということだ。Apple、Google、Microsoftには何百人ものシステムPhDがいるのに、なぜ彼らはこの分野を広く開けたままにしたのか?」

この問いは、技術の本質を突いています。OpenClawは、「新しいアルゴリズム」ではなく、「適切な抽象化」によって成功したのです。

まとめ

OpenClawのハートビートを理解する上で重要なのは、「定期実行」そのものよりも、運用に耐えるための部品が揃っていることです。

  1. 起動と配送: いつ動いて、どこに通知するか(every / target / to / accountId
  2. セッション分離: どの文脈で動くか(勝手に文脈を混ぜない)
  3. 状態の外部化: HEARTBEAT.md を含め、必要な情報を残して取り出す運用

次にやることとしては、まず HEARTBEAT.md を短いチェックリストで作ってみて、通知の出方が期待通りか確認するのが手堅いです。 その上でコストやノイズが気になったら、公式のHeartbeat vs Cron を読みつつ、チェックを外側に逃がす設計も検討すると良いです。

参考リンク