CLAUDE LABEN
BILLING — 6/15の課金変更まで残り1日。Agent SDK・headless・GitHub Actions・他社エージェントが別枠の月次クレジット($20/$100/$200・full APIレート・繰越なし)へ移行しますFABLE5 — Claude Fable 5(Mythosクラス)は一般提供で最高性能とされ、Claude Code v2.1.170以降で利用できます(6/9公開)SUBAGENTS — Claude Codeでサブエージェントがさらにサブエージェントを生成可能に。モデル・リージョン選択もより賢くなりましたENTERPRISE — Enterpriseのカスタムロールにadmin権限を追加。Owner権限なしで請求・プライバシー設定へアクセスできますPLUGINS — プラグイン検索とChrome・VSCode・ターミナル連携が改善。セッション・メモリ・権限まわりの不具合も修正されましたUI — フルスクリーン時のマウスホイール加速を無効化する設定を追加。/modelピッカーがモデルファミリーを正しく表示するよう修正BILLING — 6/15の課金変更まで残り1日。Agent SDK・headless・GitHub Actions・他社エージェントが別枠の月次クレジット($20/$100/$200・full APIレート・繰越なし)へ移行しますFABLE5 — Claude Fable 5(Mythosクラス)は一般提供で最高性能とされ、Claude Code v2.1.170以降で利用できます(6/9公開)SUBAGENTS — Claude Codeでサブエージェントがさらにサブエージェントを生成可能に。モデル・リージョン選択もより賢くなりましたENTERPRISE — Enterpriseのカスタムロールにadmin権限を追加。Owner権限なしで請求・プライバシー設定へアクセスできますPLUGINS — プラグイン検索とChrome・VSCode・ターミナル連携が改善。セッション・メモリ・権限まわりの不具合も修正されましたUI — フルスクリーン時のマウスホイール加速を無効化する設定を追加。/modelピッカーがモデルファミリーを正しく表示するよう修正
記事一覧/Claude Code
Claude Code/2026-06-14上級

サブエージェントの出力を採点し、不合格なら作り直させる SubagentStop フック

Claude Code のサブエージェントが時々ルール違反の成果物を返す問題に、SubagentStop フックで自動採点と再依頼を組み込む方法を、動くコードと運用所感つきで整理します。

Claude Code149サブエージェント8フック2品質ゲート3自動化50

サブエージェントに定型作業を任せると、9割はきれいに仕上がるのに、1割だけ約束したルールを外した成果物が混じる——これが一番厄介でした。個人開発で複数のサイト(Dolice Labs)を回している私の場合は、記事生成の下請けを別エージェントに切り出していますが、「禁止語が入る」「見出しの数が足りない」といった違反が、忙しい日ほど見逃されてマージされてしまいます。

手で全部レビューすれば防げますが、それでは委譲した意味がありません。そこで、サブエージェントが仕事を終えた瞬間に機械が採点し、基準を満たさなければ本人にやり直させる仕組みを SubagentStop フックで組みました。

SubagentStop はサブエージェントの「提出ボタン」に挟まる

Claude Code のフックには複数のイベントがありますが、サブエージェント専用の出口が SubagentStop です。親エージェントの停止に反応する Stop とは別物で、サブエージェントが応答を返し終えた直後にだけ発火します。ここがちょうど「提出物を受け取る前の検品ライン」になります。

重要なのは、このフックが終了コードと JSON 出力で挙動を制御できる点です。{"decision": "block", "reason": "..."} を標準出力に返すと、Claude Code はサブエージェントを止めずに、reason を指示として渡したまま処理を続行させます。つまり「ここがダメだから直して」とフィードバックを注入できます。採点結果をそのまま差し戻し文に使えるのが、この設計の肝です。

採点基準は散文ではなく JSON ルーブリックに固定する

「良い記事を書いて」のような曖昧な基準は機械採点に向きません。違反の有無を一意に判定できる形まで分解します。私が下請けの記事生成に使っているルーブリックはこんな形です。

{
  "min_h2": 4,
  "max_chars": 12000,
  "min_chars": 2500,
  "banned_words": ["崩壊", "最強", "爆速", "神", "完全ガイド", "決定版"],
  "forbidden_openers": ["この記事では", "本記事では", "いかがでしたか"],
  "require_code_block": true
}

ポイントは、各項目が「数えられる」か「文字列一致で判定できる」ことです。主観が入る評価(面白さ・読みやすさ)はここに入れません。あくまで「機械で確実に弾けるルール違反」だけを置き、創造的な品質は人間と本体プロンプト側に任せます。境界を引くことで、フックが誤検知でループに陥るのを防げます。

フック本体:標準入力の JSON からトランスクリプトを開く

SubagentStop フックは、標準入力に session 情報を JSON で受け取ります。その中の transcript_path がサブエージェントの会話ログ(JSONL)です。最後のアシスタント発言が提出物なので、そこを取り出して採点器に渡します。

#!/usr/bin/env bash
# .claude/hooks/grade-subagent.sh
set -euo pipefail
 
INPUT="$(cat)"                       # フックは stdin で JSON を受け取る
TRANSCRIPT="$(printf '%s' "$INPUT" | node -e \
  'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{
     console.log(JSON.parse(d).transcript_path || "")})')"
 
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
  exit 0                             # 採点対象がなければ素通り
fi
 
node "$(dirname "$0")/grade.mjs" "$TRANSCRIPT"

set -euo pipefail を付けているのは、採点器がエラーで落ちたときにフックが無言で成功扱いになるのを防ぐためです。検品ラインが壊れたまま全部合格になるのが、品質ゲートで一番怖い失敗です。

採点器:トランスクリプト末尾を読んでルーブリックと突き合わせる

採点本体は決定的(同じ入力なら同じ結果)に保ちます。外部 API を呼ばないので速く、無料で、ネットワーク障害でも止まりません。

// .claude/hooks/grade.mjs
import { readFileSync } from "node:fs";
 
const RUBRIC = JSON.parse(
  readFileSync(new URL("./rubric.json", import.meta.url), "utf8")
);
 
const transcriptPath = process.argv[2];
const lines = readFileSync(transcriptPath, "utf8").trim().split("\n");
 
// JSONL を末尾からたどり、最後のアシスタントのテキストを取り出す
let text = "";
for (let i = lines.length - 1; i >= 0; i--) {
  const ev = JSON.parse(lines[i]);
  if (ev.message?.role !== "assistant") continue;
  const blocks = ev.message.content ?? [];
  text = blocks.filter(b => b.type === "text").map(b => b.text).join("\n");
  if (text) break;
}
 
const fail = [];
const h2 = (text.match(/^##\s+/gm) ?? []).length;
if (h2 < RUBRIC.min_h2) fail.push(`H2 が ${h2} 個(最低 ${RUBRIC.min_h2} 個)`);
 
const chars = [...text].length;
if (chars < RUBRIC.min_chars) fail.push(`本文 ${chars} 字(最低 ${RUBRIC.min_chars} 字)`);
if (chars > RUBRIC.max_chars) fail.push(`本文 ${chars} 字(上限 ${RUBRIC.max_chars} 字)`);
 
for (const w of RUBRIC.banned_words)
  if (text.includes(w)) fail.push(`禁止語「${w}」を含む`);
 
for (const o of RUBRIC.forbidden_openers)
  if (text.includes(o)) fail.push(`定型表現「${o}」を含む`);
 
if (RUBRIC.require_code_block && !text.includes("```"))
  fail.push("コードブロックが無い");
 
if (fail.length === 0) process.exit(0);   // 合格:何も出力せず終了
 
// 不合格:block 判定を JSON で返し、理由を差し戻し指示にする
console.log(JSON.stringify({
  decision: "block",
  reason:
    "提出物が品質ルーブリックを満たしていません。次の点を直して再提出してください:\n" +
    fail.map(f => `- ${f}`).join("\n"),
}));
process.exit(0);

exit 0 のまま decision: block を返しているのがコツです。終了コード 2 でもブロックできますが、JSON で返すと reason をそのままサブエージェントへの指示として渡せるため、「何を直すか」が本人に伝わります。差し戻し文を箇条書きにしておくと、再生成の精度が目に見えて上がりました。

無限ループを防ぐガードを必ず入れる

この仕組みで最初にやらかしたのが、直しきれない違反でサブエージェントが延々と再試行する事態でした。たとえばルーブリックが厳しすぎて構造的に満たせないと、ブロックと再生成が止まりません。

対策として、トランスクリプトに含まれる過去の差し戻し回数を数え、上限を超えたら block をやめて人間にエスカレーションします。

const blockCount = lines.filter(l => l.includes('"decision":"block"')).length;
if (fail.length && blockCount >= 2) {
  // 2回直しても通らなければ、止めて記録する(人間が見る)
  console.error("[grade] 再試行上限。手動レビューへ: " + fail.join(" / "));
  process.exit(0);          // ここでは block しない=ループを止める
}

採点ゲートは「自動で弾く」ことより「弾き続けない」ことのほうが運用では大事でした。止まらないゲートは、止まらないアラートと同じで、いずれ全員が無視するようになります。

運用して見えた3つの実務的な勘所

ひとつ目は、ルーブリックをコードに埋め込まず JSON ファイルに切り出したことです。基準は運用の中で必ず変わります。新しい禁止語が増えるたびにスクリプトを触っていては続きません。

ふたつ目は、採点器を決定的に保ったことです。モデルに採点させる誘惑は強いのですが、同じ提出物で合否が揺れると、サブエージェントは「運が悪かっただけ」と学習できません。機械的に弾ける違反はコードで、主観的な品質は本体プロンプトと人間で、と層を分けるのが安定しました。

みっつ目は、差し戻し理由を必ず具体的にしたことです。「品質が不十分」ではなく「H2 が 3 個(最低 4 個)」と数字で返すと、再生成が一発で通る確率が上がります。フィードバックの粒度が、自動修正ループの収束速度を直接決めます。

次の一歩として、まずは banned_wordsmin_h2 だけの最小ルーブリックで SubagentStop フックを一本通してみてください。検品ラインが一度動き出すと、基準を足していくのはあとからいくらでもできます。同じように委譲の品質に悩んでいる方の参考になれば幸いです。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-06-13
ネスト型サブエージェントのコンテキスト予算 — 5階層委譲で品質を落とさない契約設計
サブエージェントが入れ子にできるようになった後、深い委譲ほど要約が痩せて再実行が増えました。階層間にトークン予算と受け渡しスキーマ、失敗隔離、独立グレーダーの4つの契約を入れて品質を立て直した実装記録です。
Claude Code2026-06-13
サブエージェントの入れ子は何階層が実用的か — 5階層ネスト解禁後に委譲構造を見直した記録
Claude Code のサブエージェントが最大5階層まで入れ子にできるようになりました。実際に深い委譲を試し、要約減衰やコスト増を踏まえて3階層に落ち着いた判断の過程を共有します。
Claude Code2026-05-05
Claude Code フックで作るゼロタッチ・コードレビュー環境
Claude Code のフック機能を使い、コミットのたびに自動でコードレビューが走る仕組みを構築します。PreToolUse・PostToolUse・Stop の組み合わせ方と、実運用で見えてきた落とし穴も含めて紹介します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →