この記事は、連載「AIの意見を聞かない技術」の第 4 回です。各回はファイルやスクリプトを一つ置けば完結します。連載の全体像と各回の一覧は序論にあります。

今回は、サブエージェントの話です。サブエージェントというのは、メインの会話を引き継がずに、別に立ち上がって働く AI のことです。私はある日、十数言語ぶんの翻訳を、1 言語につき 1 体ずつ、13 体のサブエージェントに任せました。渡したつもりの仕事を、13 体はそれぞれ、別々のやり方で進めていました。

この連載は、AI の提案を検討しないという話をしてきました。ただし、この回だけは逆のことを言います。サブエージェントに渡っている入力だけは、一度、確かめてください。

前作「読まない技術」の第 5 回では、この 13 体の事故を、出てきたログを読む側から書きました。今回は、渡す側から見ます。

まず一つだけ、開いてみてください。直近でサブエージェントに投げた仕事について、あなたが渡したプロンプトの全文です。使わせるスクリプトの名前は、そこに書いてあるでしょうか。成果物の合否を誰が決めるかは、書いてあるでしょうか。

たぶん、書いてありません。私も書いていませんでした。

CC BY 4.0

前作で、半分しか言えていなかったこと

前作の第 4 回で私はこう書きました——道具の説明文は、似た条件文が並ぶほど互いを食い合い、一番使ってほしいものほど埋もれる。だから条件を並べるのをやめた、と。

現象としては合っています。 実際、私の環境ではスクリプトが 84 本あって、スキル登録——カスタムコマンドやツールの登録——は 1 個です。

ただ、そこから「説明文は短いほどいい」と読める書き方をしてしまいました。これは間違いです。Anthropic が道具の定義について出しているガイダンスは、はっきり逆を言っています。極めて詳細な説明を書け。それが道具の性能を決める、いちばん重要な要素だと。求められている中身も具体的で、ほとんどマニュアルページです——何をするか、いつ使うか、そして使わないか、各パラメータの意味と挙動への影響、注意点や制限、そして返さない情報。最低でも 3〜4 文、複雑な道具ならもっと、とまで書いてあります。

矛盾しているように見えて、していません。線引きはここに引けます。呼び出しを決めるためのテキスト(どんなときに使うかの見出し)と、振る舞いを決めるためのテキスト(契約の中身)は別物です。前作で私が「増やすな」と言ったのは前者で、後者は増やすべきだった。

173 万トークンは、説明が足りなくて無駄に消えた

前作の第 5 回に書いた事故が、ちょうどこの話です。私の手元の本番プロジェクト——typingtube という、YouTube の音楽動画でタイピング練習ができる Web サービスです——で、十数言語ぶんの翻訳を 1 言語 1 体で任せ、13 体で約 173 万トークン使いました。

内訳を見ると、各エージェントが自分用の挿入スクリプトと検証スクリプトを書き直していました。スクラッチパッドに 12 本、ツール呼び出しは 1 体あたり 68〜97 回。

これは「使う条件」が曖昧だったから起きたのではありません。渡すもの、使う道具、どこまでが自分の仕事かという契約が、何も書かれていなかったから起きました。契約が無いと、サブエージェントは空白を自分で埋めます。埋め方は毎回違うので、13 体ぶん違うツールが生まれます。

理解したことは一文にできます。

増やしてはいけないのは「使う場面」の列挙で、厚く書くべきは契約——渡すもの、使わない場面、越えてはいけない境界。

仕組み: 用途カタログに、契約を書く

置くのは 1 枚です。AI に投げてよい仕事の一覧に、起動前に用意しておくものを並べます。サブエージェントを起動して仕事を割り振り、成果を受け取るメイン側の AI を、ここでは「中央」と呼びます。指揮を執る側です。一般の用語ではオーケストレーターです。

# サブエージェントを起動してよい用途のカタログ(実物からの簡約版)
#
# ⚠️ ここに無い用途では起動しない。要るのは「単純作業に落とし込める大量処理」だけ。
#   落とし込めていない仕事を投げると、各エージェントが自分用のツールを書き直して溶かす。
# ⚠️ requires は起動前に用意しておくもの。1 つでも無ければ hook が起動を止める。
#   これが「単純作業に落とし込んだか」の機械的な判定になっている。
# ⚠️⚠️ サブエージェントにテストを走らせない。並走ガードが 1 本しか通さないので、
#   メイン側と取り合ってどちらかが必ず落ちる。テストはメインセッションで回す。

session_total_checkpoint: 25   # ここを超えたら用途に関係なく一度止めて人に報告する

missions:
  i18n_translation:
    description: Backfill locale translations (1 agent = 1 language)
    requires:
      - scripts/i18n_ledger.py                     # 作業の一覧(渡す唯一の入力)
      - scripts/i18n_apply.py                      # 差し込みは中央で行う
      - scripts/i18n_verify.py                     # 受け入れテストも中央
      - docs/reference/i18n_translation_notes.md   # 言語別の注意点
    model: [sonnet, haiku]     # ⚠️ opus を既定にしない(訳文そのものは全体の 1 割未満)
    max_parallel: 5
    max_total: 20

description は 1 行です。増やしていないのはここ。厚いのはその下です。

そして、コメントのほうが本体より長くなりました。「ここに無い用途では起動しない」「落とし込めていない仕事を投げると無駄に消える」「テストを走らせない、理由はこう」。これが「使わない場面」で、さっきの man page の中身そのものです。禁止に理由を添える話は第 2 回でやりましたが、道具の説明文でも同じでした。

起動の直前に割り込むスクリプト(Claude Code なら hook)が、このカタログを見ています。判定は 5 つだけで、しかも回避できるかどうかが分かれています

#判定回避できるか
1用途名の宣言がある名前を書けば通る
2カタログにある用途か同上
3requires のファイルが実在する⚠️ 回避できない
4model が用途ごとの許可リストにある⚠️ 回避できない
5起動の記録の数(並列・総数・同名の再起動)別の名前で起動すればすり抜ける

回避できるほうを見て「ガードとして弱い」と思うかもしれません。逆です。1 と 2 は呼び出しを決めるテキスト、3 と 4 は契約で、効いているのは契約のほうだと、この表が示しています。requires に並んでいるのは仕事を単純作業に落とし込んだ人にしか作れないファイルなので、その実在が落とし込み済みかの判定になります。「読んだか」は測れませんが、「書いたか」は測れる。

そして、もう確かめません

一度確かめて、カタログに書いたら、そこで終わりです。以後は hook が毎回、私の代わりに確かめます。 起動のたびに requires が揃っているかを見て、欠けていれば止める。私は入力を開きません。

言わなくなったものも 1 つあります。「この作業はサブエージェントに向いていません」という説明です。以前は AI が起動しようとするたびに私が止めて、なぜ向かないかを話していました。いまはカタログに無ければ起動できず、無い理由はカタログの冒頭に書いてあります。同じ説明を、私が二度することはなくなりました。

確かめたのは一度だけです。その一度が、渡すものを教えてくれました。

注意: 全部の道具に契約を書くのではない

厚い契約が要るのは、間違えると高くつく入口だけです。私の環境ではテスト実行、コミット、サブエージェント起動の数か所。残りの 80 本近いスクリプトは素のままで、間違えてもすぐ分かって安く済みます。

ここを取り違えると、84 本ぶんの man page を書くことになり、前作が警告した「足すほど薄まる」を自分でやることになります。契約を厚くする対象は、契約が破られたときの損害で選んでください。

検証手順: requires を 1 つ消して、止まることを確かめる

前提

  • 用途カタログを 1 枚書き、起動の直前に割り込む hook を登録し終えていること
  • カタログに用途が 1 つ以上あり、その requires のファイルが実在していること

所要時間: 10 分

手順

  1. あなたが、その用途の requires に並んでいるファイルを 1 つ退避します。⚠️ 消すのではなく退避です。後始末で戻します
  2. 中央(サブエージェントを指揮する側の AI)に、その用途で起動させます

合格条件(すべて満たすこと)

  • 起動する前に止まる。⚠️ 起動してから失敗するのでは遅すぎます。渡した仕事の分のトークンは、もう使われています
  • 止まった理由に、欠けたファイルの名前が出ている
  • カタログの場所が案内されている

合格しなかったとき

  • 起動してしまった —— hook が見ているのは用途名だけで、requires の実在までは見ていません
  • 止まったが、カタログが案内されない —— 中央の AI は、次に何を用意すればよいか分からないまま止まります。案内の 1 行を足します

後始末

  • 退避したファイルを戻し、同じ用途でもう一度起動させます。止まらずに走れば終わりです

止める仕組みは「止まらない」方向に壊れます。一度も止まったことのない判定は、動いている判定と同じ見え方をします。どちらも、黙って起動を通すからです。


CC BY 4.0 はここまで

次回は「自動テストを書かない技術」。禁止事項の半分以上に、私は自動テストを書いていません。それでも、書いてあるだけの禁止は 1 つもありません。

連載「AIの意見を聞かない技術」