AIエージェントを24/7運用する前に知っておきたかったこと

Dev.to / 2026/3/28

💬 オピニオンDeveloper Stack & InfrastructureIdeas & Deep AnalysisTools & Practical Usage

要点

  • 著者は、本番環境で長期間稼働するAIエージェントを運用する際に繰り返し起きる運用上の失敗パターンを共有し、よくある確認手段では実際のハングやクラッシュを見落としうる点を強調している。
  • エージェントのメインループの内部から(別のヘルスチェック機構経由ではなく)ハートビートを送ることを推奨し、ループがI/Oで詰まる、デッドロックする、リトライにより挟まった状態になるなどを検知する。
  • あるインシデントでは、典型的なアプリケーションのエラーログを残さないまま、エージェントがOSによって(例:メモリプレッシャー) kill され、気づかれない停止状態が数時間続いたと述べている。
  • この記事では、これらの推奨事項をフレームワークに依存しないパターンとして位置づけており、LLMを反復的に動かすエージェント、外部APIをポーリングするエージェント、スケジュールに従って意思決定するエージェントなどにも適用できるとしている。
  • 最小限のコード断片として、ハートビート関数と、監視システムへ定期的にステータスデータを出力するメインループの例を示している。

私はしばらくの間、長期間稼働するAIエージェントを本番環境で運用してきました。特定のワークロードは時間とともに変わりましたが、運用上の障害は驚くほど一貫していました。

以下に示すのは、初日から欲しかったセットアップです。これは特定のフレームワークに依存しません。LLMをループで回す、外部APIをポーリングする、あるいはスケジュールに基づいて意思決定を行うなら、これらのパターンが重要になります。

1. あなたのエージェントは死んで、知らせてくれない

最初に、私のエージェントの1つが夜間にクラッシュしたとき、気づくまでに数時間を失いました。エラーログがありませんでした。通常の意味でのアプリケーションエラーではなく、OSがメモリのためにプロセスを殺したからです。

いま私がやっていること: すべてのエージェントが、そのメインループの中からハートビートを送ります。別のヘルスチェック用スレッドではありません。サイドカー処理でもありません。実際のループからです。

この違いは重要です。メインループがI/Oで詰まっている、ロックでデッドロックしている、あるいはリトライ経路の中で固まっているなら、外部の「プロセスは起動している」というチェックはほとんど役に立ちません。

最小のパターンは次のとおりです:

import time

def heartbeat(agent: str, *, status: str = "ok", tokens_used: int = 0) -> None:
    # あなたが使っている監視システムに送ります。
    print(
        {
            "agent": agent,
            "status": status,
            "tokens_used": tokens_used,
            "ts": time.time(),
        }
    )

while True:
    tokens_used = 0

    result = run_agent_cycle()
    tokens_used += result.tokens_used

    heartbeat("my-agent", status="ok", tokens_used=tokens_used)
    time.sleep(60)

ハートビートが止まったら、何かがおかしいということです。私は通常60秒ごとに確認し、2回連続でハートビートを見逃したらアラートを出します。

2. 自動再起動は思っているより難しい

「とにかく再起動すればいい」は簡単そうに聞こえますが、エッジケースに当たるまでの話です:

  • 再起動ループ: 設定が悪いと、エージェントは起動直後にクラッシュします。クールダウンがないと、クラッシュ→再起動→クラッシュ→再起動が永遠に続きます。
  • プラットフォーム差: Dockerの再起動ポリシーはうまく機能します。macOSのlaunchdは、サービスドメインが間違っていると失敗を静かに(サイレントに)します。systemdRestartSecが必要で、指定しないとスピンしてしまう可能性があります。
  • 状態の破損: エージェントが状態ファイルへの書き込みの途中でクラッシュした場合、再起動すると不整合な状態に置かれてしまいます。

いま私がやっていること: 再起動の間に5分のクールダウンを入れます。3回再起動に失敗したら、試すのをやめて私にアラートを出します。再起動時には、エージェントが再開する前に自分の状態を検証します。

良い再起動ポリシーは「常に再起動」のようなものではなく、むしろ次のようにします:

missed heartbeats -> mark unhealthy
restart once -> wait 5 minutes
restart again -> wait 5 minutes
restart third time -> stop auto-restarting, escalate to human

3. LLMのコストはヘルス指標

これが私の最大の気づきでした。従来のサービスではCPU、メモリ、レイテンシを監視します。LLMエージェントでは、サイクルあたりのトークンコストが、問題をいち早く検知できる指標であることが多いです。

暴走するループはCPUを跳ね上げません(API呼び出しはI/Oバウンドです)。メモリも跳ね上がりません。しかしトークン使用量は、200/分から40,000/分へ即座に増えます。サイクルあたりのコストを追跡していないと、API請求書を見て初めて分かることになります。

最も単純な形は移動ベースラインです:

baseline = rolling_average(tokens_per_cycle[-50:])

if tokens_used_this_cycle > baseline * 10:
    alert("possible loop", tokens_used=tokens_used_this_cycle)

4. 丁寧なシャットダウンは必須(任意ではない)

私のエージェントの1つは、シャットダウン中にAPI呼び出しをバースト的に実行して、後始末を安全に完了させます。初めてループ検知を入れたとき、すべての丁寧なシャットダウンを暴走として検知してしまいました。

いま私がやっていること: エージェントは後始末の前に「シャットダウン中」を通知します。監視システム側は、そのバーストが発生することを想定しているため、検知でフラグを立てません。

5. 日次レポートが遅い問題を見つける

アラートは緊急事態を検知します。日次レポートは、アラートが見逃すような遅いドリフトを見つけます。たとえば、エージェントが徐々にサイクルあたりのトークンを多く使っていく場合、あるいはcronの競合で毎日同じタイミングで1回だけ再起動している場合などです。

私は各エージェントについて、日次の健康状態・コスト・イベント履歴のサマリーを確認します。私の運用改善の多くは、リアルタイムのアラートからではなく、そのレポートのパターンから生まれました。

毎朝ほしい基本レポートは次のとおりです:

- そのエージェントは1日中生きていましたか?
- 再起動イベントは何回起きましたか?
- サイクルあたりのトークンコストはベースラインから外れましたか?
- ループ検知やクールダウンのイベントはありましたか?
- 自動で復旧できたものはありましたか?それとも人の対応が必要ですか?

これらのパターンは複雑ではありませんが、始めたときはどこにも書かれているのを見つけられませんでした。どなたかの「学習のためのいくつかの経験」を少しでも節約できれば幸いです。

私のセットアップがどのようなものか見てみたいなら、これらのアイデアを ClevAgent に組み込みました。とはいえ、正直なところ、手作りのハートビートに加えて、1サイクルあたりのコストを追跡する仕組みがあれば、ほとんどそこまでで目的は達成できます。

広告