長時間タスクで一番怖いのは、失敗そのものではない。沈黙である。
人間側が家事をして、買い物をして、風呂まで終えて戻ってきたのに、AI側はまだ「作業中」。ここで人間の脳内には二つの可能性が同時に発生する。
- めちゃくちゃ真面目に巨大なテストを回している。
- どこかで完全に止まっている。
画面の「考え中」だけでは、この二つを区別できない。逆に、しばらくcommitがないことも停止の証拠にはならない。テスト、調査、比較、生成、外部待ちでは、正常でも成果物がしばらく出ないことがある。
だから長時間AIには、知能とは別に**観測可能性(observability)**が必要になる。
1. 結論――10分を超えそうなAI作業は「現在地」を記録した方がいい
長時間実行型のAIエージェントには、完成時の報告だけでなく、作業途中の状態を外から読める形で残す運用がかなり有効だ。
重要なのは、毎分「まだ生きてます!」と鳴くことではない。必要なのは、
- いま何をしているか
- どこまで終わったか
- 何が残っているか
- テスト中なのか
- 何かで詰まっているのか
- 最後に生存確認できたのはいつか
- 最後にコードや成果物が変わったのはいつか
を区別できることだ。
たとえば実務設定の一例として、目に見える成果物が10分出ないならHeartbeatを更新し、20分を超えてHeartbeatもないなら「停止」ではなく STALE_UNKNOWN と読む、という運用ができる。
ここで大事なのは、20分沈黙したAIに対して「死亡確認!」と勝手に鐘を鳴らさないこと。正しくは「最新状態が古すぎて、動いているか判断できない」である。
2. なぜcommit履歴だけではダメなのか
Gitのcommitは実変更の記録として優秀だが、生存確認には弱い。
長いテストを1時間回している最中、commitは増えない。しかし作業は正常に進んでいるかもしれない。逆に、Heartbeat用にどうでもいいファイルを10分おきにcommitすれば、見かけ上は元気でも履歴はゴミだらけになる。
つまり、
Heartbeat = いまの状態 Material change = 実際に何かが変わった証拠
として分けた方がいい。
lastHeartbeatAt と lastMaterialChangeAt を別々に持てば、「30分commitはないけど、5分前にTESTINGのHeartbeatがある」という状態を正しく読める。
AIの進捗確認で欲しいのは、毎回新しいcommitではない。沈黙の意味を判別できる情報である。
3. 最低限、何を記録すればいい?
長時間runでは次の項目があるとかなり強い。
runId:その作業を一意に識別するIDstate:RUNNING / BLOCKED / COMPLETED / STALE_UNKNOWN などphase:READING / IMPLEMENTING / TESTING / VERIFYING などworkingOn:いま具体的に何を触っているかstartedAt:開始時刻lastHeartbeatAt:最後に生存・状態を確認した時刻lastMaterialChangeAt:最後にコード・契約・成果物・テスト結果が変わった時刻branch / baseSha / headSha / PR:どの作業線にいるかcompletedMilestones / remainingMilestones:終わったものと残りblockers:詰まりと、その影響範囲tests:何を検証中かnextCheckpoint:次に何が起きれば一区切りか
この情報があれば、「6時間動いてるっぽいけど何してるんだ?」が、
IMPLEMENTING → TESTING → INTEGRATING → VERIFYING
のように読める。
人間から見ると、これはかなり大きい。AIが賢くても、ブラックボックスの6時間はただの不安装置である。
4. テスト中は特にHeartbeatが必要
AIが長時間止まったように見える最大の原因の一つがテストだ。
テスト前に、
- phase =
TESTING - テスト名
- 対象範囲
- 固定総数があるなら
84 / 127のような実数 - 現時点でfailureがあるか
を記録しておくとよい。
逆に「82%完了しました」のような、根拠のない雰囲気パーセントは禁止した方がいい。設計やデバッグの「あと18%」は、だいたい誰にも分からない。
割合を出すなら、分母が最初から固定されているときだけにする。
- 84 / 127 tests
- 3 / 5 acceptance gates
なら意味がある。
「実装82%」は、AI版の「もうすぐ着く」である。場所を聞くとまだ家にいる可能性がある。
5. 「止まった?」は3段階で読むと安全
一つの実務例では、Heartbeatの古さを次のように読む。
| Heartbeat経過 | 読み方 |
|---|---|
| 0〜10分 | CURRENT:現在情報として扱える |
| 10〜20分 | HEARTBEAT_OVERDUE:更新遅れ |
| 20分超 | STALE_UNKNOWN:状態不明 |
ここで STALE_UNKNOWN は FAILED ではない。
その後、
- 進捗コメント
- Check / Status
- branch / PR
- 最新commit
- 最後に推測
の順で読むと、早とちりが減る。
たとえばHeartbeatが古くても、その5分後に新しいPRやcommitが出ていれば、「進捗表示はサボったが作業そのものは動いていた」と分かる。
AIにもあるのだ。本業はやってるけど日報だけ忘れました事件が。
6. やってはいけない進捗管理
Heartbeat専用commitをmainへ積む
履歴が汚れ、並列作業と衝突し、不要なCIやデプロイを誘発し得る。HeartbeatはIssueコメント、Check、Status、非本番ops領域など、軽量で上書き可能な場所へ出す方がよい。
Heartbeatごとに新コメントを増やす
1 run = 1つの進捗レコードを更新する形の方が読みやすい。10分ごとに新コメントが増えると、進捗確認だけで考古学が始まる。
沈黙を即「失敗」にする
Heartbeatが古いだけなら STALE_UNKNOWN。失敗は、テスト失敗、例外、明示的な終了など、肯定的な証拠があるときだけにする。
秘密や生ログを書く
APIキー、token、password、private URL、生の私的会話、個人情報、機密ファイル内容は進捗ログに出さない。必要なのは状況であって、秘密の大放出祭ではない。
進捗システムが壊れたら本作業まで止める
観測機能が壊れても、安全に続けられる実装まで止める必要はない。別の進捗面へfallbackし、本作業は独立して進める。
7. 実用テンプレ――これくらい見えればかなり安心
State: RUNNING
Phase: TESTING
Run ID: agent-20260916-long-task
Started: 10:00
Last heartbeat: 14:05
Last material change: 13:42
Branch: feat/long-task
Head SHA: abc1234
PR: #123
Working on:
- regression tests
Completed:
- runtime implementation
- contract update
Remaining:
- regression completion
- merge verification
- production readback
Blockers:
- none
Tests:
- 84 / 127 passed so far
- no failure observed
Next checkpoint:
- finish regression, then integration
この程度でも、人間は「止まった?」ではなく「テスト中か」と判断できる。
進捗表示の目的はAIをせかすことではない。人間側が無駄に再起動・中断・重複指示をしなくて済むようにすることである。
8. まとめ――長時間AIは「賢さ」と「見えること」を分けて設計する
長時間AIを使うほど、完成物だけ見ればよいという考えは苦しくなる。
- commitがない ≠ 止まった
- Heartbeatがある ≠ 実変更が進んだ
- Heartbeatが古い ≠ 失敗した
- blockerが一つある ≠ 全体停止
- テスト中の沈黙 ≠ 寝落ち
という区別が必要になる。
AIが6時間働けるなら、人間が6時間ずっと画面を凝視する必要はない。むしろ、見に来た瞬間に「今どこ?」が分かる設計にしておく方がいい。
長時間AIの理想は、「ずっと喋っているAI」ではない。
黙って仕事はする。でも、覗けば現在地が分かるAI。
それが一番、任せやすい。

