AIエージェントに「判断は人間に確認して」と書いても実行されない — Claude Code サブエージェントの制約と、委譲設計のやり直し
はじめに
作業を任せる下位のAI(サブエージェント)を役割で分け、実行の直前に割り込んで止める仕組みまで作った話は、以前に二本書いた。
AIコーディングアシスタントに「やらせない」仕組みを作る — 助言から強制へ、そして関門に空いていた横穴
総合配信プラットフォームと銀行を、並行で作る — AIエージェントの「編成」に実機能を実装させ、実測で分業をチューニングした記録
この記事はその続きであり、同時に反転でもある。編成を組み、強制層まで敷いたその規律の中核に、下位のAIには物理的に実行できない一文が入っていた。 Claude Code の公式ドキュメントを読み直して原因を特定し、委譲の設計をやり直して検証したところまでを書く。
規律の中核にあった一文
下位のAIに与える運用ルールには、こう書いていた。「判断に迷ったら独断で決めず、本人に確認する」。もっともらしい一文だと思っていた。人間のチームでも同じことを言う。判断に自信が無いなら、勝手に進めず一言聞け、と。
だが下位のAIには、聞くための道具そのものが渡されていない。以下、公式ドキュメント(sub-agents・実測環境 Claude Code 2.1.215)から、その根拠を一つずつ確認する。
欠陥① 聞く手段が無い(本丸)
公式ドキュメントには、サブエージェントから無条件・条件付きの両方で取り上げられるツールの一覧がある。原文をそのまま引く。
The first filter removes these tools, even when listed in the
toolsfield:
Agent, when the subagent is at the depth limit; in a fork the tool stays listed but returns an error instead of spawningAskUserQuestionEndConversation, which can end only the main conversationEnterPlanModeExitPlanMode, unless the subagent’spermissionModeisplanScheduleWakeupTaskOutputWaitForMcpServersWorkflow
このリストは一様ではない。Agentは「階層の深さが上限に達した時だけ」という条件付きで、ExitPlanModeも「動作モードがplanでない限り」という条件付きだ。一方、本人に問いかけるための道具であるAskUserQuestionには、そうした条件が一切付いていない。下位のAIがどんな設定で動いていようと、toolsの許可リストに明記していようと、一律に取り上げられる。
つまり「迷ったら本人に確認する」という規律は、聞く道具を持たない相手に向けて書かれていた。相手にできることは、確認せずに自分で決めることしか無い。規律を厚くしたつもりが、独断を生む指示になっていた。
下位のAIが、作業中に判断に迷う
│
┌──────────────────┼───────────────────┐
▼ ▼ ▼
「本人に聞く」道具 結果をすぐ使いたいので さらに下位へ
を呼ぼうとする 直接応答を待つ実行を選ぼうとする 作業を任せようとする
│ │ │
▼ ▼ ▼
⛔ 公式仕様で ⛔ 自作の安全装置が拒否 (階層の深さに上限あり)
無条件に取り上げ済み (後述)
│ │
└────────┬─────────┘
▼
手段が無いので「自分で決める」
│
▼
その場の判断を長期記憶に書き残す
│
▼ ……(別のセッションで)
次に同じ場面に来た下位のAIが
「前例あり」として同じ判断を踏襲する
│
▼
同じ穴に、また落ちる(戻りエッジ)
欠陥② 結果を受け取れない
下位のAIに任せた作業の結果は、いつ返ってくるのか。公式の説明はこうだ。
As of v2.1.198, subagents run in the background by default.
Claude runs a subagent in the foreground when it needs the result before continuing.
A background subagent’s results reach Claude as a completion notification in a later turn.
「バックグラウンド」というのは、任せた側が結果を待たずに次へ進む実行方式で、「フォアグラウンド」はその逆——結果が出るまで待ってから次へ進む方式だ。既定はバックグラウンドで、結果は「後のターン(later turn)」に通知として届く。
問題は、任せる側自身が下位のAIだった場合に起きる。差配だけをする役のAIが、さらに下位へ作業を投げたとする。その報告は「後のターン」に届く。しかし差配役の「後のターン」は、差配役自身が一区切りついてターンを終えたあとにしか来ない。通知が届く先に、受け取る人がまだ誰もいない。
差配役の「今のターン」 ①下位のAIへ作業を投げる ──▶ 応答を返して終了
│
下位のAI ②バックグラウンドで作業を続ける
│
③完了・報告の準備ができる
│
差配役の「後のターン」 報告はここに届くはずだった
(届け先) しかし差配役は①の時点で自分のターンを
終えており、待機してすらいない
│
▼
何も起きないまま止まる
│
人間が気づいて起こす ─┘
(戻りエッジ:実際に3回、この形が起きた)
自分で作った安全装置が、公式の逃げ道を塞いでいた
この構造には、公式が用意した回避策がある。結果がすぐ要るなら、バックグラウンドでなくフォアグラウンドで走らせればいい。 上に引いた「Claude runs a subagent in the foreground when it needs the result before continuing」がそれだ。
だが実際には、この逃げ道は塞がれていた。数日前、下位のAIが長時間応答を返さない状態を防ぐために、実行の直前で割り込む安全装置(フック)を自分で入れていた。そのフックが、フォアグラウンド実行の指定そのものを無条件で拒否する判定になっていた。公式が「結果が要るならこうしろ」と定めている、まさにその手段を、自分の安全装置が塞いでいたことになる。
数週間前から、並行に走らせた下位のAIの多くが、完了の報告が無いまま止まる場面を繰り返し観測していた。だが、なぜ止まるのかを確かめないまま、運用は変えずに使い続けていた。壊れているのを知りながら原因を追わずに運用を続けるのが、いちばん危ない状態だった。
欠陥③ 申告を検証できない
差配役の定義には、状態を自分で確かめる道具(シェルコマンドを実行する権限)が入っていなかった。だから下位のAIが「やった」と申告してきたとき、差配役はそれを裏取りせずそのまま上へ流すしかない。
実際に、差配役から「ブランチを作成させた」という報告が来たことがある。実測すると、次のようになっていた。
$ git status --porcelain
(出力なし)
$ git branch --list <ブランチ名>
(出力なし)
$ git log --oneline develop..HEAD
(出力なし)
変更ファイルは無く、ブランチも作られておらず、コミットも無い。報告された「やった」に対応する痕跡が、どこにも無かった。 申告を検証する道具を持たない差配役を経由すると、この種の空振りがそのまま上に伝わる。
もう一つの発見 — 規律を置く場所を取り違えていた
原因調査の途中で、副産物のような発見があった。下位のAIが読み込む設定の範囲について、こちらの手元の理解が古いままだった。公式にはこうある。
…every level of the CLAUDE.md hierarchy the main conversation loads, including
~/.claude/CLAUDE.md
Explore and Plan are the only subagents that omit CLAUDE.md and git status. There is no frontmatter field or per-agent setting to change which agents skip them.
つまり、個々のプロジェクト向けの設定だけでなく、全プロジェクト共通の最上位設定まで、ほぼ全ての下位のAIに自動で届く。届かないのは調査・計画に特化した二種類の特殊な動作モードだけで、それを変える個別設定は存在しない。
ところが手元の設計メモは「下位のAIが読み込むのはプロジェクト単位の設定まで」としか書いておらず、この「全プロジェクト共通の設定まで届く」という一文が抜けていた。この抜けが、「共通の規律は下位のAIまで届かないから、一体ずつの定義ファイルに同じ文言を焼き込むしかない」という誤った前提を生んでいた。結果として、同じ注意書きを10個近い定義ファイルへ複製する肥大が起きていた。1か所に置けば、ほぼ全員に届いていたはずだった。
設計をやり直した
原因が三つとも公式仕様に根拠のある構造的な欠陥だと分かったので、対策も構造で入れた。
対策1: 安全装置の適用範囲を絞った。 呼び出し元が人と直接対話しているメインの対話なのか、下位のAIなのかを、呼び出し情報に含まれる識別子の有無で判定し、フォアグラウンド実行を拒否する条件を、メインの対話発の呼び出しに限定した。判定に使う手がかりは、他の通知の仕組みで既に使っていたものと揃えた。
guard 適用範囲テスト: PASS 15 / FAIL 0
正常系4件・敵対的な入力を含む異常系のケースまで通した上での結果で、下位のAIがフォアグラウンド実行を選べる経路が復活したことを確認した。
対策2: 全ての定義ファイルの冒頭に、実行できる選択肢を明記した。 「本人に確認できる」という前提そのものを外し、代わりにこう書いた——可能なのは (a)自分で決めて実行する (b)実行せず、報告して終了する の2つだけ。判断が絡む場面では必ず(b)を選ぶ。「確認する」という選択肢を規律から消し、「確認できないなら止まって報告する」に置き換えた。
対策3: 差配だけをする役を廃止した。 欠陥②・③の根はどちらも「差配役という一段が、結果を受け取れず・裏取りもできないまま存在していた」ことにある。役そのものを無くし、任せる側が直接、実行役と検証役を指揮する形に戻した。
リマインダー是正の検証: PASS 9 / FAIL 0
廃止した役を勧める記述が、設定のどこにも残っていないことを横断的に確認した。
実行できるかを先に確かめる
ここまでの三つの欠陥に共通するのは、規律を書いた時点で「相手がそれを物理的に実行できるか」を確かめていなかったことだ。「迷ったら聞け」は、聞く道具を持たない相手には「自分で決めろ」と同義になる。そしてその独断は、記録として残ればその場限りでは終わらない。判断の記録は長期記憶に載り、次に同じ場面に来た別の下位のAIが「前例がある」としてそのまま踏襲する。一度の解釈のずれが、構造を直すまで繰り返される。
禁止や許可のルールを書くときも、同じ落とし穴がある。今回のフックは「フォアグラウンド実行というパス」を塞ぐ形で書かれていて、「無応答を防ぐ」という本来の目的の外側まで巻き込んでいた。ルールは何を防ぎたいかで書くべきで、どの経路を触るかで書くと、後から正規に増えた経路まで一緒に殺してしまう。
もう一つ。壊れているのを見ていながら、原因を確かめずに使い続けていた期間があった。人がやってもAIが介在していても、これはいちばん危ない状態だ。観測した違和感は、次の作業に流用する前に、まず正体を確かめる。今回、実際にそれをやったことで、規律・安全装置・役割分担の三つを、同じタイミングで直すところまで進められた。
その後、この記事で引用した公式仕様を条件節まで一字ずつ読み直したところ、自分の引用のほうに1件の欠落が見つかった。詳細は以下に記録した。
Claude Code のオーケストレーター用サブエージェントを廃止した理由——公式harness仕様に基づく構造的な3つの制約