Claude Codeの「見えない作業」を可視化する:ヘッドレスモードで複数ワーカーを制御する実践テクニック
出典: たろう芋

Claude Codeをヘッドレスモードで複数起動し、無人でタスクを処理させる構成では、各ワーカーの状態把握が課題となります。本記事では、この「見えない作業」を可視化するための実装パターンと、マルチエージェント構成における監視戦略を解説します。
Claude Codeのオーケストレーション構成が直面する課題
業務でAIコーディングツールを活用する際、単一のインスタンスでは処理しきれない大規模タスクに直面することがあります。たろう芋さんの投稿は、Claude Codeを「親プロセス(オーケストレーター)」と「複数の子プロセス(ワーカー)」に分けて運用する、いわゆる**マルチエージェント構成**における重要な課題を提起しています。
この構成では、親のClaudeが全体を統括し、末端のワーカーとして`claude -p`(ヘッドレスモード)を複数起動して並行処理を行います。理論上は効率的ですが、実際に構築すると「各ワーカーが今何をしているのか分からない」という根本的な問題に突き当たります。
これは単なる監視の問題ではありません。**可観測性(Observability)の欠如**は、デバッグを困難にし、エラー発生時の原因特定を遅らせ、最終的にはシステム全体の信頼性を損ないます。
ヘッドレスモードの出力を構造化して取得する
たろう芋さんが言及している解決策は、`--output-format stream-json --verbose`オプションと`jq`コマンドを組み合わせる方法です。この手法の本質を理解しましょう。
構造化ログの重要性
ヘッドレスモードで動作するClaude Codeは、デフォルトでは人間向けの対話的な出力を想定していません。しかし、`--output-format stream-json`を指定することで、**機械可読な構造化データ**としてワーカーの状態を取得できます。
claude -p "タスク内容" --output-format stream-json --verbose | jq '.'この構成により、以下のような情報を抽出可能になります:
jqによるリアルタイムフィルタリング
`jq`は単なるJSON整形ツールではなく、**ストリーム処理可能なフィルタエンジン**です。複数のワーカーから大量に流れてくるJSON出力を、必要な情報だけに絞り込むことができます。
# 特定のイベントタイプのみ抽出
claude -p "タスク" --output-format stream-json | jq 'select(.type == "tool_use")'
# タイムスタンプ付きで状態変化を記録
claude -p "タスク" --output-format stream-json | jq '{time: now, event: .type, content: .content}'編集部の視点
他のAIコーディングツールとの比較
このような「AIエージェントのオーケストレーション」という課題は、Claude Code特有ではありません。しかし、各ツールのアプローチには明確な違いがあります。
**GitHub Copilot**は、そもそもエディタ統合型であり、IDEの状態管理機構に依存します。複数インスタンスを並列実行する設計思想自体が異なるため、このような監視課題は発生しにくい一方、大規模バッチ処理には向きません。
**LangChainやAutoGPT**などのフレームワークは、最初からマルチエージェント構成を想定して設計されており、標準でロギングやトレーシング機能を備えています。しかし、これらは汎用フレームワークであり、コーディング特化の最適化がされていません。
Claude Codeの立ち位置は、**コーディングタスクに特化しつつ、ヘッドレスモードで柔軟な統合が可能**という点にあります。つまり、監視機構は自分で構築する必要がありますが、その分だけカスタマイズの自由度が高いのです。
メリットと注意すべき落とし穴
**メリット:**
1. **真の並列処理**: 複数のClaude Codeインスタンスが独立して動作するため、I/O待ちやAPI制限を分散できます
2. **障害の局所化**: 1つのワーカーが失敗しても、他のワーカーやオーケストレーターには影響しません
3. **スケーラビリティ**: ワーカー数を動的に増減させることで、負荷に応じた処理が可能です
**注意点:**
1. **コスト管理**: 複数のAPIコールが並行するため、トークン消費量が急増します。監視なしでは予算オーバーのリスクがあります
2. **状態の同期問題**: 各ワーカーが同じファイルを編集する場合、競合状態(race condition)が発生する可能性があります
3. **デバッグの複雑性**: 問題が発生した際、どのワーカーのどの処理で起きたのかを特定するには、適切なログ設計が不可欠です
適用が効果的なシーン
この構成が特に威力を発揮するのは:
逆に、**単一ファイル内の複雑なロジック実装**や、**高度な文脈理解が必要なタスク**では、単一のClaudeインスタンスを使う方が効果的です。
今日から試せるアクション
1. シンプルな監視スクリプトを作る
まずは1つのワーカーの出力を構造化して記録することから始めましょう。
#!/bin/bash
WORKER_ID=$1
TASK=$2
claude -p "$TASK" --output-format stream-json --verbose 2>&1 | \
jq --unbuffered -c '{worker: "'$WORKER_ID'", time: now, data: .}' | \
tee -a "logs/worker_${WORKER_ID}.jsonl"このスクリプトは、各ワーカーの出力にワーカーIDとタイムスタンプを付与し、JSONL形式(1行1JSON)でファイルに記録します。
2. ダッシュボードで可視化する
ログファイルをリアルタイムで監視する簡易ダッシュボードを作成します。
# ターミナルでリアルタイム監視
tail -f logs/worker_*.jsonl | jq -r '[.worker, .data.type, .data.content.text // "N/A"] | @tsv'より本格的には、Prometheus + Grafana、あるいはDatadogのようなAPMツールに統合することで、メトリクス監視とアラート設定が可能になります。
3. エラーハンドリングを組み込む
ワーカーが異常終了した場合の自動リトライ機構を実装します。
import subprocess
import json
import time
def run_worker_with_retry(worker_id, task, max_retries=3):
for attempt in range(max_retries):
try:
process = subprocess.Popen(
["claude", "-p", task, "--output-format", "stream-json"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
for line in process.stdout:
data = json.loads(line)
# ログ記録やイベント処理
print(f"[Worker-{worker_id}] {data.get('type')}: {data.get('content', {}).get('text', '')[:50]}")
return_code = process.wait()
if return_code == 0:
return True
except Exception as e:
print(f"[Worker-{worker_id}] Attempt {attempt + 1} failed: {e}")
time.sleep(2 ** attempt) # Exponential backoff
return Falseこのコードは、ワーカーの出力を監視しながら、失敗時には指数バックオフでリトライします。
まとめ:見えない作業を「見える化」する価値
Claude CodeをはじめとするAIコーディングツールが成熟するにつれ、単体での利用から**システムとしての統合**へと活用の軸が移っています。複数のAIエージェントを協調させるオーケストレーション構成は、その最前線です。
しかし、「動いているかどうか分からない」システムは、本番環境では使えません。たろう芋さんが指摘するように、構造化ログの取得と適切な可視化は、この構成を実用化するための**必須要件**です。
幸い、Claude Codeは`--output-format stream-json`という明確なインターフェースを提供しています。これを活用し、適切な監視層を構築することで、AIエージェントの「見えない作業」を完全に制御下に置くことができます。
この情報は @たろう芋 さんの投稿を参考にしています。
出典: たろう芋


