今回は、AI に手順を守らせる話です。手順というのは、出来上がるものではなく進め方のほうを指定した指示のことで、「各ファイルは 1 本ずつ開いて確かめてください」がその形です。この種の指示には共通点があります。守っても破っても、出来上がったものが同じなので、守られたかどうかを後から見分けられません。

1 作目「読まない技術」の第 6 回で、ルールは読まれたときだけ効く。自動テストや hook は読まれなくても落ちると書きました。3 作目「言わない技術」の第 5 回では、強調について効いたかどうかは測れないと書いて、効き目ではなく件数の増え方だけを見ることにしました。今回は、その「測れない」のほうを測りに行きます。

まず一つだけ、開いてみてください。あなたの指示ファイル(CLAUDE.md や AGENTS.md)の中で、進め方を指定している行です。「1 つずつ」「順番に」「必ず確認してから」。その行を守った回と守らなかった回を、あなたは出来上がったものから見分けられるでしょうか。

CC BY 4.0

見分けられないなら、それは測っていないのと同じです

私は長いあいだ、大事な指示は目立つ位置に、短く書けば守られると思っていました。公式のドキュメントにも「指示を足すほど個々の指示は薄まる」という趣旨の記述があります。私はそれを、位置と長さの問題として読んでいました。前に出す。太くする。余計な行を削る。

本番のプロダクトでは、この読みが正しいかどうかを確かめられません。守られたかを見分けるには、答えが先に分かっている課題が要るからです。そこで、プロダクトを離れて題材を作りました。

logs/ に、ログを模したファイルを 12 本置きます。各ファイルから 1 つの値を拾って answers.tsv にまとめる、というだけの作業です。行頭が RESULT: のものだけが対象で、コメント行の中や引用の中にある同じ文字列は囮、有効な行が 1 つも無いファイルもあります。正解表は題材と同時に機械が作り、作業者に渡すディレクトリの外へ置きます(人が後から作ると、作業者の答えに引きずられます)。

作業の指示は NOTES.md 1 枚です。その中に、進め方の 1 文を入れました。

ファイルは1 本ずつ開いて中身を確かめてください。

これを、会話を引き継がない作業者に渡します。渡すのはこの 1 枚だけで、起動時のプロンプトにこの 1 文のことは書きません。書いた時点で、測っているものが消えます。

1 回目の実験は「1 つのプロンプトに何行積むか」を条件にしました。差は出ませんでした。⚠️ 作業者は 377 行を 1 回のコマンドで読みます。1 回の読み込みの長さは、薄まりの軸ではありませんでした。

2 回目は題材の量を条件にしました(12 本 / 60 本。⚠️ NOTES.md は両条件で 1 バイトも同じで、sha256 の頭 12 桁を照合しています)。8 体とも課題は満点、3 か所に置いた「数を書く」指示の追従も 24/24。⚠️⚠️ 往復の数さえ条件に支配されませんでした —— 中央値は 12 本の側が 13.5、60 本の側が 12.0 で、60 本の条件が最短 7 往復と最長 67 往復の両方を出しました。作業者は grep やスクリプトで畳むので、題材の量ではセッションは伸びません。

⭐ 代わりに、1 つだけ見つかりました。破られたのは「1 本ずつ開いて確かめる」だけでした。

指示位置守るのに要る手数追従
数を 1 つ書く(3 か所)31 / 127 / 219 行目1 回書くだけ24/24
1 本ずつ開いて確かめる27 行目12 回 / 60 回の道具呼び出し3/8

67 往復のセッションでも、31 行目の指示は守られています。薄まったのは位置でも長さでもなく、手数の高いほうでした。 ここで「手数が高い指示から落ちる」と結論しかけました。3 回目はそれを確かめに行って、外れました。

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

過程を指定する指示は、成果物が同じなら守られない。守らせたければ、その過程が成果物に残る形にする。

測り方: 同じ手間の指示を 2 本、隣り合わせに置く

NOTES.md の「進め方」の節に、同じ 12 件ぶんの手間の指示を 2 本、隣り合わせて置きます。

位置範囲守った跡中身
P27 行目12 件残らない各ファイルは 1 本ずつ開いて確かめる
A28 行目12 件残る各ファイルについて checked/log_NN.txt にそのファイルの行数を書く

位置は隣、範囲も同じ 12 件、要る手数も同じ 12 回です。⚠️ 違うのは、守った跡が残るかどうかだけ。 渡した logs/ は 2 回目と 1 バイトも同じです(sha256 で照合しています)。⚠️ NOTES.md のほうは、A の 2 行を足して P を箇条書きにしたぶんだけ、2 行長くなっています。

4 体に渡した結果です。

作業者課題の答えA(跡が残る)P(跡が残らない)往復
112/122/212/12(行数も一致)12/12 守った3480.0
212/122/212/12(行数も一致)12/12 守った2078.1
312/122/212/12(行数も一致)12/12 守った1767.6
412/122/212/12(行数も一致)12/12 守った2169.3

3 回を通すと、同じ 1 文の追従はこうなります。

実験置き方P の追従
2 回目P だけ(題材 12 本)2/4
2 回目P だけ(題材 60 本)1/4
3 回目P の隣に A を置いた4/4

⭐ 文面も位置も変えていません。隣に「同じ範囲の成果物」を 1 行足しただけで、3/8 が 4/4 になりました。

P は採点器からは見えません(見えないことが論点そのものです)。数えるのは作業者の記録のほうで、1 回の道具呼び出しが log_NN.txt をちょうど 1 本だけ名指ししたものを数えます。まとめて grepfor で回した回は 0 本か複数本になるので、数に入りません。

# 作業者の記録から「1 本ずつ開いたか」を数える(手元の実装からの簡約版)
python3 - "$1" <<'PY'
import json, re, sys
single = 0
for line in open(sys.argv[1]):
    hit = re.findall(r"log_\d+\.txt", line)
    if len(set(hit)) == 1:
        single += 1
print(f"個別読み: {single} 回")
PY

CC BY 4.0 はここまで

書き直さなくなったもの

守られない指示です。以前は、追従しなかった行を見つけると書き直していました。前に動かす。太字にする。「必ず」を足す。周りの行を削って薄まりを減らす。3 回とも、位置でも長さでも手数でも追従は動きませんでした

いまは書き直す代わりに、その指示を守った跡が残る成果物を、隣に 1 行足せないかを見ます。足せたら、書き方の工夫は要りません。足せなかったら、その指示は守られない前提で残りを組みます。

1 作目 第 6 回の二分法には、境目がありました。hook にできるのは、機械が真偽を決められる判断だけです。「1 本ずつ開いたか」は決められないので、この指示はルールの側に置くしかない —— そう思っていました。⭐ 決められるかどうかは、要求する成果物を変えれば作れます。 境目は与件ではなく、こちらが引く線でした。

CC BY 4.0

注意: これは「守らせた」のではありません

⚠️⚠️ 原因が 2 つ混ざったまま残っています。 A は 1 ファイルにつき 1 つの成果物を要求するので、A を守れば P は自動的に満たされます。「A が P の追従を上げた」のか「A のおかげで P がただで満たされた」のかは、この設計では分けられません。

⭐ 実務の答えとしては、それこそが答えだと思っています。分けられないように書くのが、正しい書き方です。 ただし記事の主張としては弱くなります。言えるのは「A を置いた 4 体は全部守った」までで、3/8 から 4/4 への差そのものは、母数 4 では偶然と区別できません。

⚠️ もう一つ、この実験が言えないことがあります。課題の答えは 3 回とも全員満点でした(3 回を合わせて 16 体。⭐ 内訳は 4 体・8 体・4 体で、記事に数字を出した 2 回目と 3 回目だけなら 12 体です)。囮を 5 種類入れても天井は破れていません。難度が張り付いている実験は、差が出ても出なくても読み方が狭くなります。次は「1 件ごとに判断が要るか」のほうで難度を上げます。

⚠️ 効いている向きだけは、実測ではなく機構のほうから言えます。2 回目に指示を安いやり方へ置き換えた 5 体は、課題の答えが全員満点でした。破ったのではなく、同じ成果物を、より安いやり方で作ったわけです。成果物が同じなら、そうなります。

実験手順: 同じ 1 文を、跡が残る版と残らない版で比べる

前提

  • 会話を引き継がない作業者を 4 体以上、同じ波で同時に起動できること(逐次にすると、その日の混み具合が条件差に化けます)
  • 作業者に渡すディレクトリを条件ごとに分けられること
  • 作業者の記録を後から読めること。⚠️ 要るのは 1 回ごとの「呼んだ道具の名前」と「その対象」です —— シェルを走らせた回なら打ったコマンド行(cat logs/log_01.txt)、ファイルを読む道具なら読んだファイル名(logs/log_01.txt)。⚠️⚠️ 道具の名前しか残らない記録では測れません

所要時間: 20 分(題材の生成器と採点器を自分で書く場合。すでにあるなら 5 分)

手順

  1. 答えの分かる題材を機械で作ります。 12 本のファイルと、その正解表を同時に出力してください。⚠️ 正解表は、作業者に渡すディレクトリの外へ置きます
  2. 指示ファイルを 1 枚作ります。 課題の説明と、離れた 3 か所に「数を 1 つ書く」指示を置きます(陽性対照です。これが落ちたら、条件ではなく作業そのものが壊れています)
  3. 同じ指示ファイルから、条件を 2 つ作ります。 片方は進め方の節に P だけ(跡が残らない指示)。もう片方は P の隣に A(同じ範囲・同じ手数で、跡が残る指示)。⚠️ それ以外は 1 バイトも変えない(ハッシュを印字して照合してください)
  4. 両条件を同じ波で起動します。 ⚠️ 起動時のプロンプトに P と A のことを書かないでください
  5. 課題の答えを採点します。 ⚠️ ここに P は出ません。出るのは A までです
  6. 作業者の記録から P を数えます。 1 回の道具呼び出しが対象ファイルをちょうど 1 本だけ名指ししたものを数えてください

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

  • 手順 2 の 3 か所が、両条件とも全員正しい(= 条件差ではなく作業の失敗、を除ける)
  • 課題の答えが、両条件とも満点(= 難度の差が追従の差に化けていない)
  • P だけの版で、P を守らない作業者が 1 体以上出る
  • P と A を並べた版で、A が全件満たされる

合格しなかったとき

  • P だけの版でも全員が守った —— 手数が足りません。⚠️ 題材の本数を増やして、1 本ずつ開くコストを上げてください
  • A が全件満たされない —— A の成果物が課題の答えと重なっている疑いがあります。⚠️ 答えを作る過程で自然に出てしまう値を、A に選ばないでください
  • 両条件で何も変わらない —— ⚠️⚠️ P と A の範囲がずれています。範囲(何件ぶんか)と位置を、先に揃え直してください
  • 課題の答えが満点でない —— 難度が高すぎます。⚠️ この状態では、追従の差と難度の差が同じ数字に化けます

後始末

  • 生成した題材・作業者の記録・正解表を削除します。⚠️ 正解表も一緒に削除する —— 残すと、次に題材を作り直したつもりのまま、古い答えを採点することになります

守った跡が残らない指示は、守られたことにも破られたことにもなりません。⚠️ この実験で最後まで測れなかったのは、追従率ではなく「見分けられるかどうか」のほうでした。


CC BY 4.0 はここまで

次回は「ラッパーを使わせない技術」。私は、自分たちで作ったコマンドを「必ず使ってください」と書くのをやめました。それでも AI は、自分からそのコマンドを使います。