LLMの運用では、呼出回数、応答成功率、生成トークン数、tokens/secはかなり取りやすいです。

自分も最初はそのあたりを見ていました。

ただ、フェイルオーバーのログを集計すると、98回の検知に対してローカルセッションは86回起動していました。起動率なら87.8%です。

一方で、クラウド側へ自動で戻れた回数は0回でした。

ローカル出力46件の中央値は328バイト。かなり短い出力が7件、エラー相当が6件ありました。

86回起動した、だけでは実態がよく分かりません。なので「返事が来たか」ではなく「決めた範囲の中で、確認できる形まで仕事が終わったか」を数えるようにしました。

成功率を一つにすると、直す場所が分からない

一つの成功・失敗だけにすると、どこで落ちているか見えません。

自分は、検知、handoff、起動、実行、検証、復帰、受入へ分けています。

検知、handoff、起動、実行、検証、復帰、受入の各段階と離脱理由を追う完了ファネル

図1: 98件の処理段階と、ログから確認できた一部の離脱理由です。起動は途中の一段階です。

段階 ここで確認すること 証拠として残すもの
detect 本当に切替対象の上限だったか 分類結果、status、本文シグナル
handoff 再開に必要な状態を書けたか 完全なhandoff、ID、Git情報
spawn 継続セッションを一度だけ起動したか PID、session、spawn event
execute 説明だけでなく実際に動いたか tool call、差分、生成物
verify その仕事の終了条件を満たしたか test、件数、schema、diff
resume 元の実行先へ戻れたか handshake、現在状態の確認
accept 人が必要な判断まで終えたか 承認・却下と理由

各段階の割合は、一つ前を通った件数を分母にします。

それとは別に、最初の検知から最後のacceptまで届いたend-to-endの割合も持っています。

detect: 429なら全部フェイルオーバー、にはしない

HTTP 429が返っても、サブスクリプション上限とは限りません。

一時的な混雑やAPIのrate limitであれば、ローカルセッションを大量に起動する必要はないかもしれません。

検知イベントには、どの上限だと分類したか、根拠にした応答、否定表現との不一致、信頼度を残します。本文に秘密が含まれる場合はmaskします。

ここで誤検知していると、後ろの処理が全部成功しても不要な仕事を増やしただけになります。

handoff: ファイルができただけでは通過にしない

handoffは書込みに成功しただけでなく、最低限の項目があるか確認します。

大きすぎるhandoff、受取側から読めない保存先、別repositoryを指すものは、成功にしません。

ここも「ファイル数」だけ見ると、壊れたhandoffまで成果に見えます。

spawn: コマンドが0で終わっても、起動したとは限らない

起動コマンドの終了コードが0でも、tmux paneや子プロセスがすぐ落ちることがあります。

起動は次のイベントへ分けました。

  1. 起動要求を出した
  2. 対象プロセスまたはsessionを見つけた
  3. モデルへの最初のrequestを確認した
  4. 最初の有効なtool callを確認した

98件中86件という数字は、この途中にある数字です。

クールダウンで止めた12件は失敗へ混ぜず、cooldown-suppressedとして別にしています。意図して止めたものと、起動できなかったものは対応が違うためです。

execute: 文章量ではなく、何かが変わったかを見る

ローカル出力46件の中央値は328バイトでした。

短いこと自体は問題ではありません。短くても正しいファイルを直してテストが通れば終わっています。

逆に長い説明があっても、ファイルを一つも読まず「これから確認します」で止まれば未実行です。

実行段階では次を見ています。

ツールをたくさん使えばよい、という数字にはしていません。

目的に近づく状態変化があったかを見ます。

verify: 始める前に、何をもって終了か決める

仕事が終わったあとで、モデルへ「完了しましたか」と聞いても自己申告になります。

ルーティング時かタスク作成時に、できるだけ確認方法まで書きます。

仕事 これだけだと弱い ここまで確認する
lint修正 「修正しました」 指定lintが終了コード0
候補整理 「確認しました」 未分類候補が0件
記事生成 「記事を書きました」 必須見出し、リンク、HTML構造を検証
設定変更 「反映しました」 実プロセスが期待設定で起動
外部投稿 「投稿しました」 自動側は検証済みドラフト、人が送信確認

人の判断がいる仕事まで、無理に自動完了へしません。

レビューできる成果物を作れたところを自動処理の完了にして、人が承認したかはacceptで別に数えます。

resume: 0件を別のKPIにした

復帰0件を見逃したのは、ローカルセッションの起動を主な成功指標にしていたためです。

resumeでは次を確認します。

69件の期限切れと29件のskipも、同じ未復帰へまとめません。

期限切れならTTLの設計を直す必要があります。skipなら移行時の方針です。理由が違うので、数字も分けています。

全ログをhandoff IDでつなぐ

検知フック、ランチャー、watcher、モデル側が別々のログを書くので、共通IDがないと一件の流れを追えません。

handoff IDを相関IDにしています。

{
  "event": "verification_succeeded",
  "handoffId": "20260803-...",
  "at": "2026-08-03T12:34:56+09:00",
  "route": "local",
  "taskType": "code-change",
  "reason": "tests-passed",
  "durationMs": 183421
}

ログはappend-onlyです。

本文や秘密情報は入れず、分類した理由と数値を残します。現在状態も、イベントを順番に見ると再構成できます。

完了率だけを上げようとしない

完了率だけ最大化すると、危ない操作まで自動許可したり、簡単な仕事だけローカルへ回したりできます。

一緒に見ているのは次の指標です。

タスク種別や難易度で分けないと、短い定型作業が増えただけで全体が改善したように見えます。

ダッシュボードを見る順番

自分は次の順番で見ています。

  1. end-to-endでverified / acceptedまで届いた割合
  2. 各段階の通過率と前週との差
  3. 一番多い離脱理由
  4. タスク種別、経路ごとの差
  5. 安全性とMac資源のガードレール
  6. 個別handoffのイベント列

平均だけでなくp95と、一番古いpendingも見ます。

少数でも何時間も止まった仕事があると、利用者側ではかなり困るためです。

いまの確認項目

返事が出たかどうかは、もちろん運用上必要な数字です。

ただ、そこで終わると86/98という数字だけが残り、復帰0件を見落とします。

いまは、どこまで進んだか、何を確認して終わったのか、どこで止まったのかを別々に数えています。この数字になってから、モデルを変えるべきなのか、ルーティングや権限を直すべきなのかが、かなり判断しやすくなりました。

ローカルLLM設計・全8回

MacでローカルLLMを実際に動かして、うまくいかなかったところを8つに分けて書いています。

  1. PART 01仕事の配置
  2. PART 02コンテキストの削減
  3. PART 03handoff
  4. PART 04冪等性
  5. PART 05プロセス管理
  6. PART 06復帰監視
  7. PART 07安全ゲート
  8. PART 08完了率の計測