目次
少し前にサブエージェントを入れ子で呼べない制約に当たり、書いたラッパーを一度全部廃止したことがあります。その数日後、結局は薄いラッパーを残す方が運用が楽だと考え直して、agent-format.md に「1スキル1エージェント、本体は30行以内」と書き戻しました。揺れた末に着地した設計判断を、その理由ごと残しておきます。
サブエージェント本文に処理を書くと二重管理になる
サブエージェントとスキルを併用するなら、処理はスキル側に集約してサブエージェントはスキル名だけを書いた薄いラッパーにした方がいいです。両方に手順を書くと一次情報が2か所に増えて、片方を更新したときに必ずもう片方が古びるからです。
公式サンプルは本文にシステムプロンプトを直書きする
公式ドキュメント[1]の例を見ると、サブエージェントはフロントマターの後ろにシステムプロンプトを直接書く設計になっています。たとえば code-reviewer のサンプルは次のような形です。
---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---
You are a code reviewer. When invoked, analyze the code and provide
specific, actionable feedback on quality, security, and best practices.
本文部分そのものがサブエージェントの system prompt になっていて、呼び出されたとき Claude はこの本文を読んで動きます。サブエージェント単体で完結する設計で、スキルを使う想定はここには出てきません。
公式は同じページで「focused subagent を設計せよ」「各サブエージェントは1つのタスクに特化させよ」とも書いていて、本文に処理を書く前提でその処理を狭く保てというのが基本姿勢です[1:1]。
skill併用時に本文とSKILL.mdへ処理が散らばる
ここでスキルを併用しはじめると、サブエージェントの本文(=system prompt)と SKILL.md の両方に手順が書ける状態になります。サブエージェントには skills フィールドがあり、起動時に指定したスキルをプリロードできる[2]ので、技術的にはどちらにも処理を書けます。
これを素直にやると、たとえば「レビューを担当するサブエージェント」の本文に「最初に対象ファイルを読み、次にガイドと突き合わせ、違反があれば修正する」と書きながら、SKILL.md の側にも同じ手順が並ぶ、という状態になります。本体に書いた手順とスキル側の手順は当然ぴったり一致しないので、ガイドの参照先や手順の番号が微妙にずれていきます。
問題は、後から手順を直したいときに「どちらが正本か」を毎回考えなければならなくなることです。スキルを直してもサブエージェント本文がそのままだと、Claude は両方を読んで矛盾した指示の中から判断することになります。書き手の側も「どちらに書くべきか」で毎回迷います。
一次情報を1つに絞るならskill側へ寄せる
公式自身も SKILL.md の本文について「内容を簡潔に保て。何度も読み込まれる token cost なので、how や why を語るより what だけ書け」「500行を超えないようにし、詳細は別ファイルに分けろ」と推奨しています[2:1]。スキル側を意識的に短く保つ前提なら、サブエージェント本文を空けてスキルだけに処理を寄せた方が、長さ管理の責任も1か所にまとまります。
書き手から見た立て付けはこうです。サブエージェントファイル =「役割スロット」(誰が呼ばれるか・どんなツールを持つか)、スキルファイル =「中身」(具体的に何をやるか)。1つの役割に対して中身が1つだけある状態を保つと、片方を直せば自動的に整合する、というメンテナンス性が手に入ります。
1スキル1エージェント30行以内に保つ3つの理由
自分のローカルルール agent-format.md に書いた根拠は、二重読み防止・引き返しやすさ・役割スロットの3つです。
二重読み防止のため処理をskillに集約する
1つ目は前章で書いた通りで、サブエージェント本文と SKILL.md の両方に処理を書けると、必ずどこかで乖離します。サブエージェント側を「/<skill-name> を実行する」の一行に近い状態まで削り、ガイド参照・手順・結果フォーマットを全部スキル側に寄せれば、更新時に見るべき場所が1か所に確定します。
1対1の縛りも同じ動機です。1つのサブエージェントが複数スキルを呼べる設計にすると「どのスキルにどの手順を書いたか」が曖昧になります。役割と中身を1対1に固定すると、「reviewer は /review-post の中身しかしない」と読み手も書き手も即断できます。
ラッパーが薄いと改名・廃止・新設が軽い
2つ目は実体験ベースの理由です。サブエージェントから別のサブエージェントを呼べない制約に当たったとき、自分は一度ラッパーを全部廃止する判断をしました。コミットメッセージには「subagent ネスト不可問題に対応してラッパー廃止」と書いてあります。
その後、薄いラッパーは残した方が役割スロットとして機能することに気づいて、書き戻しました。ここで効いたのが「ラッパーが薄い」という性質です。エージェントファイルの中身が30行以内のスキル参照だけだったので、消すのも書き戻すのもファイル単位の移動で済みました。もしサブエージェント本文に system prompt として手順を全部書いていたら、廃止のたびに「この知識をどこに退避させるか」を考える必要があり、書き戻しのときもスキルから手順を逆輸入する作業が要ったはずです。
役割スロットだけが薄く動く構造にしておくと、設計を間違えても引き返しやすい、というのは試行錯誤のコストとして大きいです。
役割スロットとしてsubagentを機能させる
3つ目は、自分が別ルールとして持っている role-separation.md との接続です。そこでは「学習源を分ける」という発想で、Writer は基準だけを見る、Reviewer は基準と対象だけを見る、Updater はユーザー発言だけを学習源にする、という役割分離を取っています。
サブエージェントとスキルを1対1にすると、サブエージェントファイルがそのまま「役割スロット」になり、スキルが「その役割の中身」になります。たとえば outliner.md(サブエージェント) は「アウトラインを担当する者」というスロット定義で、/outline-post(スキル) がそのスロットの中身を提供する、という構造です。スロットと中身を分けると、後から中身だけを差し替えたり、同じ中身を別スロットから呼んだりが軽くできます。
公式ドキュメントは「ネストが必要なら skill を使うか、main conversation から chain せよ」[1:2]とも書いていて、サブエージェントはあくまでフラットに並ぶ前提の機能です。だからこそ役割スロットとして単純化しておく方が、Claude Code のアーキテクチャと噛み合います。
実測すると7エージェント全部21〜23行に収まる
自分のリポジトリで運用しているサブエージェントの行数は、次の通りです。
| ファイル | 行数 |
|---|---|
.claude/agents/analyzer.md |
21 |
.claude/agents/drafter.md |
21 |
.claude/agents/guide-updater.md |
23 |
.claude/agents/outliner.md |
21 |
.claude/agents/planner.md |
22 |
.claude/agents/researcher.md |
21 |
.claude/agents/reviewer.md |
21 |
7ファイルすべてが21〜23行に収まっています。30行の上限に対しても余裕があり、今のところ「足りなくて困った」場面は出ていません。
実際のreviewer.mdの中身
ファイルの中身は、たとえば reviewer.md だとこれだけです。
---
name: reviewer
description: 記事をガイドに照らしてチェックし、違反箇所を修正する subagent。/review-post スキルを実行する。/write-post の第5段、または /rewrite-post の最終段で呼ばれる。
tools: Read, Edit, Bash
---
# Reviewer Subagent
`/review-post` スキルを実行する。
## 入力
親から `permalink`(レビュー対象記事のファイル名)を受け取る。
## 処理
`/review-post` スキル(`.claude/skills/review-post/SKILL.md`)の手順に従って動く。入力・処理方針・守備範囲・結果フォーマットは全てスキル側に記載。
## 親会話に返すもの
`/review-post` の Step 7「結果報告」をそのまま返す。
書いてあるのは「名前」「説明」「使えるツール」「対応するスキル名」「入力」「親に返すもの」だけで、レビューの中身は一切登場しません。レビューの手順を直すときは .claude/skills/review-post/SKILL.md だけを開けば済む、という状態です。
toolsフィールドはallowlistなので整合が要る
薄いラッパーにしても、注意点が1つあります。サブエージェントの tools フィールドは allowlist で、ここに書いていないツールはスキル側の allowed-tools に書いてあっても呼び出せません[1:3]。たとえばスキルが Bash(git *) を必要としているのにサブエージェント側の tools に Bash を入れ忘れると、スキル実行の途中で止まります。
薄くする方針はそのままで、tools の最低限の整合だけは手で取る必要があります。自分の場合は「サブエージェント側は Read, Edit, Bash のような大枠で書き、引数制限はスキル側の allowed-tools に書く」というルールに揃えています。
この記事と同じ時期に書いたサブエージェントを入れ子で呼べない制約の話は、まさにラッパー設計を一度諦めかけた制約側の話です。制約のもとで「薄いラッパーに戻った」設計判断のもう片側として、合わせて読むと文脈が繋がりやすいと思います。
Create custom subagents - Claude Code Docs (2026-05-28 アクセス) ↩︎ ↩︎ ↩︎ ↩︎
Extend Claude with skills - Claude Code Docs (2026-05-28 アクセス) ↩︎ ↩︎
