Claude Media
Agent SDKのサブエージェント定義 — プログラムかファイルかの使い分け

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つの定義方法の違い

agents パラメーター

プログラム定義

コード内でAgentDefinitionを渡します。同名のファイル定義より優先され、関数で実行時に組み立てられます。

.claude/agents/

ファイルベース定義

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. 1

    最初のクエリで session_id を控える

    ResultMessageからsession_idを取り出します。query()は呼ぶたびに新しいセッションを始めるため、同じセッションをresumeしないとサブエージェントの記録に届きません。

  2. 2

    Agentツール結果から agentId を取り出す

    完了時の結果に含まれるagentId: <id>というテキストブロックを、正規表現などで読み取ります。

  3. 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の違いで比べています。

この記事を共有:XはてブLinkedIn