データ処理パイプラインの「exit 0だが本来の目的を達成していない」サイレント障害を、Claude Code CLIの--printモードとmacOS launchdを組み合わせて毎朝自動調査する仕組みを構築しました。人間が毎日手動で「サイレント障害はないか調査して」とClaude Codeに依頼していた作業を、AIエージェントに委任した実践記録です。
背景 — 毎日のモグラ叩きをどう自動化するか
データ処理パイプラインの運用で最も厄介なのは、プロセスが正常終了しているのに本来の目的を達成していないサイレント障害です。具体的には以下のような状態を指します。
- パイプラインの処理件数が0のまま数日経過
- フォールバック処理に切り替わっているのに検知されない
- フィルター施策が有効なのにログに出力されない
- テスト環境が本番データを参照している
従来は毎朝Claude Codeを起動し、「Dry-Run施策でサイレント障害を起こしているものはないか調査して」と手動で依頼していました。これを自動化したいと考えました。
3つのアプローチを比較検討した
A案: シェルスクリプトでデータ収集 → テキストとしてClaude CLIに渡す
1LOG_DATA=$(tail -50 /tmp/pipeline/*.log)2echo "$PROMPT\n$LOG_DATA" | claude -p --model sonnet- Claude はツールを使わず、渡されたテキストだけを分析します
- 最も安全、低コスト($0.02-0.05/日、30秒〜1分)
- 弱点: 収集対象外のログに異常があっても検知できません
B案: Claudeに自由に調査させる
1claude -p --dangerously-skip-permissions --model sonnet \2 "サイレント障害を調査して"- 手動調査と同等の動きです。コスト高($0.10-0.30/日、5〜15分)
- Write/Editも使えてしまうため、意図しないファイル変更のリスクがあります
C案: ブロックリストで書き込み系ツールだけ禁止する(採用)
1claude -p --dangerously-skip-permissions \2 --disallowedTools "Write,Edit,NotebookEdit,Agent,Skill,WebFetch,WebSearch" \3 --model sonnet \4 "サイレント障害を調査して"- Read、Bash(tail/grep/ls等)は使えますが、Write/Editは使えません
- 手動調査に近い探索力を持ちつつ、書き込み操作は構造的に不可能です
- コスト中間($0.05-0.15/日、2〜5分)
なぜC案を選んだか
| A案 | B案 | C案 | |
|---|---|---|---|
| コスト/日 | $0.02-0.05 | $0.10-0.30 | $0.05-0.15 |
| 実行時間 | 30秒〜1分 | 5〜15分 | 2〜5分 |
| 未知の障害検知 | 弱い | 強い | 中間 |
| 安全性 | 最も安全 | リスクあり | 安全 |
A案は「収集したログの範囲内」でしか判断できません。サイレント障害の本質は「どこで問題が起きているかわからない」ことなので、AIが自律的にログを探索できるC案を選びました。
--printと--dangerously-skip-permissionsの違い
この2つのフラグはよく混同されますが、役割が異なります。
| フラグ | 役割 |
|---|---|
--print(-p) | 非対話モード。結果をstdoutに出力して終了 |
--dangerously-skip-permissions | ツール実行時の権限確認をスキップ |
--printだけだと、Claude がReadやBashを使おうとしたときに権限確認プロンプトが表示されます。しかし非対話モードではプロンプトに応答できないため、ツール実行が拒否されます。--dangerously-skip-permissionsを併用することで、権限確認なしでツールが実行されます。
安全策として--disallowedToolsを併用することで、「権限確認はスキップするが、使えるツール自体を制限する」という設計にしています。
最終的なCLIコマンド
1claude -p --dangerously-skip-permissions \2 --disallowedTools "Write,Edit,NotebookEdit,Agent,Skill,WebFetch,WebSearch,TaskCreate,TaskUpdate,TaskStop" \3 --model sonnet \4 --max-budget-usd 3.00 \5 --no-session-persistence \6 "<プロンプト全文>"各フラグの意味は以下の通りです。
-p: 非対話モード--dangerously-skip-permissions: launchd環境で必須(対話プロンプトを出せない)--disallowedTools: Write/Edit等をブロックし読み取り専用にする--model sonnet: コスト効率重視--max-budget-usd 3.00: コスト上限の安全弁--no-session-persistence: セッション履歴を保存しない(ディスク節約)
設計時の判断: --allowedToolsではなく--disallowedToolsを使う理由
当初はホワイトリスト(--allowedTools)で必要なツールだけ許可する設計でした。しかし、必要なツールをすべて列挙するのは保守が面倒で、新しいツールが追加されたときに更新漏れが起きます。ブロックリスト(--disallowedTools)で書き込み系だけ禁止する方が保守しやすいと判断しました。
3層モニタリングの位置づけ
この自動調査は、既存の監視スタックの最上位レイヤーとして位置づけています。
1L1 validate_health.sh (06:30) → プロセスは動いているか?2L2 monitoring_harness.py (毎時) → パイプラインは流れているか?3L3 daily_investigation.py (06:50) → ビジネス目的を達成しているか? ← NEWL1/L2は「exit 0か」「ログが更新されているか」を見る定量的なチェックです。L3はAIがログの中身を読み、「処理件数が0のまま続いている」「フォールバックしている」「フィルターが機能していない」等の意味のある異常を判断する定性的なチェックです。
調査プロンプトの設計 — AIに何を見させるか
調査プロンプト(investigation_prompt.md、約160行)で8つの観点を指示しています。
- 処理件数: entries > 0 か(0が3日以上でアラート)
- 監視対象の状態: 条件発火が動作しているか
- API通信: 外部APIでエラーがないか
- フォールバック不発生: 代替処理に落ちていないか
- フィルター動作確認: enabled=trueの施策がログに出ているか
- エラーパターン不在: Traceback, ERR-, timeout がないか
- DB書き込み正常: 結果テーブルにレコードがあるか
- データ整合性: テスト環境が本番データを読んでいないか
プロンプトにはログファイルのパスもすべて記載しています。--add-dirで作業ディレクトリを追加する方法も検討しましたが、CLAUDE.mdがロードされてコンテキストコストが$0.50以上増加するため不採用にしました。
launchdジョブの設定
1<dict>2 <key>Label</key>3 <string>jp.example.daily_investigation</string>4 <key>ProgramArguments</key>5 <array>6 <string>/path/to/python</string>7 <string>/path/to/daily_investigation.py</string>8 </array>9 <key>StartCalendarInterval</key>10 <dict>11 <key>Hour</key><integer>6</integer>12 <key>Minute</key><integer>50</integer>13 </dict>14 <key>EnvironmentVariables</key>15 <dict>7 collapsed lines
16 <key>HOME</key><string>/Users/username</string>17 <key>PATH</key>18 <string>/path/to/python/bin:/Users/username/.local/bin:/usr/local/bin:/usr/bin:/bin</string>19 </dict>20 <key>Nice</key><integer>10</integer>21 <key>ProcessType</key><string>Background</string>22</dict>重要なポイント:
- PATHに
~/.local/binを含めます(claudeCLIのインストール先) - 実行時刻はL1ヘルスチェック(06:30)の後、朝の業務開始前に設定します
Nice=10,ProcessType=Backgroundで低優先度バックグラウンド実行にします
処理フロー
106:50 launchd 起動2 │3 ├── 調査プロンプト読み込み(8観点 + ログパス + レポート形式)4 │5 ├── claude -p --dangerously-skip-permissions \6 │ --disallowedTools "Write,Edit,..." \7 │ --model sonnet \8 │ "<プロンプト全文>"9 │10 │ → Claude CLI が Bash(tail/grep) で自律的にログを読み、判断11 │ → 約2分で完了12 │13 ├── レポートをファイルに保存(7日超は自動削除)14 │15 └── メール送信1 collapsed line
16 └── 失敗時 → Slack通知にフォールバック実運用の結果
初回テストの結果は以下の通りです。
- 実行時間: 136秒
- コスト: $1.00以内(Maxプラン内なので追加課金なし)
- 検出した問題: 3件
- 特定環境で3日間未実行
- データ鮮度の問題
- API権限の問題
- ユニットテスト: 28件全PASS
毎朝メールで調査レポートが届くようになり、手動調査の時間がゼロになりました。
コスト構造
Claude Maxプラン(月額$200)を利用している場合、APIコストは月額に含まれるため追加課金は発生しません。--max-budget-usd 3.00はMaxプラン内でのコスト上限として機能し、暴走時の安全弁になります。
Maxプランでない場合は、Sonnetモデルで1日$0.05-0.15程度のAPI費用が発生します。月$1.5-4.5程度です。
まとめ
Claude Code CLIの--printモード + --disallowedToolsの組み合わせにより、**「AIが自律的にログを読んで判断するが、ファイル変更はできない」**という安全な自動調査を実現できました。
ポイントは3つです。
--disallowedToolsで書き込み系ツールをブロックし、読み取り専用にする- 調査プロンプトに具体的な観点とログパスを記載し、AIの探索範囲を適切に絞る
- 既存の監視スタック(L1/L2)の上にL3としてAI調査を積む構成にし、役割を明確に分離する
「毎日手動でAIに調査を依頼する」という作業があるなら、それ自体を自動化する価値があります。