5. 自律的行動システム

Cron、ハートビート、Auto-Reply、フック、サブエージェント

概要

OpenClawが「パーソナルアシスタント」たる最大の特徴は、ユーザーの指示を待たずに 能動的にタスクを実行する能力です。この自律的行動は、5つのサブシステムが 連携することで実現されています。

自律ループ: Cronがスケジュールジョブを発火 → ハートビートでメインエージェントを起動 → エージェントがHEARTBEAT.mdを読んで行動 → 必要に応じてサブエージェントを生成 → 結果がAuto-Replyを通じてユーザーのチャンネルに配信 → フックが全ステージで イベントを発行

1. Cronシステム(スケジュールタスク)

src/cron/service.ts

アーキテクチャ

完全なジョブスケジューラで、時間ベースの自律的エージェント実行を可能にします。

スケジュールタイプ

種類説明
at一回限りの実行(絶対時刻指定)「15:00にリマインダー」
every固定間隔(everyMs + anchorMs)「30分ごとにチェック」
cron標準cron式(タイムゾーン対応)0 9 * * *(毎朝9時)

実行モード

モード動作
メインセッション システムイベントテキストをキューに入れ、ハートビートを起動してメインエージェントを呼び出す
分離セッション 独立したPIエージェントをスポーンし、独自のセッション・ツール・モデルで実行。結果はアナウンス/Webhook/なしで配信

実行フロー

armTimer() → setTimeout(60秒上限) │ ▼ onTimer() → タイマーティック │ ├── ジョブリストから期限切れジョブを検索 ├── 各ジョブをrunning状態にマーク ├── 逐次実行 │ ├── [メイン] enqueueSystemEvent() + runHeartbeatOnce() │ └── [分離] runIsolatedAgentJob() → 完全PIエージェント ├── 結果適用 ├── 次回実行時刻の再計算 ├── 状態永続化(JSON5ファイル) └── セッションリーパー(古いCronセッションの掃除)

安全メカニズム

メカニズム設定
指数バックオフ30秒 → 1分 → 5分 → 15分 → 60分(連続エラー時)
最小再発火間隔2秒(スピンループ防止)
ジョブタイムアウト10分(デフォルト)
スケジュールエラー上限3回連続でジョブ自動無効化
スタックラン検出2時間で古いrunningマーカーをクリア
ワンショット自動無効化atジョブは任意の終了状態後に無効化

2. ハートビートシステム(定期チェックイン)

src/auto-reply/heartbeat.ts

ハートビートとは:定期的にメインエージェントを起動し、 HEARTBEAT.mdファイルに記載された保留中のタスクを確認させる仕組みです。

動作フロー

  1. Cronサービスがハートビートトリガーを発火
  2. ハートビートプロンプトが送信: 「Read HEARTBEAT.md if it exists. Follow it strictly. Do not infer or repeat old tasks. If nothing needs attention, reply HEARTBEAT_OK.」
  3. エージェントがHEARTBEAT.mdを読み、タスクがあれば実行
  4. タスクなしの場合 HEARTBEAT_OK を返却 → ユーザーには通知されない

最適化

3. Auto-Replyシステム(自動応答)

src/auto-reply/

全体フロー

受信メッセージ │ ▼ dispatchInboundMessage() (dispatch.ts:35) │ ▼ dispatchReplyFromConfig() (dispatch-from-config.ts:83) ├── メッセージ重複排除 ├── プラグインフック発火(message_received) ├── 内部フック発火 ├── 停止コマンドの高速中断チェック │ ▼ getReplyFromConfig() → AI応答生成 │ ▼ runReplyAgent() (agent-runner.ts) → PIエージェント実行 ├── セッション履歴読み込み ├── コンテキスト構築 ├── PIエンベデッドエージェント実行(ツール付き) ├── モデルフォールバック処理 ├── リマインダーコミットメント検出 │ └── "I'll remember"と言いつつ未スケジュール → 警告ノート追加 │ ▼ 応答ペイロード生成 │ ▼ TTS変換(設定時) │ ▼ チャンネルルーティング → 送信元チャンネルへ返信

インバウンドデバウンス

src/auto-reply/inbound-debounce.ts

チャンネルごとに設定可能なデバウンスで、連続メッセージをバッチ処理します。

グループアクティベーション

src/auto-reply/group-activation.ts

モード動作
mention@メンションされた場合のみ応答
alwaysグループ内の全メッセージに応答

メッセージキューモード

モード動作
steer実行中のエージェントセッションにメッセージを注入
followup次のターンにキュー
collectメッセージをバッチ収集
steer-backlogsteerを試行、失敗時にキュー
interrupt現在の処理を中断

4. フックシステム(イベント駆動)

src/hooks/internal-hooks.ts

イベントタイプ

タイプ発火タイミング
agent:bootstrapエージェント初期化時(ワークスペースDir、ブートストラップファイル、設定を含む)
gateway:startupGateway起動時
message:received受信メッセージ(from、content、channelId等)
message:sent送信後(to、content、success/error)
commandコマンド実行時
sessionセッションイベント
agentエージェントイベント

フック登録と実行

フックの設定可能性

src/hooks/config.ts

5. サブエージェントシステム(生成型ワーカー)

src/agents/subagent-spawn.tssrc/agents/subagent-registry.ts

スポーンフロー

サブエージェントスポーン要求 │ ▼ バリデーション ├── スポーン深度チェック(callerDepth >= maxSpawnDepth → 拒否) ├── アクティブ子プロセス数チェック(activeChildren >= maxChildren → 拒否) └── エージェントID許可リストチェック │ ▼ セッション作成 ├── Gatewayで子セッション作成 ├── モデルオーバーライド・思考レベル適用 ├── サブエージェント用システムプロンプト構築 │ ├── "Stay focused"(集中しろ) │ ├── "Complete the task"(タスクを完了しろ) │ ├── "Don't initiate"(自発的行動するな) │ ├── "Be ephemeral"(一時的な存在であれ) │ ├── ユーザー会話禁止 │ ├── 外部メッセージ送信禁止 │ ├── Cronジョブ作成禁止 │ └── 永続的状態保持禁止 │ ▼ 実行 ├── Gatewayのagentメソッドで子タスクを実行 └── サブエージェントレジストリに登録

レジストリ

src/agents/subagent-registry.ts

アナウンスフロー

src/agents/subagent-announce.ts:710-991

  1. PIランの完了を待機
  2. 最新のサブエージェント出力をチャット履歴から取得
  3. 子孫サブエージェントの完了を待機(部分的なアナウンスを防止)
  4. 統計付き完了メッセージを構築(実行時間、トークン数)
  5. 要求者のセッションへ配信
    • steered: アクティブセッションに注入
    • queued: 後で処理するためにキュー
    • direct: Gateway直接呼び出し
  6. ネスト配信: 要求者サブエージェントが消滅済みなら祖父母にバブルアップ
  7. リトライ: 最大3回、指数バックオフ(1秒→2秒→4秒→8秒)、5分で期限切れ

システム連携の全体像

自律的行動の全体フロー: Cronスケジューラ ─┬─[メイン]──→ enqueueSystemEvent() │ │ │ ▼ │ ハートビート起動 │ │ │ ▼ │ メインエージェント │ HEARTBEAT.md読み込み │ │ │ ├── タスクあり → 実行 │ │ └── サブエージェント生成可 │ └── タスクなし → HEARTBEAT_OK │ └─[分離]──→ 独立PIエージェント │ ├── ツール実行 ├── サブエージェント生成可 └── 結果配信 │ ▼ Auto-Reply パイプライン │ フォーマット → TTS → チャンネルルーティング │ ▼ ユーザーへ配信

安全メカニズムまとめ

メカニズム場所目的
スポーン深度制限subagent-spawn.ts無限エージェント生成の防止(デフォルト最大1)
エージェントあたり最大子数subagent-spawn.ts同時子エージェントの制限(デフォルト最大5)
エージェント許可リストsubagent-spawn.tsスポーン可能エージェントの制御
Cronエラーバックオフtimer.ts指数バックオフ 30秒→60分
Cron自動無効化jobs.ts3回のスケジュールエラーでジョブ無効化
ジョブタイムアウトtimer.ts10分の実行上限
スタック検出jobs.ts2時間で古いマーカーをクリア
アナウンスリトライ上限subagent-registry.ts最大3回リトライ
アナウンス期限subagent-registry.ts5分で古いアナウンスを強制期限切れ
ハートビートスキップheartbeat.ts空のHEARTBEAT.mdでAPI呼び出しスキップ
サブエージェント制約subagent-announce.tsCron/能動的行動/副次クエスト禁止
デバウンスinbound-debounce.ts連射メッセージ処理の防止
送信ポリシーsend-policy.ts自動応答のallow/denyオーバーライド
フックエラー分離internal-hooks.tsハンドラごとのtry/catchでカスケード障害防止