並行して走る AI セッションが同じ仕事を二重に拾うのを hook で検知する
複数セッションを並行で走らせると、引き継ぎファイルの同じ「次の目的」を2つのセッションが拾って重複実装が走る。基盤が持つ目的台帳は定型文で汚れていて使えない(無関係同士の類似度が1.00)ため、transcript の assistant 宣言行から目的を取り、Jaccard 類似度で衝突を警告する hook の作り方。ノイズ床の測り方、user行を拾う罠の回帰テスト、刺すイベントの選び方、設定登録後の構造 read-back まで。
約3.7万トークンの節約 (API料金換算で約55円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「並行して走る AI セッションが同じ仕事を二重に拾うのを hook で検知する」は、AIのしつけカテゴリのAI指示書(MDファイル)です。複数セッションを並行で走らせると、引き継ぎファイルの同じ「次の目的」を2つのセッションが拾って重複実装が走る。基盤が持つ目的台帳は定型文で汚れていて使えない(無関係同士の類似度が1.00)ため、transcript の assistant 宣言行から目的を取り、Jaccard 類似度で衝突を警告する hook の作り方。ノイズ床の測り方、user行を拾う罠の回帰テスト、刺すイベントの選び方、設定登録後の構造 read-back まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.7万トークン(API料金換算で約55円)・88%のトークンを節約できます。
- カテゴリ
- AIのしつけ
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約4.2万トークン
- この巻物使用時
- 約5,200トークン
- 節約量
- 約3.7万トークン (約55円)
- 更新日
- 2026-09-17
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/ai-hook-4/raw を読み込んで、この指示書どおりに実装して"
中身
並行して走る AI セッションが「同じ仕事」を二重に拾うのを hook で検知する
誰向けか
AI コーディングエージェント(Claude Code 等)を複数セッション並行で走らせていて、
次にやることを引き継ぎファイル(next-session.md のような1枚)から拾わせている人。
起きる事故
セッション A が引き継ぎファイルの「次の1目的」を読んで着手する。20分後、セッション B が 同じファイルの同じ行を読んで、同じ目的に着手する。引き継ぎファイルは「誰が着手中か」を持たないので、 どちらも自分が最初だと思っている。
実際に起きた時に気付けた理由は、B が作ろうとしたブランチ名が A のものと偶然同じで
branch already exists で落ちたから、というだけだった。ブランチ名が違えば重複実装が1本まるごと走る。
やってはいけない解き方(先に潰しておく)
1. 「着手印を書くルール」にする
引き継ぎファイルに「着手中: セッションX」と書かせる運用にしても、書き忘れた瞬間に無効になる。 人にも AI にも手作業が増え、守られない時にだけ壊れる。機械が勝手に持っている情報だけで判定する。
2. エージェント基盤が持っている「セッションの目的」台帳をそのまま使う
多くの基盤は、セッションごとに <sessionId>.json のような状態ファイルを持っている。
これに purpose のようなフィールドがあると、そのまま使いたくなる。まず中身を実測すること。
実測したら、purpose は「そのセッションの最初の user prompt」を保存する実装だった。
つまり中身は目的ではなく、
- バックグラウンドジョブの完了通知(
<task-notification> …) - フックが注入する定型文
- スラッシュコマンドの説明文
だった。結果、無関係なセッション同士の類似度が 1.00 のペアが多数あった。 この状態で類似度の閾値を決めても、何を選んでも常時誤検知になる。
検証の型: 新しい判定ロジックを書く前に、入力データの実分布を測る。 「無関係なもの同士が、どれくらい似ていると判定されるか」を先に出す。これがノイズ床。 ノイズ床より十分高い位置に閾値が置けないなら、そのデータは使えない。
使える解き方
目的の出どころ = エージェント自身の発話
多くの運用では、セッション冒頭でエージェントに
**[本セッションの目的]** <目的> のような1行宣言をさせている(させていないなら、まずそれを足す)。
これは人間が書いた目的そのものなので、台帳の purpose と違って汚れていない。
会話ログ(transcript)は1行1 JSON の形式で保存されていることが多い。そこから拾う。
// transcript: <ログ置き場>/<projectSlug>/<sessionId>.jsonl
for (const line of text.split('\n')) {
let rec; try { rec = JSON.parse(line); } catch { continue; }
if (rec.type !== 'assistant') continue; // ← 最重要
const m = extractText(rec).match(/\*\*\[本セッションの目的\]\*\*\s*(.+)/);
if (m) purpose = m[1].trim(); // 複数あれば最後の宣言を採用
}
type !== 'assistant' の行を必ず落とすこと。 ここを緩めると、
「エージェントに目的を宣言させろ」と指示している側のテンプレ文(user 行やフック注入文)を拾ってしまい、
全セッションが同じ目的を宣言しているように見える。実際に一度踏んだ。
このケースは回帰テストとして必ず1本書く。
効果(実測)
| 目的の出どころ | 無関係なセッション同士の類似度の上限(ノイズ床) |
|---|---|
基盤の purpose 台帳 | 1.00(使用不能) |
| transcript の assistant 宣言行 | 0.04 |
ノイズ床が 0.04 なので、閾値はその数倍(例: 0.20)に置ける。
比較
目的文字列をトークン集合にして Jaccard 係数(積集合 ÷ 和集合)で比べる。 日本語を扱うなら空白分割は使えない。基盤側に既存の正規化処理があるなら、それに合わせる。
鮮度で絞る: 会話ログの mtime が直近 N 時間以内(例 8時間)のものだけを他セッションとみなす。 死んだセッションと衝突を騒ぐと、それだけで信用されなくなる。
hook としての作法(これを外すと本末転倒になる)
- 衝突が無ければ一切出力しない。 毎回何か出す hook は読まれなくなる。
- 絶対に落ちない。 全体を try/catch で包み、想定外でも何も出さず exit 0。 作業を止める hook は、防いだ事故より多くの時間を奪う。
- ブロックしない。 並行作業が正当なこともある。止めるべきは「気付かないこと」だけなので、警告に留める。
- 出す内容は次の行動まで書く。「衝突かも」だけでは動けない。
⚠️ 着手衝突の疑い: この目的は別セッションが先に着手している可能性がある。
- セッション <id 先頭8桁> / 最終活動 <時刻> / 類似度 <0.00>
目的: <相手の目的>
着手前に確認せよ: (1) 相手の worktree/branch が既にあるか確認する
(2) 重複なら着手せず、別の目的を選び直す
どのイベントに刺すか(ここに落とし穴がある)
「セッション開始時」に刺したくなるが、開始時点ではそのセッションはまだ目的を宣言していない。 自分の目的が取れないので、永久に無音になる。
ユーザー発話ごとのイベントにも刺すこと。宣言の直後、次の発話の時点で鳴る。
残る穴: 宣言した最初のターンの中では鳴らない。完全に塞ぐには 「ツール実行前」イベントまで降ろす必要があるが、毎回走るコストに見合うかは別途判断する。 塞げていない穴は、塞げたふりをせずそのまま書き残す。
検証のしかた(「テストが通った」で終わらせない)
- 合成テスト(最低5本): 衝突あり / 無関係 / 古いセッション / user 行だけの宣言(回帰) / 自分の宣言なし
- 本番データでそのまま実行 → 無音であること。誤検知ゼロの確認はこれでしかできない
- 実際に起きた事故の再現: 事故当時の本物のログを一時ディレクトリへコピーし、 自分の目的を当時のものに戻して実行 → 鳴ることを確認する
合成テストだけだと、自分に都合のいい入力しか作らないので 2 と 3 が要る。
実装を他のエージェントへ委譲する時の注意
- 委譲先の実行環境が違う場合(例: Linux 側で動かす CLI に、Windows の作業ツリーを渡す)、 「全テスト緑」の報告は自分の環境を保証しない。必ず自分でもう一度走らせる。 実際、委譲先で緑だったテストが手元では全滅した(テストが実行ディレクトリに依存していた)。 テストからスクリプトを参照する時は自ファイルからの相対で解決させる。
- 仕様書に「測定」と「実装」を両方詰め込むと、ツール実行の予算を測定で使い切って実装が落ちる。 測定は先に済ませ、実装の依頼には結果の数値だけ渡す。
- 委譲が終わったら、報告文ではなく作業ツリーの実体を見る(
git statusで指示外のファイルが 変わっていないか)。新規ファイルは未追跡なので、差分検知の自動チェックをすり抜けることがある。
設定ファイルへ登録した後は、構造で read-back する
登録スクリプトが「N件追加しました」と出し、設定ファイルの更新時刻もバックアップも変わったのに、 当該の hook が1件も入っていなかったことがあった(件数は同時に走った別 hook の分だった)。
登録スクリプト側が「実体ファイルが所定のディレクトリに無ければ登録しない」ガードを持っていることがある。 置き場所を間違えると無言で落ちる。
# grep ではなく、パースして構造で確認する
node -e "const o=JSON.parse(require('fs').readFileSync('<設定ファイル>','utf8'));
for (const ev of ['SessionStart','UserPromptSubmit'])
console.log(ev,(o.hooks[ev]||[]).flatMap(g=>g.hooks||[]).filter(h=>String(h.command).includes('<名前>')).length)"
登録前後で各イベントの hook 総数も控える。増えているか、そして減っていないかを見る。
よくある質問
+「並行して走る AI セッションが同じ仕事を二重に拾うのを hook で検知する」とは何ですか?
複数セッションを並行で走らせると、引き継ぎファイルの同じ「次の目的」を2つのセッションが拾って重複実装が走る。基盤が持つ目的台帳は定型文で汚れていて使えない(無関係同士の類似度が1.00)ため、transcript の assistant 宣言行から目的を取り、Jaccard 類似度で衝突を警告する hook の作り方。ノイズ床の測り方、user行を拾う罠の回帰テスト、刺すイベントの選び方、設定登録後の構造 read-back まで。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約4.2万トークンかかりますが、この巻物を使えば約5,200トークンで済みます。差し引き約3.7万トークン(API料金換算で約55円)・88%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
AI運用ルールを機械的に守らせる hook 設計 — ルール文が守られない本当の理由
チームでAIエージェントを使うと運用ルールが必ず守られなくなる。真因は「読んでいない」ではなく hook がそのマシンで登録されていない/委譲先が沈黙して壊れていること。禁止=実行前拒否・誘導=依頼時の具体コマンド注入・担保=セッション開始時の自己修復の3層、明示例外の短命トークン、warn→blockの段階昇格、BOM/サンドボックス/timeout など失敗が沈黙する罠と、環境依存で落ちないテストの作り方までを実測ベースでまとめた導入手順。
AIの応答を止める番人hookを1ランナーに統合し、書き直しを最大1回にする(誤爆率をfixtureで先に測る)
Stop hook を9本積んだら Stop の65%が書き直し・最多ゲートの91%が誤爆だった。誤爆測定→否定文除外→1プロセス合流→再試行上限統一→全PC移行→KPIで効果確認までの手順。
定額プランの最上位モデルを枯渇させずに使う「二段レーン」設計
5時間/週の枠を桁違いに食う最上位モデルを、長時間タスクと失敗時の昇格だけに自動で当て、上限に当たったら既定モデルへ退避する委譲レーンの作り方。判定は純関数・検証まで含む。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア