目的
Claudeの記事を色々見ていると、「エージェント同士でやり取りし、人間は最初の指示を出すだけであとは全部Claudeがやってくれる」という事例を見かけました。自分もこれを実現したいと思い、実装用とレビュー用のサブエージェントを作成しました。
指示を出すだけでClaude同士がやり取りし、実装からレビューまで完了させてくれる状態を目指した記録です。
1. サブエージェントとは何なのか
サブエージェントとは、メインの会話とは別に、独立したコンテキストウィンドウで動作する特化型のAIアシスタントです。
サブエージェントを使うことで、以下のようなメリットが得られます。
- メインの会話コンテキストを消費せずに調査や実装を行える(コンテキストの節約)
- レビューなど役割を特化させ、今までのやりとりに引きずられずに評価ができる
- 軽量なモデルを指定して処理させることでコストを抑えられる
また、Claude Codeにはあらかじめ組み込みのサブエージェントが用意されています。
代表的なものは以下の3つです。
| エージェント | モデル | トリガー(呼ばれるきっかけ) | 用途 |
|---|---|---|---|
| Explore | メイン会話から継承 | Claudeがコードベースの検索・理解が必要と判断したとき | 高速な読み取り専用のコードベース探索 |
| Plan | メイン会話から継承 | プランモード中、実装前にコードベースを理解する必要があるとき | 計画立案のための調査(読み取り専用) |
| general-purpose | メイン会話から継承 | 探索と実装の両方が必要な複雑なマルチステップタスクのとき | 複雑な調査・複数ステップの操作・コード変更 |
自分で定義したサブエージェントのことは「カスタムサブエージェント」と呼びます。
2. 作成するにはどうしたらいいのか
カスタムサブエージェントは、Markdownファイルのフロントマター(メタデータ)と本文(システムプロンプト)で構成されています。
フロントマターの主なフィールド
| フィールド | 必須 | 内容 |
|---|---|---|
| name | ○ | 小文字とハイフンの一意な識別子 |
| description | ○ | Claudeがこのサブエージェントに委譲するかどうかの判断材料 |
| tools | - | 使用を許可するツール(省略時は全て継承) |
| disallowedTools | - | 使用を禁止するツール |
| model | - | sonnet / opus / haiku / inherit など(デフォルトはinherit) |
| permissionMode | - | default / acceptEdits / plan など |
| maxTurns | - | 停止するまでの最大ターン数 |
| skills | - | 起動時にプリロードするスキル |
| mcpServers | - | このサブエージェント専用のMCPサーバー |
| hooks | - | ライフサイクルフック(PreToolUse等) |
| memory | - | 永続メモリのスコープ(user/project/local) |
| isolation | - | worktreeにするとgit worktreeで分離実行 |
必須なのはnameとdescriptionだけで、他は省略可能です。特にdescriptionは、メインスレッドが「このタスクはこのサブエージェントに任せるべきか」を自動判断する材料になるため、具体的に書くほど誤発火を防げます。
今回は、実装を担当するjava-developerと、そのレビューを担当するjava-reviewerの2つを作成しました。想定していた流れは以下の通りです。
指示を出す(私) → 実装(java-developer) → コミット(java-developer) → PR作成(java-developer)
→ レビュー(java-reviewer) → 指摘修正(java-developer) → マージ(java-reviewer)
3. 作成したけどうまくいかなかった
作成したサブエージェントを使って、こう指示を出しました。
未実装の箇所を洗い出し実装を進めてください。
すると、java-developerサブエージェントは使ってくれたものの、そこで止まってしまい、レビューへの受け渡しは行われませんでした。結局、「次はレビューして」と自分で指示を出す必要があり、想像していた「指示は最初の一回だけ」という理想になりませんでした。
原因を調べていくうちに、これは単に「フローを書き忘れていた」からではなく、サブエージェントの仕組み上そもそもできないと分かりました。
サブエージェントは、他のサブエージェントを呼び出す権限を持ちません(無限に入れ子になるのを防ぐための仕様上の制約です)。
つまり、java-developerの中に「実装が終わったらjava-reviewerを呼んで」と書いたとしても、java-developer自身がjava-reviewerを起動することはできません。進行管理(オーケストレーション)は常にメインスレッドが担う必要があった、というのが本当の原因でした。
4. サブエージェントについて知識を得た結果わかったこと
調べていく中で、サブエージェントを使うべき場面・避けるべき場面について整理できました。
使うべき場面
- メインの会話の文脈に引きずられず「まっさらな目」で評価してほしいとき(例:実装者とは別コンテキストでのコードレビュー)
- お互いが触るファイルが重複せず、独立して進められるとき(例:3つの独立したAPIを同時に実装する)
- 構造化されたワークフローの一部として使うとき(例:調査→計画→実装のように段階が分かれており、メインスレッドが進行管理役に徹し、各段階を割り振る設計にできる場合)
使わないほうがいい場面
- タスクが単純・日常的で、メインの会話でそのまま片付く
- 1つの機能の実装を、レイヤー単位(Entity/Service/Controllerなど)で複数サブエージェントに分割する。サブエージェントは互いの成果物を直接参照できないため、分割するとメインスレッドが「Aの成果物をBに伝える」という橋渡しを逐一行う必要が生じ、かえってコストが増える
- サブエージェントの仕組みを理解していない
「指示一回だけ」に近づけるには
私がやりたかった「一回の指示で最後まで完了する」を実現するには、サブエージェント側に書くのではなく、メインスレッドにオーケストレーションの指示を書く必要がありました。
まとめ
- サブエージェントは独立したコンテキストで動く特化型アシスタントで、コンテキストの節約・役割の特化に向いている
- サブエージェント同士は直接やり取りできない。進行管理は常にメインスレッド側の仕事
- 「指示一回で最後まで」を実現したいなら、サブエージェントの中身ではなく、メインスレッド側のオーケストレーション設計を作り込む必要がある
