【第1回】サブエージェントをはじめよう——Claude Code に「別の自分」を呼び出させる
Claude Code のサブエージェント機能を初めて使った体験記。スキル・エージェント・Taskの違い、Hello World、カスタムエージェント作成まで
Claude Code を使っていると、会話が長くなるにつれてコンテキストが積み上がっていきます。検索結果や中間成果物が溜まって、本筋がどこにあるのかわからなくなる。
「この調べ物、別のところでやってほしいな」——そう思うことが増えていました。
サブエージェントという機能があるのは知っていました。でもスキルとエージェントとTaskの違いがわからない。ドキュメント(スキル、エージェント)を読んでも、どちらも .claude/ 以下にマークダウンを置くだけに見える。何が違うのか。
読んでもわからないなら、触ってみよう。Claude Code に相談するところから始めました。
この連載では、12回の実験を全4回でお届けします。
- 第1回(今回): Claude Code と対話しながら Hello World、カスタムエージェント作成まで
- 第2回: エージェントの性質——戻り値・並列・同期非同期・ネスト
- 第3回: 権限モデルを理解する——Write vs Edit の罠、同期/非同期の落とし穴
- 第4回: エージェントを使いこなす——スキル→エージェント委任パターンとベストプラクティス
「ネタ探しエージェントを作りたい」
私の Obsidian には、長年の読書メモや実験記録が約3万件あります。以前、kbsearch という CLI ツールを作って横断検索できるようにしました(詳しくは前の記事に書きました)。
私: obsidian の kbsearch を使って yatmita のネタを探してくれるエージェントを作ってみたい。それで記事にする。
Claude Code: エージェントの形式は?Claude Code のスキル/エージェント(
.claude/agents/)として作る?それともスタンドアロンのスクリプト?
私: なんかサブエージェントの使い方がよくわからないんだけど、別のプロジェクトではうまく動かなくて結局汎用エージェントにスキルを使わせるように指示した。
Claude Code: じゃあ今回もスキル版で?
私: いやせっかくだから両方試してみよう。
エージェントとスキル、両方作って比較する。その過程を記事にする——という方針が決まりました。
Hello World:「それは汎用エージェントじゃないの」

私: まずサブエージェントのハローワールドをやって。
Claude Code が general-purpose エージェント(組み込みの汎用タイプ)で kbsearch を叩いてくれました。約21秒で結果が返ってきた。KB 一覧取得、obsidian vault での「スムージー」検索、3件ヒット。
私: それは汎用エージェントじゃないの。
Claude Code: その通りです。今のは
general-purposeサブエージェントに指示を渡しただけで、カスタムエージェント(.claude/agents/)ではありません。
汎用エージェントは動いた。でも私が作りたいのは「kbsearch でネタを探す」という専門知識を持った、カスタムのエージェントです。
カスタムエージェントを作る
Claude Code が .claude/agents/neta-research-agent.md を作ってくれました。
---
name: neta-research-agent
description: yatmita の記事ネタを Obsidian ナレッジベースから探す
tools: Bash, Read, Glob, Grep, Write
maxTurns: 20
---tools や maxTurns が何を意味するかは、この時点ではよくわかっていません。「とりあえずこう書くものらしい」程度の理解です。各フィールドの意味は第3回で改めて検証します。
本文には kbsearch の使い方、出力フォーマット、ネタ候補に必須の項目を書きました。この本文がそのままエージェントのシステムプロンプトになります。
呼んでみます。
認識されません。
Claude Code: カスタムエージェントが認識されていません。セッション中に作成したエージェントは再起動しないと読み込まれない可能性があります。
公式ドキュメントにも「Subagents are loaded at session start」と書いてあります。完全に見落としていました。後で調べると、同じところでつまずいている人が何人もいました。
セッションを再起動します。
Claude Code: 再起動後、
neta-research-agentがエージェント一覧に出ています。呼んでみます。
今度は動きました。kbsearch を自律的に叩き、「料理 科学」で検索して「旨味の相乗効果」の記事ネタを提案してきた。汎用エージェントと違って、出力がきちんと構造化されています。
ここで、通信の仕組みが気になりました。
私: カスタムエージェントとメインとはどうやって通信するの?
Claude Code: Agent ツールの prompt(入力)と return(出力)だけです。途中経過は見えない。結果だけ返ってくる。つまり「関数呼び出し」に近い。
私: 非同期で返ってくるの?
Claude Code:
run_in_background: trueを付ければ非同期で、付けなければ同期です。
私: 並列化は?
Claude Code: 1つのメッセージで複数の Agent を同時に呼べます。やってみますか?
私: じゃあ並列処理してみて。
3ドメイン(smoothie, buddhaism, labo)でネタ探しを並列起動——したのですが、ここで壁にぶつかります。
最初の壁: 権限エラー
3つのエージェントを並列で起動したら、全員が同じエラーで帰ってきました。
neta-research-agent: Bash ツールの実行権限が現在拒否されています。
kbsearchコマンドを実行するには Bash ツールの許可が必要です。
さっき汎用エージェントでは動いたのに。なぜ。
しばらく悩んで、気づきました。汎用エージェントで動いたのは、そのセッションで既に kbsearch の使用を手動承認していたからでした。セッションを再起動したら承認は白紙に戻る。
.claude/settings.local.json の permissions.allow に Bash(kbsearch:*) を追加して、再度並列起動。今度は3つとも成功しました。
{
"permissions": {
"allow": ["Bash(kbsearch:*)"]
}
}この時点では「settings.local.json に書けば解決するのか、なるほど」程度の理解でした。権限まわりにはもっと深い罠があるのですが、それは第3回で嫌というほど体験します。
動いた。そして疑問が増えた
カスタムエージェントが kbsearch を自律的に使って、ネタ候補を提案してくれました。しかも3ドメイン並列で。
「別の自分に仕事を頼む」感覚が、ようやく現実のものになりました。
同時に疑問も増えました。戻り値はテキストだけ? エージェントの中からさらにエージェントを呼べる? 同期と非同期で何が変わる?
次回: エージェントの性質を検証する
第2回では、エージェントの動作を実験で詳しく調べます。
戻り値の受け取り方、並列実行、フォアグラウンド/バックグラウンドの違い、ネスト呼び出しの挙動——実際に試してみると、予想通りにいかないことがいくつかありました。特にネスト呼び出しの結果は衝撃的でした。1時間近くハングしました。
「別の人に仕事を頼む」の比喩がどこまで成立するか、次回明らかにします。