この記事は、連載「追わない技術」の第 4 回です。うまくいかない症状と、その対処を一つずつ並べています。各回はファイルやスクリプトを一つ置けば完結します。その仕組みがなぜ要るのかは、置いたあとに解説を読めば分かります。連載の全体像と各回の一覧は序論にあります。
今回は、いまの会話のAIだけで終えることのできる仕事を、サブエージェントに渡す話です。サブエージェントは、いまの会話とは別に起動され、指示された範囲の仕事だけをして、報告を返すAIです。サブエージェントを起動して仕事を渡す、いまの会話の AI(メインエージェント)を、ここでは本体と呼びます。どの仕事を渡すかを決める基準は、本体が持っているものは渡さない、ということです。
私は、サブエージェントを数多く起動していました。手元の記録では、342 回、サブエージェントに仕事を渡しています。
まず一つ思い出してみてください。
あなたが最後に仕事を任せたとき、任せた相手は、あなたがそれまでに読んだものを持っていましたか。
CC BY 4.0
342 回渡して、1 回あたり本体 16 手ぶんを新しく読み込ませていました
私は、その 342 回ぶんの記録から数えました。ここで 1 手と呼ぶのは、AI が 1 回応答するぶん、つまり Claude Code が送る 1 回のリクエストです。
ここで数えるのは「新しく読み込ませた量」だけにします。同じ会話を続けるあいだ、リクエストの大部分は、前のリクエストで送ったものと同じで、プロンプトキャッシュ(prompt caching)から読まれるからです。キャッシュから読めた量まで足すと、同じ文章を何十回も数えることになり、桁が変わります。
| 新しく読み込ませた量(中央値) | |
|---|---|
| 本体の 1 手 | 1,593 |
| 渡した相手の 1 手目 | 6,340(本体の 4.0 倍) |
| 渡した相手 1 回ぶんの合計 | 25,390(本体 16 手ぶん) |
1 回仕事を渡すと、本体で 16 手進むのと同じ量を、新しく読み込ませることになります。
そして渡した相手は、中央値で 8 往復して、報告を返してきました。1 往復は、渡した相手の側の 1 手です。8 往復ぶんの仕事で、本体 16 手ぶんを新しく読み込ませていたことになります。
往復の数をそろえて、1 往復あたりで比べたのが、次の表です。
| 1 往復あたり、新しく読み込ませた量 | |
|---|---|
| 本体 | 1,593 |
| 渡した相手 | 3,083 |
1.9 倍です。往復の数をそろえても、渡すと新しく読み込ませる量は約 2 倍になります。
2 倍になるのは、渡した相手が前提を持っていないからです
なぜ 2 倍になるのか。渡した相手は、本体がそれまでに読んで積み上げたもの(ここでは前提と呼びます)を持っていません。
同じ記録から、「キャッシュから読めた量」のほうも出しました。
| 1 手でキャッシュから読めた量(中央値) | |
|---|---|
| 本体 | 267,532 |
| 渡した相手の 1 手目 | 21,849 |
渡した相手の 1 手目にキャッシュから読めた量は、本体の 12 分の 1 です。本体は 1 手ごとに 26 万トークンぶんをキャッシュから読みながら進んでいて、新しく読み込ませていたのは 1,593 だけでした。割合にすると 99.1% がキャッシュからの読み出しです。
本体で続けるほうが新しく読み込ませる量が少ないことも、最初の応答が早いことも、この理由から来ています。
flowchart TD A["本体が積み上げた前提"] --> B["本体で続ける<br/>⭐ 前提をキャッシュから読める<br/>新しく読み込ませるのは<br/>直前のやり取りの分だけ"] A -.->|"引き継がれない"| C["渡した相手<br/>⚠️ 前提を持たずに始まる<br/>1 手目に 4 倍"] B --> D["1 往復 1,593"] C --> E["1 往復 3,083"]
最初の応答を返すまでも、同じ理由で長くかかっていました
| 秒(中央値) | |
|---|---|
| 本体の 1 手(ツールの結果が返ってから、次の応答を返すまで) | 6.7 |
| 渡した相手が、最初の応答を返すまで | 11.4 |
本体の 1 手と比べて、1.7 倍です。
渡すほうが得になる条件: 本体に足すのが報告だけで済むとき
| 中央値 | |
|---|---|
| 渡した相手が新しく読み込ませた量(1 回ぶんの合計) | 25,390 |
| その結果として、本体に足された量 | 3,244 |
本体に足された量は、渡した相手が新しく読み込ませた量の約 8 分の 1 です。渡した相手が読み込んだものは、渡した相手の側に残りますが、本体で同じ量を読むと、読んだものは全部、本体に足されます。
そして本体に足されたものは、そのあとの手でキャッシュから読まれます。キャッシュから読んだ部分にかかる費用は、安いとはいえ、0 より大きい値です。たとえば本体で 30 ファイルを読むと、本体は、そのあとの手で毎回、その 30 ファイルぶんをキャッシュから読みながら進むことになります。
どの仕事を、サブエージェントに渡さないか
試しに、セッションの上限に早く届かないように、調べものをサブエージェントに任せるようにしたとします。ところが、上限に届くまでの早さは、いつもと変わりませんでした。/usage の内訳では、使用量のうちサブエージェントの割合が大きく出ています。任せた仕事の多くは、いまの会話のAIがもう読み終えたファイルを使うものでした。今後どうするかを、あなたなら次のどちらにしますか。
- もっと多くの仕事を、サブエージェントに任せる
- 本体が持っている前提で足りる仕事は、本体で続ける
答えは、本体が持っている前提で足りる仕事は、本体で続けるほうです。サブエージェントのリクエストも、本体の会話のリクエストとは別に、使用量に数えられます。/usage の内訳に出ていたのがそれです。そして会話を引き継がないサブエージェントは、本体が読み終えたファイルを持たずに始まり、要るものを依頼の文から受け取るか、自分で読み直します。この回の記録では、渡した相手は 1 手目に本体の 1 手の 4.0 倍を、1 往復あたりでも本体の 1.9 倍を、新しく読み込ませていました。任せる仕事を増やすと、前提を持たずに始まる相手が、そのぶん増えます。本体で続ければ、読み終えたファイルはキャッシュから読めるので、新しく読み込ませるのは直前のやり取りの分だけです。サブエージェントに任せるのは、本体がまだ読んでいないものを大量に集めて、報告だけを持ち帰れば済む調べものに限ります。読んだものは相手の側に残り、本体に足されるのは報告だけです。
理解したことは一文にできます。
会話を引き継がないサブエージェントは、本体が積み上げた前提を持たずに始まるので、渡すと新しく読み込ませる量が増え、最初の応答も遅くなる。だから、本体がすでに持っている前提で足りる仕事は、渡さない。本体が持っていないものを集め、報告だけを持ち帰れば済む仕事を、渡す。
なぜ、渡した相手は、本体がもう読んだものを読み直すのか
Claude Code のサブエージェントのページによると、サブエージェントは、新しく隔離されたコンテキストで始まります。例外は、こちらの会話を引き継いで始めるフォーク(fork)です。フォークでも、フォーク自身のツール呼び出しは本体の会話に入らず、本体に戻ってくるのは最後の結果に限られます。フォーク以外のサブエージェントに見えるのは、起動のときに受け取るものだけです。受け取るのは、自分の system prompt と、本体が書いた依頼の文と、指示ファイルと、git の状態などです。本体の会話の履歴も、本体が読んだファイルも、この中には入っていません。本体がもう読んだものが仕事に要るなら、サブエージェントは、それを依頼の文から受け取るか、自分で読み直すことになります。
新しく読み込ませる量の差が出る理由は、プロンプトキャッシュがどう働くかにあります。Claude Code の prompt caching のページによると、モデルが読むのは、そのリクエストで送られてきたものだけです。Claude Code は毎回、system prompt と、プロジェクトの文脈と、それまでのメッセージとツールの結果の全部を、新しいメッセージと一緒に送り直します。新しい内容は末尾に足されるので、リクエストの大部分は 1 つ前のリクエストと同じです。API は、リクエストの先頭を、最近処理したもの(キャッシュ)と突き合わせ、一致した部分を処理し直さずに読み出します。本体の 1 手で 99.1% がキャッシュからの読み出しだったのは、本体の 1 手のリクエストが、1 つ前のリクエストの末尾に、直前の 1 手のやり取りを足しただけのものだからです。
同じページのサブエージェントの節によると、サブエージェントは、本体とは別の system prompt とツールの組で、自分の会話を始めます。先頭が違うので、サブエージェントの最初のリクエストは、本体のキャッシュを読むことができません。フォークは本体と同じ先頭で始まるので、最初のリクエストから本体のキャッシュを読みます。この回の記録の 342 回に、フォークはありません。渡した相手の 1 手目に新しく読み込ませた量は、本体の 4.0 倍でした。
時間の差も、1 手目に本体のキャッシュを読むことができないところから出ています。prompt caching のページは、キャッシュを読むことができずに入力を処理し直すリクエストは遅くなる、と書いています。渡した相手が最初の応答を返すまでには、本体の 1 手の 1.7 倍かかっていました。
渡すほうが得になる条件も、同じ仕様から出ています。Claude Code のベストプラクティスのページによると、Claude のコンテキストには、会話の全部が入り、Claude が読んだファイルも、コマンドの出力も、その中に残ります。Claude Code が毎回送り直すのは、このコンテキストです。Anthropic の料金のページは、キャッシュから読んだ部分の価格を、入力トークンの価格とは別の列に並べています。どのモデルの行でも、その価格は 0 より高く、入力トークンの価格より低く書かれています。
Claude Code のコストのページに書かれているのは、サブエージェントは本体の会話のリクエストに上乗せして自分のリクエストを送り、そのリクエストも使用量に数えられる、ということです。/usage の内訳に出るのが、そのサブエージェントの割合です。
サブエージェントのページは、量の多い出力はサブエージェントのコンテキストに残り、本体の会話に戻るのはサブエージェントの報告だけだ、と書いています。ベストプラクティスのページによると、Claude はコードベースを調べると多くのファイルを読み、読んだファイルはどれもコンテキストの一部を占めます。渡した相手が新しく読み込ませた量は 25,390 で、本体に足されたのは 3,244 でした。
検証手順: 渡した仕事の前提を、本体がもう持っていたかを、記録から確かめる
前提
サブエージェントを起動する手段と、その記録(新しく読み込ませた量)が要ります。記録が無いと、推測で数えることになります。数えるのは「新しく読み込ませた量」だけです。キャッシュから読めた量を足すと、同じ部分を何度も数えて桁が変わります。損得は、1 回の記録では決めず、同じ種類の仕事を複数回ぶんまとめて見て決めてください。
所要時間: 30 分(記録が手元にある場合。記録を残す設定から始めるなら、まず 1 日ぶん貯めるところから)
手順
- 直近の記録から、渡した相手 1 回ぶんの「新しく読み込ませた量」の合計を出します
- 同じ記録から、本体の 1 手あたりの「新しく読み込ませた量」を出します
- 手順 1 の数を、手順 2 の数で割ります。これが「1 回渡すと本体の何手ぶんか」です
- 渡した相手が実際に何往復したかを数え、手順 3 の数と比べます。往復の数より手順 3 の数のほうが大きければ、その差が、渡したことによる上乗せです
- 手順 1 の数と、その結果として本体に足された量を出します。手順 1 の数を足された量で割った比が大きいほど、渡す価値のあった仕事です
- 手順 5 の比が小さかった仕事を並べ、「本体がその仕事の前提をもう持っていたか」を 1 件ずつ確かめます
合格条件(すべて満たすこと)
合格の条件は 3 つです。1 つ目は、手順 3 の数が出ていることで、記録が無く推測になったなら、この手順は成立していません。2 つ目は、手順 4 で、往復の数と手順 3 の数の差を説明することができることです。説明がつかないなら、単位を取り違えています。3 つ目は、手順 6 で並べた仕事のうち、「本体がもう持っていた」に当たるものが 1 件以上あることです。0 件なら、渡し方はすでに正しいので、この回の話は要りません。
合格しなかったとき
- 1 回ぶんの量が、本体 1 手ぶんとほぼ同じ —— キャッシュから読めた量まで足しています。足すのは「新しく読み込ませた量」だけです
- 本体に足された量が、手順 1 の数とほぼ同じ —— 報告が長すぎます。渡す意味は、読んだものを渡した相手の側に残すことにあります
後始末
集計に使った中間ファイルを削除します。記録そのものは残してください。次に数え直すときの母数になるからです。数えた結果は日付つきで残します。記録は増え続け、同じ集計を明日やり直すと、数が変わるからです。
CC BY 4.0 はここまで
渡さなくなったもの
- 私は、本体がもう読んだファイルを、サブエージェントにもう一度読ませるのをやめました。渡した相手は本体が持っている前提を持たずに始まり、1 手目に本体の 1 手の 4 倍を新しく読み込ませます
- 「念のため並行で調べさせる」のをやめました。渡した 1 回ぶんで、中央値で本体 16 手ぶんを新しく読み込ませていました
- 読んだ中身をそのあと使う仕事を、渡すのをやめました。返ってくるのは報告だけなので、中身が要れば本体が読み直すことになります
- いまも渡している仕事は、本体がまだ読んでいない、大量の、報告だけを持ち帰れば済む調べものです。渡した相手が読み込んだものは相手の側に残り、本体に足された量は、中央値で 3,244 トークンでした
次回は「エージェントチームを使わない技術」。私は、AI どうしに話をさせる形を使っていません。 互いに送ったメッセージは 1 度で終わらず、以後ずっと両方の会話に残って読み直されるからです。