Agent SDKのサブエージェント定義 — プログラムかファイルかの使い分け
Agent SDKはサブエージェントをプログラムかファイルで定義できます。両者の違いと、実行時に動的生成する設計だけが持つ利点を解説します。
Agent SDKでサブエージェントを定義する2つの方法
Agent SDKでサブエージェントを作る方法は実質2つです。コードの中でagentsパラメーターにAgentDefinitionを渡すプログラム定義と、.claude/agents/配下にMarkdownファイルを置くファイルベース定義です。加えて、どちらも定義しなくてもClaudeは組み込みのgeneral-purposeサブエージェントをAgentツール経由で呼び出せます。公式ドキュメントはプログラム定義を「SDKアプリケーションで推奨される方法」と位置づけています。Agent SDKでエージェントを組む最小構成はClaude Agent SDK入門で扱っています。
サブエージェントを使う理由は4つに集約されます。1つ目はコンテキストの分離です。サブエージェントは独立した会話として動くため、内部のツール呼び出しや結果は本体の会話に積み上がらず、最終メッセージだけが親に返ります。2つ目は並列実行です。独立したサブタスクを同時に走らせれば、全体の所要時間は一番遅いタスク1つ分で済みます。3つ目は専門化した指示です。サブエージェントごとに固有のシステムプロンプトを持たせられるので、本体のプロンプトを肥大化させずに専門知識を注入できます。4つ目はツール制限です。ドキュメントレビュー専用のサブエージェントにReadとGrepしか渡さなければ、誤って本文を書き換えるリスクそのものが消えます。
これらの利点自体はファイルベース定義でも得られます。プログラム定義が独自に持つのは、実行時の条件でエージェント定義そのものを組み立てられる能力です。この違いが両者の使い分けを決めます。
AgentDefinitionではどんな項目を設定できるか
AgentDefinitionはdescriptionとpromptを必須フィールドとし、複数の任意フィールドで細部を制御します。
| フィールド | 型 | 必須 | 役割 |
|---|---|---|---|
| description | 型string | 必須必須 | 役割いつ使うかをClaudeに伝える説明文 |
| prompt | 型string | 必須必須 | 役割サブエージェントのシステムプロンプト |
| tools | 型string[] | 必須任意 | 役割許可ツール一覧(省略時は利用可能な全ツールを継承) |
| model | 型string | 必須任意 | 役割モデル上書き(opus / sonnet / haiku / fable / inherit等) |
| permissionMode | 型PermissionMode | 必須任意 | 役割このサブエージェント専用の権限モード |
上記のほかにも、除外ツールを指定するdisallowedTools、起動時にプリロードするスキルを指定するskills、メモリー参照先をuser/project/localから選ぶmemory、MCPサーバーを個別指定するmcpServers、そのサブエージェントがメインスレッドとして動くときの初回発話を決めるinitialPrompt、エージェントループの上限回数を決めるmaxTurns、バックグラウンド実行を強制するbackground、推論の強さを指定するeffortが並びます。
Python SDKではdisallowedToolsやmcpServersのような複数語のフィールド名も、Pythonの慣習であるsnake_caseではなくワイヤーフォーマットに合わせたcamelCaseのまま残ります。TypeScript側の型定義とプロトコル上のフィールド名を一致させるための例外です。
バージョンによる挙動変化も押さえておく必要があります。Claude Code v2.1.198より前は、Agentツール呼び出しでrun_in_backgroundを省略するとサブエージェントは同期実行されていました。v2.1.198以降は省略時にバックグラウンド実行が既定になり、Claudeが結果を先に必要とする場合だけrun_in_background: falseを明示します。backgroundフィールドをtrueにすれば、Claudeの判断によらずそのサブエージェントを常にバックグラウンド化できます。加えてv2.1.198からは、サブエージェントが本体セッションのextended thinking設定をそのまま継承するようになりました。maxTurnsに達したときの出力を「部分的」と明示するマーキングは、Claude Code v2.1.246以降が必要です。
サブエージェントは親から何を引き継ぐか
フォーク(元の会話を複製する方式)でない限り、サブエージェントのコンテキストウィンドウは白紙から始まります。ただし完全に空ではありません。親から子へ明示的に渡るのはAgentツールの呼び出し時に渡すprompt文字列だけなので、サブエージェントに必要なファイルパスやエラーメッセージ、それまでの判断はそのプロンプトに直接書き込む必要があります。
| サブエージェントが受け取るもの | 受け取らないもの |
|---|---|
自身のシステムプロンプト(AgentDefinition.prompt)とAgentツールの呼び出しプロンプト | 受け取らないもの親の会話履歴やツール実行結果 |
プロジェクトのCLAUDE.md(settingSources経由で読み込む設定) | 受け取らないものAgentDefinition.skillsに列挙していないプリロード済みスキルの内容 |
ツール定義(親から継承するかtoolsで指定した部分集合) | 受け取らないもの親のシステムプロンプトそのもの |
SendMessageツールを持つサブエージェントは、最初のターンでセッション内の他の名前付きエージェント一覧を自動的に渡されます。どの名前へメッセージを送れるかを知るためで、Claude Code v2.1.206以降が必要です。フォークされたサブエージェントは親の会話をそのまま引き継ぐため、このリストは受け取りません。サブエージェントを早期に終了させるAPIエラー(レート制限など)は、結果として親に届くことはありません。親が見るのは正常に完了した最終メッセージだけです。
もう一つ見落としやすいのが、Claude Code v2.1.210以降で入った最終メッセージのスキャン処理です。サブエージェントの出力を親が読む前に、Claude Codeはハーネスだけが発行するはずの制御タグ(<system-reminder>など)の模倣、.claude/settings.jsonやbypassPermissionsのような権限設定への言及、Human:やAssistant:で始まるターン境界の模倣、という3種類のパターンを走査します。制御タグの模倣にはバックスラッシュを挿入して無害化したうえで[harness: ...]という注記行を先頭に追加し、権限設定への言及はそのまま残しつつ同じ注記行を追加します。ターン境界の模倣にはコロンの前にバックスラッシュを入れるだけで、注記行は追加されません。いずれの場合もテキストの削除や書き換えは行われません。この仕組みは、サブエージェントが読み込んだ外部コンテンツに紛れ込んだ指示文が、親の会話に指示として混入するのを防ぐためのものです。
プログラム定義とファイルベース定義はどう共存するか
同じ名前のサブエージェントを両方の方法で定義した場合、プログラム定義が優先されます。公式ドキュメントは「同名のプログラム定義はファイルベース定義に優先する」と明記しており、意図しない上書きに気づかず両方を残してしまうと、.claude/agents/側の変更が無視され続ける事故につながります。ファイルベース定義の書き方や利用可能なツールの詳細はClaude Code Sub-agents完全ガイドにまとめています。
組み込みのgeneral-purposeサブエージェントは、agentsも.claude/agents/も用意しない状態でも、Claudeがsubagent_typeを指定せずにAgentツールを呼べば使われます。SDKアプリケーションでこの既定を切りたい場合は環境変数CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1を設定します。この場合、subagent_typeを省略したAgentツール呼び出しは失敗し、エラーメッセージは利用可能な残りのサブエージェント種別を列挙します。
どちらの方法を選ぶかは、設定が実行前に確定しているかどうかで決まります。
| 状況 | 向く方法 | 理由 |
|---|---|---|
| リポジトリで共有し、対話セッションからも呼びたい | 向く方法ファイルベース | 理由Gitで追跡でき、対話環境でもそのまま読み込まれる |
| 実行時の入力に応じてプロンプトやモデルを変えたい | 向く方法プログラム定義 | 理由ファクトリー関数でAgentDefinitionを都度組み立てられる |
| SDKアプリ内で設定を1箇所のコードに閉じ込めたい | 向く方法プログラム定義 | 理由デプロイ物にMarkdownファイルの配布が不要になる |
動的にAgentDefinitionを生成すると何が変わるか
ファイルベース定義は起動前に確定した静的なMarkdownです。プログラム定義がその上にもたらす価値は、AgentDefinitionを返す関数を書けるという一点に尽きます。公式ドキュメントが挙げる例は、セキュリティレビュー用のサブエージェントをstrictかbalancedかで作り分けるファクトリー関数です。厳格なレビューのときだけmodelをopusに切り替え、緩やかなレビューではsonnetのままにする、という判断をコードの条件分岐でそのままエージェント定義に反映できます。呼び出しごとにテナントやユーザーの権限レベルが変わるマルチテナントのSDKアプリケーションでは、この動的生成がなければサブエージェントの数だけMarkdownファイルを事前に用意することになり、組み合わせが増えるたびにファイルが増殖します。
もう一つプログラム定義側に閉じているのが、サブエージェントの呼び出し検出と再開の仕組みです。Claudeはサブエージェントを常にAgentツール経由で呼び出します。ツール名はClaude Code v2.1.63でTaskからAgentに変わりましたが、system:initメッセージのツール一覧やresult.permission_denials[].tool_nameには今もTaskという文字列が残るため、両方の値を確認するコードでないと古いSDKとの互換性が壊れます。ストリームされるメッセージにparent_tool_use_idが付いていれば、そのメッセージはサブエージェントの内部から来たものだと判定できます。
サブエージェントは完了後の任意時点で再開できます。完了時のAgentツール結果にはagentId: <id>というテキストブロックが含まれ、これとsession_idを控えておけば、2回目のquery()呼び出しで同じセッションをresumeしながら該当エージェントIDをプロンプトに含めて続きを聞けます。ただし組み込みのExplore・Planサブエージェントは1回限りの実行でagentIdを返さないため、再開が必要な場面ではカスタムエージェントかgeneral-purposeを使う必要があります。ファイルベース定義のサブエージェントでも同じ再開の仕組みは使えますが、再開時に渡すエージェント定義自体をプログラムから動的に組み立てられる点は、やはりプログラム定義側の強みです。
まとめ — 静的に配るか、動的に組み立てるか
Agent SDKのサブエージェント定義は、配布と共有を優先するならファイルベース、実行時の条件でエージェントそのものを作り分けるならプログラム定義、という軸で選べます。同名の定義がある場合はプログラム定義が常に勝つため、移行期に両方を残す設計は避けたほうが事故が少なくなります。サブエージェントが増えるほど気になる同時実行数や入れ子の深さ、支出の上限はAgent SDKのサブエージェント上限、ツールの許可・拒否ルールやpermissionModeの詳細はAgent SDKのパーミッション制御にまとめています。
よくある質問
同じ名前のサブエージェントをプログラムとファイルの両方で定義したらどうなりますか
プログラム定義が優先されます。.claude/agents/側の同名ファイルは読み込まれず、コード側のAgentDefinitionだけが有効になります。
組み込みのgeneral-purposeサブエージェントを無効化するとどうなりますか
環境変数CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1を設定すると、subagent_typeを指定しないAgentツール呼び出しが失敗するようになります。エラーメッセージには利用可能な残りのサブエージェント種別が列挙されます。
サブエージェントに渡すtoolsを省略するとどうなりますか
サブエージェントに利用可能な全ツールを継承します。逆に一覧を指定すると、そこに無いツールはサブエージェントのセッションにそもそも存在しない扱いになり、権限プロンプトもエラーも出ません。
ファイルベースで定義したサブエージェントの変更が反映されません
新規に作ったディレクトリはセッション開始後のファイル監視の対象外なので、そのディレクトリで最初の1ファイルを作った直後はセッションの再起動が必要です。加えて--disable-slash-commands付きで起動したセッションはこの監視自体を行いません。
サブエージェントは自分でさらにサブエージェントを起動できますか
既定では可能です。ネストの深さや同時実行数、支出をコード側から制御する方法はAgent SDKのサブエージェント上限で扱います。