はじめに:1人会社で「部長の部下の部下」まで作った話
当社(合同会社ジョインクラス)は、社員が僕1人です。その代わりに Claude Code のエージェントを17体(CTO、CMO、出版部長、税理士…)置いて、launchd で17本以上のジョブを回しています。
組織図を描くと、つい階層を深くしたくなります。「CEO → 部長 → 担当 → 作業者 → 検証者」の5階層です。実際に作って動かしてみました。結論から書きます。
- Claude Code の組み込みサブエージェント(Agent/Task ツール)は入れ子にできない。サブエージェントの中からさらにサブエージェントは呼べない
- 5階層にするなら、
claude -pをプロセスとして呼ぶしかない - 1階層増えるごとに固定費が約 $0.3 かかる。5階層の1本道だけで $1.5 前後になる
- 実用になるのは3階層まで。4階層目からは、コストと失敗の伝わり方で割に合わなくなる
この記事では、当社で実際に動いているスクリプトを使って、組み方、深さを制限するガード、コストの測り方、破綻するポイントを順番に説明します。
前提:Claude Code の「階層」は2種類ある
| 種類 | 呼び方 | コンテキスト | 入れ子 |
|---|---|---|---|
| 組み込みサブエージェント | 親が Agent(Task) ツールで .claude/agents/*.md を起動 |
親とは別だが、同じセッションの中 | 不可(サブエージェントは Agent ツールを持てない) |
| プロセス分離 | Bash から claude -p を起動 |
完全に別のプロセス・別のセッション | 可(Bash が使えれば何段でも) |
つまり5階層は、この2種類を交互に重ねて作ります。
L1 launchd(cron 相当)
└ L2 claude -p … 部門オーケストレーター(例: 出版部長)
└ L3 組み込みサブエージェント(Agent ツール)… 担当者
└ L4 Bash → claude -p … 作業者(別プロセス)
└ L5 組み込みサブエージェント … 検証者
L3 → L4 の段だけは、サブエージェントに Bash を許可して claude -p を叩かせるという「抜け道」です。この記事の破綻点の大半はこの段で起きます。
Step 1:全階層で使う共通ラッパー(実物)
当社では、無人実行の claude はすべてこのラッパーを経由させています。.company/scripts/lib/claude-run.sh そのままです。
#!/bin/bash
# claude を無人実行するときの共通ラッパー。実コストを記録する。
#
# 実測すると「2+2」を聞くだけの1回でも $0.27〜0.47 かかっていた。
# 内訳はキャッシュ作成の約41,000トークンで、
# そのうち自社ファイル(CLAUDE.md・エージェント・スキル)は5,800トークンしかない。
# 残りはClaude Code側の固定オーバーヘッドなので、**削れるのは呼び出し回数だけ**。
#
# 注意: --bare は認証を読まないため "Not logged in" で失敗する。使わない。
_CLAUDE_RUN_LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
claude_run() {
local label="$1"; shift
claude --output-format json \
--fallback-model claude-sonnet-5 \
--max-budget-usd 3 \
"$@" 2>/dev/null \
| python3 "$_CLAUDE_RUN_LIB/record-cost.py" "$label"
}
ポイントは3つあります。
-
--output-format jsonでtotal_cost_usdを取り出し、record-cost.pyがcost-tracker.jsonにラベルごとに記録する。階層ごとにラベルを分けておけば、どの段がいくら使ったかが後から分かる -
--max-budget-usd 3で1プロセスあたりの上限を決める。ネストするとこの上限は段ごとに独立してかかるので、5階層なら最悪 $15 になる - 標準出力には応答テキストだけを流す。親は
RESULT=$(...)で受け取れる
Step 2:L1→L2 部門オーケストレーター(実物)
launchd から毎週水曜7:00に起動している出版部門のジョブです(抜粋)。
source "/Users/kyoagun/workspace/one-ceo/.company/scripts/lib/claude-run.sh"
cd "$PROJECT_ROOT"
RESULT=$(claude_run dept-publishing --effort low -p "
あなたは出版部門のPublisherエージェントです。以下のタスクを実行してください。
1. 全書籍の状態を確認
2. 各書籍のZenn上の情報を確認(公開状態、章数)
3. Q2目標に対する進捗を評価
4. 改善提案があれば1-2個提示
" --allowedTools "Read,Bash" 2>/dev/null | tail -20)
echo "$(date): 結果: $RESULT" >> "$LOG_FILE"
[ -n "$RESULT" ] && notify_slack "📚 [出版部門 週次レポート]\n${RESULT}"
ここで大事なのは --allowedTools "Read,Bash" です。Bash を渡した時点で、この L2 は claude -p を呼んで L3、L4…と下に伸ばせます。週次レポートには本来不要な権限なので、ネストさせたくないジョブでは Bash を外すか、次の深さガードを入れてください。
Step 3:深さガードを入れる(これが無いと暴走する)
ネストで一番怖いのは、子が親と同じプロンプトで自分自身を呼んでしまうことです。止める仕組みがないと、--max-budget-usd の上限まで段を増やしながらお金を使い続けます。
環境変数は子プロセスに引き継がれるので、それで深さを数えます。上のラッパーを次のように拡張します(当社の現行版からの差分です)。
CLAUDE_MAX_DEPTH="${CLAUDE_MAX_DEPTH:-3}"
claude_run() {
local label="$1"; shift
local depth="${CLAUDE_NEST_DEPTH:-0}"
if [ "$depth" -ge "$CLAUDE_MAX_DEPTH" ]; then
echo "[claude_run] depth=$depth >= max=$CLAUDE_MAX_DEPTH, refused: $label" >&2
return 75 # EX_TEMPFAIL。親側で「これ以上委任するな」と判断できる
fi
CLAUDE_NEST_DEPTH=$((depth + 1)) \
claude --output-format json \
--fallback-model claude-sonnet-5 \
--max-budget-usd "$(budget_for_depth "$depth")" \
"$@" 2>/dev/null \
| python3 "$_CLAUDE_RUN_LIB/record-cost.py" "${label}@L$((depth + 1))"
}
# 深い段ほど予算を絞る(3 → 1 → 0.5)
budget_for_depth() {
case "$1" in
0) echo 3 ;;
1) echo 1 ;;
*) echo 0.5 ;;
esac
}
変更点は次の3つです。
-
CLAUDE_NEST_DEPTHを1ずつ増やして子プロセスに渡す - 上限に達したら
claudeを起動せず、終了コード75を返す - コストのラベルに
@L2のように段を付ける。どの段が高いかがcost-tracker.jsonを見るだけで分かる
検証方法
わざと再帰させて、止まることを確認します。
source .company/scripts/lib/claude-run.sh
CLAUDE_MAX_DEPTH=2 claude_run nest-test -p '
次のコマンドをそのまま1回だけ実行し、出力をそのまま返して:
source .company/scripts/lib/claude-run.sh && claude_run nest-test -p "2+2は?" --allowedTools Bash
' --allowedTools "Bash"
echo "exit=$?"
# 段ごとのコストを確認
jq '.' .company/scripts/cost-tracker.json | grep -A2 'nest-test'
CLAUDE_MAX_DEPTH=2 なら L2 と L3 までは起動し、その下は refused が stderr に出れば成功です。
Step 4:L3 サブエージェント側の書き方
組み込みサブエージェントは .claude/agents/*.md のフロントマターでツールを絞ります。当社の CTO エージェントはこうなっています。
---
name: ai-ceo-cto
description: CTO/開発部長エージェント。プロダクト開発全般を統括し、dev-architect, dev-coder, dev-reviewerのタスクを管理する。
tools:
- Read
- Write
- Edit
- Bash
---
description に「dev-coder を管理する」と書いてありますが、組み込みサブエージェントからは dev-coder を Agent ツールで呼べません。Bash が許可されているので claude -p 経由で L4 を作ることはできますが、それをやらせるかどうかは本文で明示しておく必要があります。僕は次の1行を本文に入れています。
## 委任ルール
- 下位エージェントの起動は `claude_run` 経由に限る。`claude` を直接呼ばない(深さガードを通すため)
- 委任してよいのは「独立して検証できる成果物」があるタスクだけ。調査の丸投げは禁止
破綻点:4階層目から何が起きるか
5階層の構成を組んで検証したとき、問題になったのは次の4つです。
1. 固定費が段数に比例して積み上がる
ラッパーのコメントにある通り、claude -p は中身が「2+2」でも1回 $0.27〜0.47 かかります。キャッシュ作成の約41,000トークンのうち、自社の CLAUDE.md やエージェント定義は5,800トークンしかありません。残りは Claude Code 側の固定オーバーヘッドなので、プロンプトを削っても下がりません。
| 構成 | プロセス数 | 固定費の目安 |
|---|---|---|
| 2階層(L2のみ) | 1 | 約 $0.3 |
| 3階層(L2 + 組み込みL3) | 1 | 約 $0.3 + サブエージェント分 |
| 5階層(L2, L4 が別プロセス) | 2以上 | 約 $0.6〜 ※L4がファンアウトすると×N |
L4 を「章ごと」「ファイルごと」にファンアウトさせると、10章の本で $3〜5 が一瞬で消えます。削れるのは呼び出し回数だけなので、段を増やすほど不利になります。
2. エラーが上に届く前に「要約」されて消える
L5 の検証者が「テストが3件落ちた」と返しても、L4 がそれを要約し、L3 がさらに要約すると、L2 の Slack 通知では「概ね問題なし」になってしまいます。各段が tail -20 や「結果を簡潔に報告」で出力を削るので、悪い知らせほど先に消えます。
対策として、失敗はテキストではなく終了コードとファイルで返すようにしました。
# L4 側:失敗は exit code と専用ファイルで返す
if ! npm test > "$WORK/test.log" 2>&1; then
echo "FAILED" > "$WORK/STATUS"
exit 1
fi
echo "OK" > "$WORK/STATUS"
L2 は要約文ではなく STATUS ファイルを読んで判断します。
3. 権限が段を下るほど広がる
L2 に --allowedTools "Read,Bash" しか渡していなくても、L4 の claude -p に --allowedTools を付け忘れると、L4 はプロジェクトの settings.json の設定で動きます。当社では以前、AI が git push --force を自動で実行してブランチが消えたことがあり、それ以来「絶対禁止リスト」を作っています。ネストするとこのリストを全段で守らせる必要があります。ラッパーで --allowedTools を必須にするのが確実です。
4. デバッグが事実上できない
5段目で何が起きたかを追うには、5個のセッションログを時系列でつなぎ直す必要があります。現行のラッパーはラベルが部門名だけなので、ネストすると、どのプロセスがどの段かも分かりません。Step 3 でラベルに @L4 のような段番号を付けているのはこのためです。
使いどころ:深くしていいのはこの3パターンだけ
以上を踏まえて、当社のルールはこうしています。
| 階層 | 使ってよいケース | 例 |
|---|---|---|
| L2 | 定期ジョブの本体 | 出版部門の週次レポート |
| L3(組み込み) | 親のコンテキストを汚したくない読み取り・調査 | 全書籍の config.yaml 走査 |
| L4(別プロセス) | 成果物が独立していて、機械的に検証できるもの | EPUB生成 → check-kindle-epub.py で合否判定 |
| L5 | 原則禁止。使うなら L4 の検証専任のみ | — |
判断基準は1つだけです。「その段の出力を、LLM ではなくスクリプトで合否判定できるか」。できないなら、その段は親が自分でやった方が安く、壊れにくくなります。
まとめ
- 組み込みサブエージェントは入れ子にできない。5階層は
claude -pのプロセスと交互に重ねて作る -
claude -p1回の固定費は約 $0.3。削れるのは呼び出し回数だけ -
CLAUDE_NEST_DEPTH環境変数で深さガードを入れ、コストのラベルに段番号を付ける - 失敗はテキストではなく終了コードとファイルで返す
- 実用上の上限は3階層。4階層目は「スクリプトで検証できる成果物」があるときだけ
経営の視点で言うと、組織図をそのままエージェント階層に写すのは、人間の会社で中間管理職を増やすのと同じで、伝言ゲームとコストが増えるだけでした。当社が CLAUDE.md を1,000行超えで破綻させ、200行に削って .claude/agents/ に分けたときと同じ教訓です。階層は浅く、責務ははっきりさせる。
もっと深く知りたい方へ
オーケストレーターとサブエージェントの役割分担、コンテキストの受け渡し、失敗時のリトライ設計は、拙著『Claude Codeマルチエージェント開発』で、当社の17エージェント構成をもとに体系的にまとめています。
書籍一覧はこちら → https://zenn.dev/joinclass?tab=books