Agent SDKのサブエージェント定義 — プログラムかファイルかの使い分け
Agent SDKはサブエージェントをプログラムかファイルで定義できます。両者の違いと、実行時に動的生成する設計だけが持つ利点を解説します。
Agent SDKでサブエージェントを定義する2つの方法
Agent SDKでサブエージェントを作る方法は、コードの中でagentsパラメーターにAgentDefinitionを渡すプログラム定義と、.claude/agents/配下にMarkdownファイルを置くファイルベース定義の2つです。どちらも用意しなくても、Claudeは組み込みのgeneral-purposeサブエージェントをAgentツール経由で呼べます。プログラム定義はSDKアプリケーションで推奨される方法です。Agent SDKでエージェントを組む最小構成はClaude Agent SDK入門にあります。
3つの定義方法の違い
プログラム定義
コード内でAgentDefinitionを渡します。同名のファイル定義より優先され、関数で実行時に組み立てられます。
ファイルベース定義
Markdownで書き、Gitで共有します。対話セッションからも同じ定義を呼べます。
組み込み general-purpose
subagent_typeを省略したAgentツール呼び出しの受け皿です。環境変数で外せます。
サブエージェントを使う理由は、コンテキストの分離、並列実行、専門化した指示、ツール制限の4つです。サブエージェントは独立した会話として動くため、内部のツール呼び出しや結果は本体の会話に積み上がらず、最終メッセージだけが親に返ります。ドキュメントレビュー専用のサブエージェントにReadとGrepしか渡さなければ、本文を書き換えるリスクそのものが消えます。
これらの利点はファイルベース定義でも得られます。
AgentDefinitionではどんな項目を設定できるか
AgentDefinitionはdescriptionとpromptを必須フィールドとし、残りは任意です。
| フィールド | 型 | 必須 | 役割 |
|---|---|---|---|
| description | 型string | 必須必須 | 役割いつ使うかをClaudeに伝える説明文 |
| prompt | 型string | 必須必須 | 役割サブエージェントのシステムプロンプト |
| tools | 型string[] | 必須任意 | 役割許可ツール一覧(省略時は利用可能な全ツールを継承) |
| model | 型string | 必須任意 | 役割モデル上書き(opus / sonnet / haiku / fable / inherit、または完全なモデルID) |
| permissionMode | 型PermissionMode | 必須任意 | 役割このサブエージェント専用の権限モード |
permissionModeは、本体の会話がbypassPermissions・acceptEdits・autoのいずれかのとき無視され、サブエージェントも同じモードで動きます。サブエージェント側でbypassPermissionsを宣言しても、親のモードのまま動きます(v2.1.267以降)。
上記のほかにも、除外ツールを指定するdisallowedTools、起動時にプリロードするスキルを指定するskills、メモリー参照先をuser/project/localから選ぶmemory、MCPサーバーを個別指定するmcpServersが並びます。メインスレッドとして動くときの初回発話を決めるinitialPrompt、エージェントループの上限回数を決めるmaxTurns、バックグラウンド実行を強制するbackground、推論の強さを指定するeffortもあります。
omitClaudeMdをtrueにすると、サブエージェントはユーザー・プロジェクト・ローカルのCLAUDE.mdを読まずに動きます。管理ポリシーのファイルは読み込まれます。TypeScript Agent SDK v0.3.271以降が必要で、Python SDKのAgentDefinitionにはこのフィールドがありません。
Python SDKでは、disallowedToolsやmcpServersのような複数語のフィールド名もsnake_caseにならず、camelCaseのままです。ワイヤーフォーマットに合わせた例外です。
実行の既定にもバージョンによる違いがあります。v2.1.198以降、Agentツール呼び出しでrun_in_backgroundを省略するとバックグラウンド実行になり、Claudeが結果を先に必要とするときだけrun_in_background: falseを付けます。backgroundをtrueにすれば、Claudeの判断によらず常にバックグラウンド化できます。v2.1.198より前は、この既定への切り替えが段階的に進んでおり、省略すると同期実行になることがありました。maxTurnsに達した出力を「部分的」と明示するには、Claude Code v2.1.246以降が必要です。
サブエージェントは親から何を引き継ぐか
フォーク(元の会話を複製する方式)でない限り、サブエージェントのコンテキストウィンドウは白紙から始まりますが、完全に空ではありません。親から子へ明示的に渡るのは、Agentツールの呼び出し時に渡すprompt文字列だけです。必要なファイルパスやエラーメッセージ、それまでの判断は、そのプロンプトに直接書き込みます。
| サブエージェントが受け取るもの | 受け取らないもの |
|---|---|
自身のシステムプロンプト(AgentDefinition.prompt)とAgentツールの呼び出しプロンプト | 受け取らないもの親の会話履歴やツール実行結果 |
プロジェクトのCLAUDE.md(settingSources経由で読み込む設定。omitClaudeMdで外せる) | 受け取らないものAgentDefinition.skillsに列挙していないプリロード済みスキルの内容 |
ツール定義(親から継承するかtoolsで指定した部分集合) | 受け取らないもの親のシステムプロンプトそのもの |
本体セッションのextended thinking設定は、サブエージェントにそのまま引き継がれます。SendMessageツールを持つサブエージェントは、最初のターンでセッション内の他の名前付きエージェント一覧を自動で受け取ります(v2.1.206以降)。どの名前へ送れるかを知るためです。一覧は起動時点のスナップショットなので、後から名前が付いたエージェントは載りません。フォークは親の会話ごと引き継ぐので、この一覧を受け取りません。
サブエージェントを早期に終了させるAPIエラー(レート制限など)は、結果として親に届きません。親が見るのは正常に完了した最終メッセージだけです。
v2.1.210以降は、親が最終メッセージを読む前に、Claude Codeが指示の形をしたパターンを走査します。パターンの種類で扱いが違います。先頭に付く注記行の意味はサブエージェント出力スキャンの解説にまとめています。
最終メッセージの走査: 3種類の扱い
制御タグの模倣
<system-reminder>のようにハーネスだけが出すタグは、開き山括弧の後ろにバックスラッシュを挿入して無害化します。先頭に[harness: ...]の注記行が付きます。
権限設定への言及
.claude/settings.jsonやbypassPermissionsへの言及は書かれたまま残し、同じ注記行だけを追加します。
ターン境界の模倣
Human:やAssistant:で始まる行は、コロンの前にバックスラッシュを入れます。注記行は付きません。
テキストの削除や書き換えは、いずれの場合も行われません。サブエージェントが読んだ外部コンテンツ内の指示文が、親の会話に指示として混ざるのを防ぐ仕組みです。
同名の定義が複数あるとき、どれが使われるか
同じ名前のサブエージェントを両方の方法で定義した場合、プログラム定義が優先されます。.claude/agents/側の変更が無視され続ける事故は、両方を残したまま移行したときに起きます。ファイルベース定義の書き方はClaude Code Sub-agents完全ガイドにあります。
Claude Code本体(SDKではなくclaudeコマンド)側には、定義の置き場所ごとに優先順位があります。
| 優先順位 | 置き場所 | 範囲 |
|---|---|---|
| 1(最高) | 置き場所管理設定(managed settings) | 範囲組織全体 |
| 2 | 置き場所--agents CLIフラグ | 範囲現在のセッションのみ |
| 3 | 置き場所.claude/agents/ | 範囲現在のプロジェクト |
| 4 | 置き場所~/.claude/agents/ | 範囲自分の全プロジェクト |
| 5(最低) | 置き場所プラグインのagents/ | 範囲プラグインを有効にした場所 |
v2.1.287のclaude --helpでは、--agents <json-or-file>は「JSONオブジェクトでカスタムエージェントを定義する。--printと併用するときはJSONを収めたファイルのパスも渡せる」という趣旨の説明です。例として{"reviewer": {"description": "Reviews code", "prompt": "You are a code reviewer"}}が載っています。ファイルパスを渡す形はv2.1.281以降で、対話セッションではファイルパスが拒否されます。
同じディレクトリ配下の別ファイルが同じnameを宣言した場合は、ファイルシステムの読み込み順で1つだけが選ばれ、どちらが選ばれるかは決まっていません。名前は一意にします。
組み込みのgeneral-purposeは、agentsも.claude/agents/も用意しなくても、subagent_typeを省略したAgentツール呼び出しで使われます。SDKアプリケーションでこの既定を切りたい場合は、環境変数CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1を設定します。外れるのはgeneral-purposeだけでなく組み込みの全タイプで、自分で定義したものだけが残ります。
どちらの方法を選ぶかは、設定が実行前に確定しているかどうかで決まります。
| 状況 | 向く方法 | 理由 |
|---|---|---|
| リポジトリで共有し、対話セッションからも呼びたい | 向く方法ファイルベース | 理由Gitで追跡でき、対話環境でもそのまま読み込まれる |
| 実行時の入力に応じてプロンプトやモデルを変えたい | 向く方法プログラム定義 | 理由ファクトリー関数でAgentDefinitionを都度組み立てられる |
| SDKアプリ内で設定を1箇所のコードに閉じ込めたい | 向く方法プログラム定義 | 理由デプロイ物にMarkdownファイルの配布が不要になる |
動的にAgentDefinitionを生成すると何が変わるか
ファイルベース定義は起動前に確定した静的なMarkdownです。プログラム定義なら、AgentDefinitionを返す関数を書けます。公式の例は、セキュリティレビュー用のサブエージェントをstrictかbalancedかで作り分けるファクトリー関数です。厳格なレビューのときだけmodelをopusにし、緩やかなレビューではsonnetのままにする判断を、条件分岐でエージェント定義に反映できます。
呼び出しごとにテナントやユーザーの権限レベルが変わるマルチテナントのアプリでは、この動的生成がなければサブエージェントの数だけMarkdownを事前に用意します。組み合わせが増えるたびにファイルも増えます。
サブエージェントの呼び出しを検出し、再開する
Claudeはサブエージェントを常にAgentツール経由で呼びます。ツール名はv2.1.63でTaskからAgentに変わりましたが、system:initメッセージのツール一覧には今もTaskが残ります。検出コードはAgentとTaskの両方の値を見ます。
ストリームされるメッセージにparent_tool_use_idが付いていれば、サブエージェントの内部から来たメッセージです。
サブエージェントは完了後の任意の時点で再開できます。再開した側は、前回までのツール呼び出し・結果・推論を含む会話履歴をそのまま保持します。
サブエージェントを再開する手順
- 1
最初のクエリで session_id を控える
ResultMessageからsession_idを取り出します。query()は呼ぶたびに新しいセッションを始めるため、同じセッションをresumeしないとサブエージェントの記録に届きません。 - 2
Agentツール結果から agentId を取り出す
完了時の結果に含まれる
agentId: <id>というテキストブロックを、正規表現などで読み取ります。 - 3
2回目の query() で resume を渡す
resumeにsession_idを指定し、プロンプトにエージェントIDを含めて続きを頼みます。カスタムエージェントを再開するときは、1回目と同じ定義をagentsパラメーターに渡します。
組み込みのExploreとPlanは1回限りの実行でagentIdを返しません。再開が要る場面では、カスタムエージェントかgeneral-purposeを使います。サブエージェントの記録は本体の会話とは別のファイルに残り、独立して保たれます。
ファイルで定義したサブエージェントが読み込まれないとき
Claude Codeは~/.claude/agents/と.claude/agents/を監視し、新規・編集したファイルを数秒で拾います。再起動は要りません。定義が現れないときは、次の原因を上から確認します。
- 新しい
agentsディレクトリ: 監視対象はセッション開始時に存在したディレクトリだけです。ディレクトリを新規に作って最初の1ファイルを置いたときは再起動が必要で、最もよくある原因です - frontmatterの不備、または
nameの重複: YAMLを確かめ、既存のエージェントが同じnameを使っていないか見ます --disable-slash-commands: このフラグ付きで起動したセッションは監視を行わないので、新しいファイルには常に再起動が要ります--add-dir・/add-dir・additionalDirectories(Python SDKではadd_dirs)で足したディレクトリ: その.claude/agents/は読み込まれますが監視されないため、追加や編集のたびに再起動します.claude/agents/を直しても挙動が変わらない:query()に渡したagentsに同名の定義がないか見ます
Claudeがサブエージェントに任せず自分で作業してしまうときは、プロンプトでエージェント名を指定する、descriptionに使う場面を具体的に書く、の2つが公式の対処です。
まとめ — 静的に配るか、動的に組み立てるか
ファイルからagentsへ移すときは、.claude/agents/側の同名ファイルを消すか名前を変えてから移します。サブエージェントが増えたときの同時実行数・入れ子の深さ・支出の上限はAgent SDKのサブエージェント上限、permissionModeやツールの許可・拒否ルールはAgent SDKのパーミッション制御にあります。他社のフレームワークとの定義方式の違いはClaude Agent SDKとGoogle ADKの違いで比べています。