2026年4月の終わり、火曜日の朝6時半に起きてコーヒーを淹れる前に、wrangler tail claudelab-net をターミナルに打ち込むのが日課になっていました。Cloudflare Workers で動かしている4つの Lab サイト(Claude Lab・Gemini Lab・Antigravity Lab・Rork Lab)と、別軸で運営している Lacrima・Mystery を合わせて、合計6本の Worker が動いています。前夜の自動投稿パイプラインが落としたエラー、深夜にスパイクした 5xx、Stripe Webhook の処理失敗。どれも放置できないけれど、それぞれを順番にログで確認していくと、朝の1時間半が静かに溶けていく感覚が3ヶ月続いていました。
2014年から個人でアプリ事業を運営してきた身として、サーバーログを読む時間は「投資」として割り切ってきた部分があります。累計5,000万ダウンロードまで育った壁紙・癒し・引き寄せ系のアプリ群でも、AdMob の挙動や Crashlytics のクラッシュを毎朝確認する時間は欠かさず取ってきました。ただ、Lab 4サイトと Blog 2サイトを並行で運用し始めてから、ログ判読のコストは明らかに人ひとり分の手作業を超えていました。「順番に開いて、目で追って、似たパターンを記憶と照合する」という作業を、Antigravity の Inline Agent と Background Agent に少しずつ渡していった3週間の記録です。
6サイトの Worker ログを毎朝順番に見ていた頃
きっかけは、4月中旬の月曜日の朝、Claude Lab の自動投稿スケジュールが3本連続で 500 を返していたことに気付くのが、最初のエラー発生から4時間遅れたことでした。Cloudflare の Email Notifications は飛んでくるけれど、6サイト分のメールが朝の受信箱に詰まっていると、本当に対処が必要なものが埋もれます。「クォータ消費がいつもより多い」「ASSETS バインディング経由のフェッチが間欠的に失敗している」といったソフトな異常は、メールにすら現れません。
wrangler tail を1本ずつ走らせる構成は、たしかにリアルタイム性は高いものの、6本同時に開くとターミナルの分割が散らかります。grep で絞り込めば良いのですが、絞り込みのキーワードはエラーごとに違います。「5\d\d」で全部のステータスエラーを拾うと、Cloudflare の health-check を含むノイズが大量に流れてきて、本物のシグナルが流れていきます。
宮大工だった両家の祖父が「手を入れる場所は毎日見える位置に置いておけ」と言っていたのを、なぜか思い出すような状況でした。見える位置にあるけれど、見るコストが高すぎる。これは、人間が判読する前に一次切り分けを誰かに任せたほうが良い種類の作業だと、4月20日頃に観念しました。
構成:Antigravity のターミナルから wrangler tail を出させる
Antigravity の Inline Agent には、ターミナルコマンドを実行して標準出力を読む能力があります。ここに wrangler tail の --format json 出力を流し込み、エージェント側で JSON を1行ずつ判読させる構成にしました。具体的には、以下のような短いコマンドをエージェントに渡すだけです。
# 6サイトのログを並行ストリーミング
for SITE in claudelab-net gemilab-net antigravitylab-net rorklab-net lacrima-jp the-mystery-org; do
wrangler tail "$SITE" --format json &
done
# 30秒間収集して終了
sleep 30 && kill $(jobs -p)このスクリプトを Antigravity 側の AGENTS.md に「朝の最初の30秒で6サイトの直近ログを取得し、level === "error" のレコードと、outcome === "exceededCpu" のレコードを抽出して件数と代表サンプルを返す」と書いて任せました。Agent は最初の数日間、wrangler がインストールされていない別のターミナルセッションを開いてしまうことがあったので、AGENTS.md の冒頭に「ターミナルは必ずプロジェクトルートの .dolice-labs/ops/ から起動する」と一文足したら、迷子にならなくなりました。
ログ判読は Background Agent ではなく、敢えて Inline Agent に任せています。理由は2つで、ひとつは私の朝のコーヒーを淹れる時間(だいたい4分)に並走させたかったから、もうひとつはエージェントの返答に対して即座に追加の質問を投げ返したかったからです。「Stripe Webhook の400が3回続いているけど、これは試運転の決済か本物か」という判別は、エージェント単独では難しく、私の追加情報(前夜に Stripe 側で test_mode 切替を試したかどうか)が必要でした。
任せられたこと、任せなかったこと
3週間の運用で、任せられた範囲と任せなかった範囲がはっきり分かれてきました。
任せられた範囲は、おおむね次の3つです。
- ステータスコード別のサマリ生成: 30秒間に流れた数百行のログから、2xx / 4xx / 5xx の件数と、5xx の代表 3 件を URL とともに切り出す作業。これは Inline Agent が安定して高品質にこなしてくれます。
- 既知のノイズの除外: Cloudflare の health-check、
/.well-known/系のクロール、明らかなボットからの 404 など、「気にしなくていい」パターンの判別。AGENTS.md に「無視リスト」を箇条書きで置いておけば、毎朝同じ判断を再現してくれます。 - Worker 内部の例外スタックの整形: 私のコードベースの例外メッセージは、ローカリゼーション都合で日本語が混ざることがあり、ターミナルでは折返しが汚くなります。エージェントに「整形して、ファイル名と行番号と例外メッセージを箇条書きで」と頼むと、ぐっと読みやすくなりました。
逆に、任せなかった(任せられなかった)範囲は次の2つです。
- 本番デプロイのロールバック判断: 「直近のデプロイが原因かどうか」をエージェントが推測することはできても、ロールバックを実行する権限は与えていません。
wrangler rollbackは実行に承認ダイアログを挟む設計にしてあります。これは、まだ私の判断基準を AGENTS.md に書ききれていないからです。 - Stripe Webhook の意味判定: 400/500 が出ているとして、それが「テストモードでの試運転」なのか「本物の決済で取り逃した」のかは、エージェントには判定不能でした。決済 ID のフォーマットや、Stripe の管理画面側の状態と突き合わせる必要があり、ここは私が見るしかありません。
ぶつかった3つの壁
順調に進んだ部分ばかりではありませんでした。3週間のあいだに、3つの壁にぶつかりました。
1つ目の壁: wrangler tail の出力スキーマが Worker 種別で違う
最初に組んだとき、6サイトのうち1サイトだけ「Smart Placement」を有効にしていて、ログレコードのフィールド名が一部違っていました。具体的には event.request.url の場所が違ったり、smartPlacement 関連のメタデータが追加で乗っていたりして、エージェントが最初の数日間「URL が取得できません」と返してくることがありました。AGENTS.md に「URL は event.request.url ?? event.url ?? '(unknown)' の順で取得する」と擬似コードで書いておいたら解決しました。
2つ目の壁: ターミナルセッションのタイムアウト
Inline Agent のターミナルセッションは、ある程度の時間で自動的に閉じることがあります。30秒の収集中にセッションが切れて、kill: command not found のような出力が混ざることがありました。これは、6サイトを並列ではなく直列に走らせ、1サイトあたり5秒に短縮することで安定しました。並列の方が一見効率的に見えても、エージェントが扱う出力量が一気に増えるとパースが乱れることがあると分かりました。
3つ目の壁: 「異常ではない異常」をどう扱うか
たとえば、Lacrima の特定の投稿が SNS でバズった翌朝、5xx は出ていないけれど、CPU 使用率が普段の8倍に跳ねている、ということがありました。これは「異常」と呼ぶには境界線上で、エージェントは「異常です」と報告してきますが、私としては「想定範囲内の人気上昇」として処理したい。こういう「文脈依存の異常判定」を AGENTS.md に書き起こす作業に、結局時間がかかりました。完全には言語化できておらず、いまも調整中です。
3週間で変わった数字と、変わらなかったこと
体感では、朝のログ確認に費やしていた時間は90分前後から25分前後に落ちました。25分のうち5分が Antigravity との対話、残り20分が「エージェントの報告を踏まえた判断と対処」です。判断と対処の20分は、おそらく今後もそんなに削れません。意思決定は人間が引き受けるべきだという感覚は、3週間でむしろ強まりました。
数値の変化として記録しているものは、おおむね次の通りです。
- 朝の Worker ログ確認時間: 約 90 分 → 約 25 分
- エラー発生から私が一次切り分けを始めるまでの平均遅延: 約 3〜4 時間 → 約 30 分(朝の確認時間が短くなった分、確認頻度を1日2回に増やせた)
- Cloudflare Email Notifications の未読数: 朝の受信箱に常時 40〜60 件 → 平均 10 件以下(エージェント側で「これは無視」と判定したものは Cloudflare Dashboard で通知設定を絞った)
逆に変わらなかったこととしては、深夜帯の障害対応はやはり人間(私)が起きて対応する必要があること、そして「異常かどうかの最終判断」の責任はエージェントに渡せないこと、この2つです。1997年、16歳でインターネットに触れた頃、海外フォーラムで質問を投げて返事を待つ時間に「自分のシステムは自分で見届けるしかない」という感覚を覚えました。29年経ったいま、その感覚は道具が変わっても残っていて、エージェントは「見届けの解像度を上げる」存在ではあっても「見届けそのもの」ではないのだと感じています。
これから試したいこと
次の3週間で試そうとしていることが2つあります。
ひとつは、Background Agent への移行です。Inline Agent は対話的で気持ちが良いのですが、私が PC の前にいなければ走りません。深夜の障害を「翌朝の Inline Agent 起動時にまとめて出力する」のではなく、深夜のうちに Background Agent が一次切り分けを終えていて、朝に私はサマリだけ受け取る、という形に移行したいと考えています。Background Agent の権限設計と、Slack 通知の運用ルールが固まり次第、移行する予定です。
もうひとつは、AdMob と Crashlytics 側の朝の確認作業との統合です。アプリ事業のほうで毎朝見ている指標と、Lab/Blog 6サイトのログを、ひとつのエージェント対話の中で順番に見られるようにしたい。これは AGENTS.md を分離している現状から、共通のエージェント設定にまとめる作業が必要になります。
離れて暮らす子どもたちに将来「父はどうやって運営していたの」と聞かれたとき、「ログを目で追う時間を、判断する時間に置き換えていった」と説明できるだけの記録は残しておきたいと感じています。今回の3週間も、そのための一段だったと振り返れるよう、ログとして残しておきます。