実践ガイド

September 21, 2026 · View on GitHub

docs/ の 27 本の実測を、手順の形に直したもの。 「何が分かったか」ではなく「何をすればいいか」で並べています。 数字はすべて各レポートの実測で、末尾にリンクがあります。

実験ごとの記録は findings.md、ブログ用の要約は summary.md


決める順番

0. そもそも向いている問題か
1. 答えの形を決める           ← ここが一番効く
2. 質問を書く
3. 1 リクエストに詰める
4. 閾値を決める               ← ここが一番手間
5. 複数の答えを合成する
6. 運用に載せる
7. 測り続ける

8. (判定ではなく行動させるなら)action space を決める  ← 0 の前に戻ってくる

1 と 4 で 8 割決まります。3 は考えなくてよくなる(全部詰める)ので楽です。

判定させるのではなく実アプリを操作させるなら、8 を先に読んでください。 1〜7 はそのまま効きますが、そこでの一番大きい失敗は閾値でも質問文でもなく 操作の集合が足りていないことで、それは 0 の「向いている問題か」に戻る話です。


0. そもそも向いている問題か

向いている

  • 選択肢が列挙できる。 その場の合法手・実在するタスク名・ハンドラ一覧。 列挙すると不正な答えが表現不能になる(MOBA 489 判断・チェス 37 手で反則 0)。
  • 順序のある判定。 allow < confirm < block、優先度、severity。
  • 独立した述語の束。 ハザード検出、欠陥クラス、リスク要因。
  • 同じ state に対して判断が複数ある。 チームの視界に対して 3 キャラ、 ファイル 1 個に対して全関数。fan-out がそのまま効く。
  • 全件に回したい / 人の操作に間に合わせたい。 200 ms・関数 1 個 $0.000017 なので、 「サンプリングして眺める」ではなく「全部に回す」が選べる。
  • 実アプリを操作させたい —— ただし操作の集合を列挙できるなら。 ブラウザの操作は「候補が列挙できて順序がない」ので形としては合います。 ただし判定と違って足りない操作が精度の劣化ではなく到達不能として出るので、 先に 8.1 を通してください。

向いていない

  • 文字列を生成させたい。 そもそも返しません。 行動させるときにこれが効いてきます —— 検索クエリも clear: true も Jev には書けないので、 値はコード側で持つか、選択肢の形にして action space に入れるしかありません(8.2)。
  • 関数やコードの中だけでは判断できないもの。.sort() は比較関数なしだと辞書順」 のような特定の API の挙動を知らないと見えない判定は、正しい問いを名指しで投げても 0.09〜0.22 で「いいえ」と返ってきます(22 §3)。 型・既存の lint ルール・テストの担当で、置き換えではなく補完。
  • 列挙し忘れたクラスを拾いたい。 原子質問だけにすると列挙外に穴が空きます。 汎用質問を必ず併置する(§5)。
  • 閾値の境界に乗った決定をそのまま自動化したい。 境界帯はメッセージの些細な違いで 決定が入れ替わります(09 §3)。境界帯は人へ。

1. 答えの形を決める(ここが一番効く)

問題の形使う理由
順序がある(許可段階・優先度・severity)scorechoice だと順序が捨てられる
独立した述語(「これは X か」)noul1 つずつ閾値が引ける
互いに排他な分岐(どのハンドラ・どの手)choice候補集合で不正解を消せる
「何をするか」と「何に対してか」が両方あるchoice を操作別に分ける(8.3)1 問にすると要素と値の組み合わせが爆発する

順序のある結論を choice で聞いてはいけない。 コード側の閾値をどう捏ねても 14/24 だったものが、 choicescore の一手で 19/24 → 23/24 になりました。

理由は confidence の意味が変わることです。dd if=/dev/zero of=/dev/sda を両方で聞くと:

choice: confirm=0.35 block=0.65 allow=0.00  -> block@0.47
score:  0=0.00       1=0.04     2=0.96      -> 1.95/2 @0.93

choice の 0.47 は「危険か迷っている」のではなく 「confirm と block のどちらに寄せるか迷っている」だけでした。 → 01 §3


2. 質問を書く

やること

  • noul の criteria はネストする。

    { "type": "noul", "instructions": "…",
      "criteria": { "true": "…", "false": "…" } }
    

    トップレベルに true/false を置くとサーバーは 200 を返して黙って捨てます。 エラーは出ません。気付けるのは input_tokens の差だけ。 → 00

  • score の rubric は 1 軸だけにする。 「ひどさ」のように軸が混ざると単調になりません。 「レビュアーがどれだけ押し返すか」のような1 本の反応の強さにする。 → 01 §5

  • 選択肢の説明は省いてよい。 criteria の値に null を置くと「選択肢名だけで解釈」。 20 ハンドラで入力 441 トークン、confidence 0.98〜1.00。 名前が内容を説明していれば名前だけで足ります —— 53 タスクから名前だけで 90.0%、 1 行の説明を足して 100%、実装まで見せても上積みゼロ。 → 00 17

  • 候補はその場の合法手に限る。 バリデーションではなく型で消すのと同じ効き方。 文字列で手を生成させる方式との決定的な差。 → 02 §5 03 §2

  • コードが既に知っていることは criteria の説明文に書く。 「そのノードは何 hop 先か」「タワーがあるか」。五目並べの WINS/BLOCK ヒントと同じ。 → 02 §5

  • 繰り返させたくない選択はコード側で判定する。 「同じことをするな」と覚えさせるより 安く確実で、連打は完全に止まります(消すか候補に付けるかは下の 2 項)。 → 05 §5

  • 決定的に判定できる事実は、消しても伝えてもよい —— 精度は同じ。 「押しても効かない候補」をジオメトリで判定したとき、選択肢から消すのと state に書いて渡すのは同値でした(到達・手数ともに差なし)。 消す側の追加価値はトークン 4% で、精度ではありません。 → 消すのは安くしたいときの手段で、効かせたいときの手段ではない。 ただし測っていない側を推し測ってはいけないのがここの教訓です (57 は「伝えるだけ」のアームを置かずに 「消すほうが効く」と書いていて、それは一度も検証されていませんでした。 元をたどると 05 の 「伝える」も直前の 1 手についてのグローバルな真偽値で、 候補ごとに付けた形は測っていません)。 → 62 §4

  • 精度が同値なら、間違えたときに復帰できるほう(伝える)を既定にする。 消す側は判定を間違えるとどの閾値でも復帰できません —— 05 §3 では効果判定のバグで 有効な操作が「無効」と記録され、正解の経路が 2 手とも封じられました。 伝える側は誤ったラベルを渡すだけで、モデルが覆せます。 → 24 §5 の 「決定的な絞り込みは静かに失敗する」と同じ形で、 決定的に消す機構は、判定の正しさをそのまま到達可能性に変換します。

やらないこと

  • 閾値を質問文に書かない。 閾値はコード側の決定です。質問文に書くと、 再校正のたびに質問が変わって過去の測定と比較できなくなります。
  • 判定基準の「例外規定」をゲートに渡さない。 渡すと直る一方で 隣のケースにも一般化されます(while(true) の免除を do/while(true) に広げて 見逃しが 0 → 11 件)。 → 16 §5
  • 「該当なし」を choice の選択肢に混ぜない。 → §2.1

2.0 subject から見えない条件を質問文に書かない

例外規定を書くときの落とし穴です。catch 節について **「呼び出し元がこの失敗を知る必要があるなら違反」**と書いたら、 4 件の答えが全部 1.03〜1.58 の真ん中に来ました(gap 0.28)。 「呼び出し元が知る必要があるか」は catch 節を見ても決められないので、 モデルは正しく「わからない」を返していたことになります。

catch 節自体が何をしているか(投げ直すか / 失敗と区別できない値を返すか) だけを聞く形に直したら gap 0.77 になりました。

→ 例外は書いてよいが、subject から検証できる形に書く。 4.0 の gap がこれを検出します。 → 24 §2

2.0.1 質問にコストを書くかどうかで答えが 21 ポイント動く

同じ判断・同じ state・同じ 1 リクエストで、 「第 2 ワーカーはトークンとレイテンシと誤り伝播を増やす」を先に述べるかだけを 変えた 31 §2 の実測:

質問正解誤りの向き
コストを先に述べる22/38(58%)+0 / −16(全部「やらない」側)
何も述べない30/38(79%)+4 / −4

述べた版は「やらない」側で 6/7・6/6 と最良で、 やるべき 22 件では 6/22。 → これは正解率の指標ではなく保守性のダイヤルです。 偽陽性が高くつくなら述べる、偽陰性が高くつくなら述べない、と 意識して選ぶ。何も考えずに「コストを説明しておこう」と書くと、 気づかないうちに保守側に振り切れます。

2.0.2 相互排他な候補なら choice 1 つで全部の順序が出る

choice の答えは選んだものだけでなく probabilities に全選択肢の確率を 持っています。つまり n 個の候補を並べたいなら質問は 1 つで足ります32 §5 の実測:

1 タスクのトークン結果
候補ごとに score 1 問(n 問)3,254テスト実行 1.00 回
全候補で choice 1 問1,554テスト実行 1.00 回

ただし確率は「どれも駄目」を拾えません —— 和が 1 になるので、 全滅のときも誰かが高くなります(gap −0.04)。 同じことを noul に聞くと gap 0.08 で分かれます。 → 順序は choice、「そもそも候補に入っているか」は別の noul

2.1 逃げ道は選択肢ではなく別の問いにする

choice必ずどれかを選びます。範囲外の入力が conf 0.96 の自信のある誤答になります:

"What is the airspeed velocity of an unladen swallow?" -> technical conf=0.96
"asdf qwer zxcv"                                       -> technical conf=0.99

対処は 2 通りありますが、等価ではありません:

やり方該当なし 18 件副作用
別の noul で聞く18/18なし
choice の選択肢に「該当なし」を足す16/18答えのある難問までそこへ逃げる(conf 0.50)
両方かける89.8% → 88.0% と下がる

00 17 §3


3. 1 リクエストに詰める(ほぼ考えなくてよい)

判断に要りそうな述語は全部書いて 1 回投げる。 20 問を 1 リクエストにすると:

レイテンシ入力トークン
20 回に分割5227 ms8277
1 リクエスト246 ms1000

21x 速く 8x 安く、答えも動きません(単独で聞いた場合との平均差 0.011)。 → 質問を削る最適化は意味がなく、往復を削る最適化だけが効く。

state の「一部分」についての質問も束ねて良い。 ファイル全関数を 1 リクエストで judge しても、 1 個ずつ聞いた場合と平均絶対差 0.082・Spearman ρ 0.931・判定差 3.6%。 隣の悪いコードへの汚染はほぼ起きません。

答えを捨てるかもしれない質問も詰めて良い(投機)。 「操作が決まってからでないと 対象は聞けない」と思って 2 往復にする必要はありません。先に全部聞いて、 使う 1 つだけ読んで残りを捨てると、判断は変わらずリクエストが半分になります。

実行された head の一致TV
本番(2 fixture)12/120.000
敵対盤面(4 fixture)21/21平均 0.003

argmax が同じなのではなく分布が同じです。壁時計は −53%。 理由は曖昧さが「どの操作か」の側にしかないからで、全操作が必要な画面でも operation が 0.55 のときその target は 1.00 —— 後段の質問は簡単な半分です。 → 61 §4 61 §8 74 問まで測っても崩れません。 29 §4 は 74 の候補について 1 リクエストで聞いた答えと、1 問 1 リクエストで 1,036 回聞いた答えを対で比べました —— 0.25 以内に 99.8% 一致・レベル一致 97%・平均差 0.032、 入力トークンは 296,845 対 718,128。 → 「候補が多いから 1 問ずつ」は 2.4 倍払って同じ答えを買う行為です。

3.2 全問で同一の文字列は state に置く —— 上限が 2 倍になる

score の criteria も、instructions のタスク文も、質問ごとに同じ文字列です。 fan-out はそれを n 回払います。 30 §7 の実測:

1 問の入力トークン1 リクエストに入る問数
4 段階の criteria とタスク文を質問ごとに書く251260
criteria を 4 語にしてタスク文を state に移す118520

答えはレベル一致 95%・0.25 以内 95%・平均差 0.057 で、指標は動きません。 → 候補が多いときに最初にやるのは候補を削ることではなく、 質問から重複文字列を抜くことです。

上限は質問数ではなくトークン

1220 questions ->  65047 tokens  OK
1240 questions -> ~66100         400 {"error_type":"max_tokens_exceeded"}

state 32662 tokens  OK
state ~33400        400 max_tokens_exceeded
  • リクエスト全体 65536(64Ki)トークン
  • state だけで 32768(32Ki)トークン ← 独立枠。こちらが先に詰まる
  • 255 は choice の選択肢の上限。質問数の上限ではありません

どちらも公式スキーマにありません。実装側は見積りを正確にするより、 max_tokens_exceeded を見たら質問集合を半分に割って投げ直すのが確実です。 → 00 21 §5

state に何を入れるか

文脈は精度を上げるのではなく、judgment を穏やかにします。

実験構造化 state の効果
0408最大のレバー。文字列では区別できない判断ができる
17changed_files も直前の終了コードも 1 件も動かさなかった
21 §6誤検出が 1/5 になり、見逃しが増えた

| 28 §4 | 文書全体を足すと omission が 1.84 → 0.57。文脈は答えの隠れ場所になる |

3 つとも同じ現象(文脈が増えると score が下がる)です。 → 足す前に、ゴール文だけで足りているか確かめる。そして 「どちらの誤りを減らしたいか」で決める(誤検出を減らしたい = 足す、見逃しを減らしたい = 外す)。

そして、入れた事実は「関連あり」と印付けしないと読まれません。 未実行の関数名を code_not_yet_executed という配列に素で置くと 1/12。 印の付け方は 2 通りあって、どちらか一方で足ります:

名前を state に名前をゴール文に
指示なし1/12 ← これだけ落ちる9/12
指示あり(「その名前のコントロールを探して押せ」)9/129/12

ゴール文はそれ自体が印(そこに書いてあるものは実行の目的を定義する)で、 明示的な指示はどこに置いても印になります。 → ただし「やれ」と書くと幅を削る。 名前をゴール文に置いて指示は書かないのが 最良手で、到達状態 14.0/14・無駄手 5.0(指示を足すと 10.0/14・無駄手 9.0)。 印付けまでやって、やり方は指定しない。58 §4.5

3.1 判断の対象は state ではなく質問に置く

候補が n 個あって 1 つずつ判定したいとき、置き場所が 2 通りあります —— 候補を state に入れて質問を固定するか、state をプロジェクト側だけにして 候補を質問の instructions に乗せるか。 29 §4幅を 1 に固定してこの 2 つだけを比べました:

一致(0.25 以内)AP
候補は質問(solo)99.8%0.53
候補は state(single)53%0.46

リクエスト数もトークン数もほぼ同じで、置き場所だけで落ちます。 そして候補を質問に置けば、そのまま §3 の束ね方に乗る —— state は 1 回ぶんしか払いません。 → state は「判断の前提」、質問は「判断の対象」。

3.3 2 次元のものを渡すときは、state の形が読める軸を決めている

34 §1.1 は NetHack の 80×24 の画面を 21 本の文字列として渡して、 同じ盤の同じ 1 文字について 2 つ聞きました:

質問正解AUC
<@ より下の行96%0.997
<@ より右の列74%0.857

行の比較は配列の添字の比較で、列の比較は行の中で何文字目かを数えることです。 AUC の差なので校正のずれではありません。

同じ実験の §1.2 は もっと極端で、地図全体の敵の数は AUC 0.933 で読めるのに、 @ の隣に敵が居るかは真のとき 49% —— コイン投げでした。

「2 次元を読める / 読めない」ではなく、渡した形がどの軸を読めるようにしたか。 配列にした軸は読めます。行の中の位置は読めません。 渡す前に、聞きたい関係がどちらの軸に乗っているかを見てください。 34 §2 はこの帰結の実測で、 隣接マスを言葉で書き添えるだけで、却下率が 31% から 3% に落ちます。

3.4 ベースラインに与えた道具は、判断側にも与えてから比べる

34 §2.4 は 私がこれを踏んだ記録です。探索ボットには訪問済みのマス集合とコミット済みの目標を 持たせ、判断側には記憶を 1 つも渡さずに並べて、 「1 手ずつは妥当なのに数百手の方針が無い」と書きかけました。 記憶の欠落で自分の探索ボットが 400 手足踏みしたのを直前に見ていたのに、です。

訪問回数だけを足すと:

地図化したマス歩いたマス却下率
記憶なし48203%
記憶あり1525519%
探索ボット(訪問済み集合 + 目標 + 幅優先)193514%

「判断側にはできない」と書く前に、コード側に与えた情報を数え上げる。 07 以来の「判断を抜いた同じ仕組みと比べる」の裏側で、 比べる相手に下駄を履かせていないかを見る手順です。

そしてこの表は指標の読み方も変えます —— 却下率が低いのは「上手い」ではなく「動いていない」でもあり得ます。 3% と 19% を並べるまで、それは見えませんでした。


4. 閾値を決める(ここが一番手間)

4.0 まず gap を見る —— 閾値の問題かどうかがそこで決まる

判定を score 順に並べて、当たりと外れの間にどれだけ差があるかを見ます。 これが先で、閾値はその後です。

gap答えがどこに居るか意味やること
広い(1.8〜2.2)違反と残りが分かれている質問が効いている何もしない。 閾値はどこに置いても同じ答え
狭い(0.2〜0.3)全部が「満たしている」側違反が無い何もしない。 実コードではこれが普通の状態
狭い(0.2〜0.3)閾値をまたいで連続答えが分かれていない閾値ではなく質問を書き直す

gap だけ見ると下 2 行が同じに見えます。 実リポジトリに当てたら狭い gap が 3 ルールで出て、 2 つは「違反が無い」・1 つは「文が働いていない」でした。 区別するのは gap ではなく答えがどのレベルに居るか(band)で、 読む道具の側に出させるべき数字です(26 §4)。

ad-hoc ルールを 5 つ書いたら 2 つが gap 0.16 / 0.28 で失敗しました。 どちらも校正では直らず、文を書き直して 2.16 / 0.77 になりました。 残り 3 つは gap 1.79〜2.16 で、閾値は既定値のまま何もしていません。

4.1 以下は「答えは分かれているのに閾値で潰している」場合の話です。 gap が狭いときにそれをやると、直らないものを直そうとして時間を使います。 → 24 §1

4.1 質問ごとに引く。共通 1 本は間違い

同じ形の 8 問を並べたら、自クラスへの答えが 0.20 から 0.94 まで開いていました:

指標自クラス平均clean 平均AUC
unit_or_arithmetic0.940.051.00
unhandled_async0.220.060.92
api_default0.200.100.80

下 2 つは死んでいるのではなく冷たい —— 0.80 に一度も届かないのに、順序は正しい。 共通閾値 0.80 は 13/36、質問ごとに引けば 24/36(誤検出は同じ)。 → 22 §4

ただし 24/36 は当てはめた標本での数字です。清潔な側をホールドアウトして 誤検出 0 を要求すると 18/36 に落ちます(共通閾値の 13/36 にはそれでも勝つ)。 → 25 §2

4.1.1 質問の形を変えたら閾値を引き直す

confidence は質問の形の性質で、アーム間で比較できません。 同じ盤面の同じ判断で、正しく着く側が 0.46〜0.71、失敗する側が 0.93〜0.99でした (選択肢を絞った質問は自信を持ちやすく、広い質問は迷う)。

→ 4.1 は「質問ごとに引く」ですが、同じ質問でも形を変えたら引き直しです。 → 61 §3

4.2 コーパスで校正した文を実コードに当てるときは「わざとやっている版」を疑う

植え込みバグ 641 行で 5/5 だった文を、実リポジトリ 9,315 行に当てると **19 指摘のうち 17 件が「意図的にそうしてあるコード」**でした。 catch { return {} } は文のとおり違反で、同時に 18 の設計そのもの。

そして confidence の向きが逆になります: 自信のある 5 件は 0/5、 当たった 2 件は conf 0.29 / 0.19。形が自明なほど自信は高く、 形が自明な違反は意図的であることが多い。 → 低 confidence を「弱い指摘」として畳むと、実コードでは当たりを畳む (26 §3)。

4.3 confidence はルーティングに使い、ゲートにしない

報告の前提条件にすると 62.5%、外すと 73.7%。 clean も bug も confidence が 0.54〜0.57 に入るので、柵にすると正解ごと捨てます (score 2.42 / confidence 0.42 の当たりが消えていた)。

正しい使い方: 閾値を越えたものは必ず報告し、confidence が低いものは 別のメッセージ(「人が見ろ」)にする。判定を消すのではなく出し分ける。 → 21 §7 07

4.3.1 confidence には床がある。「どうでもいい」とは言わない

35 §1.1どの手を選んでも絶対に結果が変わらないゲームを作って測りました (総当たりで確認済み、criticality は全局面で厳密に 0)。 それでも 1 - confidence0.418。 5 種のゲームでの実測レンジは 0.42〜0.77 で、 照合相手の criticality のレンジ 0.00〜0.54 に対して下側が丸ごと欠けています

confidence は順位付けには使えて、「この判断は自由だ」の検出には使えません。 03 §3 の 「confidence が難しさを測っている」は順位としては再現し、絶対値としては再現しません。 「confidence < 0.3 ならどうでもいい判断なのでコードで決める」という設計は、 この床のせいで一度も発火しないか、常に発火します。

4.3.2 出力が合っていても、confidence は別のものを見ていることがある

35 §3 が 一番はっきりした形です。三目並べの盤で 「3 つ並べたら負け」と state に明記して自己対戦させると:

実測
着手の正しさ81%(一様ランダム 52%、普通の三目並べ 66%)
doubt反転ルールの criticality−0.145
doubt普通のルールの criticality(同じ盤)+0.813

手は反転規則で選べているのに、confidence は反転前の盤の難所で上がっています。 2 つの criticality 自体は逆相関(−0.40)なので、言い換えではありません。

confidence を「その判断がどれだけ難しかったか」として二次利用するなら、 出力の正しさとは別に照合してください。 出力が当たっていることは、confidence が同じものを見ている証拠になりません。 33 §3 の 「読まれているかを別に測る」と同じ形で、別の量には別の対照が要ります。

4.3.3 confidence に失敗を見せる —— 見せなければ反応しない

confidence は「初見で効かない手」を検出しません。 押しても何も起きない手 13 件のうち 12 件が 0.99 以上で返ってきました。低く出たのは「正しいが手応えのない手」でした。

ただしこれは渡していなかっただけでした。state に last_action_error無効果が続いた回数を入れると、同じ blocked な手の confidence が 0.91 → 0.57 → 0.49 → 0.45 と減衰します。

信号が無いのではなく、信号を作る事実を state に戻していない。 閾値を疑う前に「その判断に必要な失敗の履歴が state に入っているか」を見てください。 → 57 §4 62 §3.2

4.4 当てはめるなら境界ではなく gap の真ん中に置く。そしてホールドアウトで採点する

「clean の最大値 + 0.01」で引くと、誤検出の境界そのものに置くことになります。 10 関数足しただけで越えました(unescaped_compositionテンプレートリテラル全部に発火)。

ホールドアウトで測り直すと、この置き方は質問が稼いだ余白を全部捨てています:

置き方検出誤検出(当てはめた標本)誤検出(ホールドアウト)
clean の最大値 +0.0123/36024
gap があれば中央、無ければ +margin23/3607
誤検出 0 を要求(margin 0.20)18/3600

手順はこう:

  1. 当てはめた標本の「誤検出 0」は報告しない。 負例の最大値の上に置いたのだから発火しません。
  2. gap があるなら中央に置く。 正例は gap のずっと上にいるので検出は落ちません。
  3. margin をモデルの揺れから決めない。 効いた margin は draw sd の 20 倍でした。 閾値を壊すのは再 draw(0.37%)ではなく見たことのないコード(2.0%)です。
  4. fold を切って採点する。 部品(crossValidate)が 1 行でやります。

22 §11.4 / 25 §3

4.4.1 「どこでも高い」を引く —— 判断に対する IDF

同じ質問を n 個の候補に投げて上位を取るとき、 どの候補にも高い点が付くものが上位を埋めます。 30 §6 では 461 skill のうち code-reviewerdevops-engineer が どのリポジトリにも 2.9〜3.0 を取り、それは正しいので上位から外れません。

対策は score そのものではなくその候補自身の水準からの超過を読むこと:

value(候補, 対象) - mean(value(候補, 参照対象群))

参照は対象自身を除いて取ります(ホールドアウト)。実測では 候補プールが大きいほど効いて、461 件では AP 0.19 → 0.29。 → 候補ごとの水準は 1 列の数字なので、一度測って配れます。

4.4.2 gap が正でも draw と比べる

32 §4 は このリポジトリで初めて gap が正になった測定です (ラベルが node --test の終了コードだから)。それでも使えません:

score    solvable 2.70 ±0.31   unsolvable 0.99 ±0.61   gap 0.03   AUC 1.000
         同じ質問を 2 回引いたときの上位値の動き: 平均 0.043・最大 0.210

AUC 1.000 で gap 0.03、draw が 0.043。 次の draw が跨ぎます。 同じ表の noul は gap 0.08 対 draw 0.018 で、こちらは 4 倍の余裕がある。 → gap を見たら必ず --repeat を足して draw と比べる。 25drawNoise がその部品です。

4.5 ラベル付きの小さなコーパスが一番効く投資

同じプロンプトでエージェントに質問を書かせると 10/24 から 23/24 まで振れます。 閾値を合わせ直すだけで 10〜22 → 21〜22 に収束しました。 設計より校正のほうが効く。04 §2

4.5.1 指標をプロンプトに入れる前に、指標だけで分かるかを測る

機械的な指標を判断に渡したくなったら、先に指標だけで採点します33 §1 の実測では 5 つの数値指標が全部 AUC 0.5 の上に座っていて、 そのまま渡した結果がこれです:

見せたもの正解AUC1 タスクのトークン
diff + ソース + テスト90%0.9416,548
それ + 機械的な指標90%0.92111,094

トークン +69% で AUC −0.02。 そして risk の側では 89% → 85% と下がる。 → ラベルを分けない指標はプロンプトに入れても分けません。 27 §3 で算術が完勝したのは 指標が答えそのものだったからで、「機械的だから渡すと良い」ではない。

そして渡した情報は保守性のダイヤルでもあります —— 指標を足すと「安全」と言う誤りが 14 → 16 件に増えました。

4.5.2 「読まれているか」を別に測る対照を置く

「渡したのに効かない」は 2 通りに読めます —— 中身が無意味だったのか、見られていないのか。 区別するには渡した情報だけが答えを持つ質問を混ぜます。 33 §3 では 指標ブロックがカバレッジ数を持っているので、 「テストはこの行を通るか」を聞くと指標だけの arm が 261/261。 → だから「効かない」は中身の話だと言えます。

4.6 答えが測れる量の関数なら、聞かずに計算する

「人を起こすか」を SLO(ユーザー向けリクエストのうち 5xx か 1 秒超過の割合)から決めると、 1 行の規則が 16/16・誤ページ 0・見逃し 0。同じ窓を Jev に聞くと見逃しは 0 のまま 誤ページが 6〜27 件出ます。

判断を足す前に、その答えが測れる量の関数でないか確かめる。 23 §5 の 「規則で解ける部分は判断に混ぜない」の、順序つき答えの版です (27 §3)。

4.7 スキップの代償が非対称なら、閾値もコストで重み付けする

タスクを走らせるか決める filter で、唯一の見逃しは 4 秒のタスクが 1.24 対 1.25 で 落ちたものでした。飛ばして節約できるのは 4 秒、失うのは その変更で赤くなる唯一のチェック

→ 1 本の閾値の前に 「安すぎて飛ばす意味がないもの」の床を引く。 「10 秒以下は無条件に走らせる」の 1 行で見逃しが 0 に戻り、代償は 0.5 machine 分でした (削減 75.8% → 72.7%)。 → 23 §7

コストが実測できているなら、床は定数ではなく関数にできます。 score を確率に直して 「閉包の秒数 + penalty × P(見逃し)」を最小化すると、各タスクの柵がそのタスク自身の秒数から出ます (0.1 秒のレシピは無条件、5.3 秒のレシピは 1.30 を要求)。 実測ラベルで 検出 18/18 を保って削減 73.5% → 81.4%。 自由なパラメータは「見逃し 1 件の値段」1 個だけになり、これはコーパス無しで答えられる質問です。 ただし効果は必ず判断を抜いた同じ規則と比べること(同じ検出で 81.4% 対 40.9%)。 → 25 §5

4.8 ばらつき対策を作り込む前に、ばらつきが決定に届いているか見る

同じ diff を 10 回投げると、240 組のうち閾値 1.25 をまたぐのは 7 組そのどれも実際には赤くならないタスクでした。 10 draw の平均で決めても、1 draw で決めても、検出も machine 秒数も同じ (選ぶ集合が違うのは 150 決定中 16 件)。

→ 信頼区間や複数 draw の平均を実装する前に、またぐ組が当たりかどうかを見る。 モデルの揺れは、答えの範囲の約 1%(noul で 0.009 / 0..3 の score で 0.030)でした。 → 25 §7


4.9 書いてある規則を分解しても、良くなるとは限らない

31 が相手にした skill は決定手続きを ブール式で書いています —— (1 or 2) and (4)。 なので「4 つの原子を聞いてコードで結合」と「そのまま 1 問で聞く」の 両方が作れて、測ると 30/38 対 30/38 の同点でした。

分解の値打ちは正解率ではなく 2 つです:

  1. 誤りの向きを選べる(組み立ては +2/−6 で保守的、素直に聞くと +4/−4)
  2. 答えの理由が出る —— (1)=0.19 (2)=0.30 (3)=0.93 (4)=0.95 は 「検査はできるが分けられる部分が無い」と読めます

そして分解には固有の危険があります。結合の 1 行は原子より脆い —— 条件 3 を規則に入れただけで、4 つの原子が全部正しいまま 4/4 が 0/4 になりました(31 §4)。 → 判断を測るときは結合も疑う。どの原子の誤りが効くかはブール式で決まる (連言の位置にある条件の誤りは全件を反転させ、選言の位置は片方が真なら吸収される)。


5. 複数の答えを合成する

  • 原子信号と総合質問の保守側を採る。 スコア単独 90.3%・原子単独 61.1% に対し 保守側 94.4%(同一応答で比較)。 → 18 §2
  • 平均しない。 信号を持つ 1 問が薄まります。最大か、各自の閾値からの超過率で。 → 08 §4.3
  • 原子質問は汎用質問の「上乗せ」で、代わりではない。 列挙した 8 クラスでは原子が勝つ(25/36 対 18/36)のに、列挙していないクラスでは負けます (11/15 対 15/15)。両方聞けば両方取れる。 → 22 §11.2
  • 原子質問は互いに重複している。 8 指標のうち 7 個は抜いても自クラスを失いません (隣の指標か汎用 noul が拾う)。増やすより、広い 1 個を正しく校正する。
  • 合成規則は自分のバグ面になる。 分解は銀の弾丸ではありません。 → 01 §4

6. 運用に載せる

6.1 失敗の落とし先を精度より先に決める

判断を下す場所(hook・ゲート・lint)では、精度より先に「API が落ちたらどうなるか」が決まります。

  • 全失敗経路を「判断なし = 通常フロー」に落とす。 allow に倒せば黙って承認、deny に倒せば障害で全停止。
  • ゲートは狭める方向にだけ使う。 allow を返すと ユーザーが設定した permission ルールを上書き承認してしまいます。
  • リトライしない。 hook の予算ではリトライが予算を食い潰します。

実装では失敗経路を列挙してテストにする(hook 7 経路 / lint プラグイン 12 経路、いずれも API 不要)。 → 18 §1

6.2 同期の拡張点には「判断を前に出す」

ESLint のルールは await できません。3 つの出口を実測:

事前準備lint 時間(12 ファイル)
事前バッチ + 同期ルックアップwarm pass31 ms(何もしない lint が 36 ms)
ルール内でブロッキング(execFileSync)不要4327 ms(1 ファイル 361 ms)
未判定を指摘して CI を落とす不要31 ms

**バッチの単位は「拡張点が全体を見られる瞬間」**で決まります(ESLint なら Program:exit = ファイル)。 「できる」と「入れて良い」は別。 → 21 §1

6.3 判定ロジックはコードではなくデータに出す

hook の判定規則を .jev に出すと、差し替え可能になり、 規則と理由文の 16 分岐を API なしで検証できます。 ただしそのファイルは信頼された入力です(全部 defer にすればゲートは無効化される)。 設定ファイルと同じ扱いに。 → 18 §6b

6.4 実運用の形に載せることが最後のテスト

オフラインの正解率は質問文の妥当性を保証しません。 23/24 を取った exfiltrates の文言を hook にしたら、1 発目で普通の git push を deny しました。 24 件のコーパスに「正当な外向き転送」が 1 件も入っていなかっただけです。

コーパスに無いクラスの穴はコーパスでは見えません。 → 18 §3

6.5 「成功したように見えるもの」は必ず外で検証する

自動生成したテストとキャッシュは、同じ穴を別の値段で踏みます。

  • クリック列 + 最終 URL のテストは、注文を記録しないアプリに対して緑のまま通ります。 操作が通ったことと結果が起きたことは別です(59)。
  • action キャッシュはそれを無料かつ無音にします。 壊れたアプリの上で 10/10 再生して ゴール到達し、ordered: no で終わりました(62 §6.6)。

終状態はアプリの外側で見る(注文が記録されたか、レコードが増えたか)。 キャッシュも生成テストも、その検証と必ず併用してください。 キャッシュを入れるならキーは判断に使った state の全体にします —— 切り詰めた画面テキストで作ったら、カート個数が窓の外にあって 別のページが同じキーになり、14 手ループしました

6.6 「無駄手 0」は進んでいる証拠ではない

無駄手(画面が変わらない手)を数えるのは有効ですが、それだけではループを検出できません。 総当たりも振動も画面を毎回変えるので、無駄手 0 のまま予算を使い切ります (そういう失敗が 3 回出ました)。

予算の使い切り自体を失敗として数える。 「無駄手が少ない」で健全性を判断しない。


7. 測り続ける

  • record / replay を最初から入れる。 同じ入力で分岐が変わるので、無いとテストが書けません。 実装差とモデルのばらつきを分ける唯一の土台でもあります (閾値の再設計をリクエスト 0 回でやれたのはこれのおかげ)。 → 19 §4
  • 記録に閾値も書く。 無いと閾値を直すたび過去のレポートの数字が黙って書き換わります。 → 22 §11.4
  • ラベルはコード実行で証明できるものに寄せる。 「コード品質」のような審判のいない領域では、 バグ 1 個ごとに関数を実行して壊れていることを示すプローブを付ける (バグのプローブは必ず失敗、それ以外は必ず成功、を API なしで検証)。 証明できない意見は別クラスにして主指標から外す
  • ベースラインを併記する。 不均衡な集合では「何も言わない」が 76.5% を取ります。 精度と再現率を分けて出す。
  • 隠しているものはコードで担保する。 「実装を伏せている」「ラベルを送っていない」は 主張ではなくリクエスト本文の検査で(48 文字窓の一致で例外)。
  • 弱いベースラインで観測した効果を一般化しない。 弱い編成 + Jev が強い編成 + scripted に 5-1 でも、基準を強い bot にすると 0-6 に反転しました。 → 12 §8
  • アームの列挙を疑う。置かなかったアームについては何も言えない。 「消す」と「伝える」を比べるつもりで「伝えるだけ」を置き忘れると、 出る結論は「消すと効く」になります —— 後で足したら同値でした。 → 結論ではなく解釈が静かに間に合わなくなるので、 書く前に「この文が否定される配置はどれか」を 1 つ挙げてください。 → 62 §4
  • 「正直な限界」節を義務にする。これが唯一効いた検出器でした。 この repo を後から全部当たったら、未検証の主張は限界節を書き忘れた本にだけ 残っていました(25〜28 の 4 本)。逆に、限界節のある本は自分で穴を名指ししていて、 17 §6 は「ゴールが曖昧なら §5 は反転するはず」と 予告まで当てています(23 §4 が後に確認)。 → 節を書く作業そのものが、測っていない軸を数える作業です。 最低限「アームはこれで全部か」「n はいくつか」「盤面は誰が作ったか」を書く。
  • 手法を 1 対 1 でしか測っていないなら、重ねたときは別に測る。 素のベースラインに対して効いた手法どうしは、足し算ではなく崖でした。 型付き欠損もジオメトリ欠損も単独なら着く(2/2)のに、両方欠けたときだけ 0/2。 各手法はもう一方が在ることを前提に予算内に収まっています。 → 62 §3.1

8. 行動させるとき ——「選ばせる」と「やらせる」は別物

1〜7 は「判断を 1 つ取る」ための手順です。実アプリを操作させると、 同じ精度の話が別の失敗の仕方をします。 判定なら間違った答えが返るだけですが、 行動は取り消しの要る変更で、しかも足りないものは 精度の劣化ではなく到達不能として出ます。

8.1 action space を先に設計する

最初に決めるのは質問文でも閾値でもなく、操作の集合です。 「この画面で到達したい終状態を、全部この操作の組み合わせで書けるか」を先に確かめます。

CLEAR(フィールドを空にする)を入れずに測ったら、こうなりました:

到達トークン対象フィールドに触ったか
clear あり2/2はい
clear なし0/2+43%一度も触らない

fill() は置換なので値の変更には CLEAR は要りません。 要るのは空という終状態で、これは他のどの操作でも表現できません。

そして落ち方が独特です。ドライバは「正しい要素に間違ったことをする」のではなく、 画面を出て別の経路を探しに行き、ルートを 3 周期しました。 エラーは出ません。 → 操作が足りないことは、精度の数字ではなく「同じ画面に戻ってくる」で現れる。62 §6.7

8.2 target は要素ではなく実行単位にする

選ばせるのは「要素」ではなく「実行できるもの」です。 ドロップダウンなら index:option、テキストなら (index, text)。 「要素だけ指して値は呼び出し側が推測」にすると、値が Jev の外に残ります —— Jev は文字列を返せないので(0)、 その推測はコード側のヒューリスティックになります。

6 択のドロップダウンで測った差:

target の形値は誰が決めるか結果
index:option(対を選ばせる)Jev基準
要素だけコード側「まだ試していない選択肢」+5 手(消去法で着く)
要素だけコード側「今の値と違う最初の選択肢」0/2 —— 非停止(8.4)

2 行目と 3 行目の差はヒューリスティックの出来です。つまり 要素だけを指す形にすると、精度がコード側のフォールバックの質に化けます。

調べた 4 実装すべてがこうしていて、「要素だけ」という形は誰も出荷していません (jev-ultrafast の index:option、playwright-mcp の browser_select_option(target, values)、 stagehand の {selector, method, arguments}、browser-use の InputTextAction{index, text, clear})。 → 62 §1

空 value の <option> を target に出さない。 プレースホルダは値ではありません。 出すと満たした要件を捨てる手が confidence 0.4〜0.7 で名指しされます (自分の移植と上流の両方に同じ穴がありました)。 → 61 §8

8.3 操作と対象は 1 リクエストで同時に聞く

operation操作別の target head を同じリクエストに入れ、 選ばれた操作が指す head だけ読んで残りは捨てます。 CLICK の head にはクリックできる要素だけ、SELECT の head には index:option だけ。

捨てる分は無駄になりません —— 数字と理由は 3 にあります。 ここで効いているのは型で分けたことです。head ごとに候補が絞られているから 後段の質問が簡単になり、投機が当たります。

分けずに 1 問で済ませようとすると、道は 2 つしかありません —— index:option を全部並べる(選択肢数が要素数 × 値数になる。255 の上限に当たります)か、 要素だけを並べて値はコード側で決める(8.2 の表)か。 測ったのは後者で、+5 手か 0/2 でした。

8.4 位置の事実ではなく進捗の事実を渡す

「今どこか」で次の手を選ばせると振動します。 同じ形を 3 回踏みました:

判断材料何が起きたか
「今の値と違う最初の選択肢」"" → a → b → a2 周期。3 番目以降に予算をいくら積んでも到達しない
「下に 145px / 上に 172px ある」上下の 2 周期(18/18 手)
(必要な操作が無いとき)「他の画面がある」ルートの 3 周期(8.1)

いずれも非停止で、遅いのではありません。 → 選択ではなく掃引・列挙にすると止まります(一方向に 1 画面ずつ、 試した選択肢を覚える)。渡すべきは「どこにいるか」ではなく **「何をもう試したか」**です。 → 61 §3 62 §6.5

8.5 削って安くする機構は精度を買わない

転用候補を 5 つ実装して測った決算が、きれいに割れました。

機構結果
型付き fan-out(8.3)採用
clear(8.1)採用
ゴール語彙での候補検索却下 — recall@20 が 1/10(何もせず先頭 20 件なら 10/10)
action キャッシュ却下 — 録画である(6.5)
視野で絞る + SCROLL で担保却下 — トークン −67% は本物だが、効いていた盤面で 2/2 → 0/2

採用した 2 つはどちらも action space の表現力を上げる側で、 却下した 3 つはどちらも候補や質問を削って安くする側です。 → 精度を買えるのは前者だけで、後者が買えるのはトークンです。

個別に覚えておくとよいもの:

  • ゴールとの語彙一致で候補を絞ってはいけない。 ゴールは結果を名指し、コントロールは遷移で名付けられているので語彙が噛み合いません (Continue to delivery はゴールに対してスコア 0)。 画面テキストを足すと全体のスコアが上がって識別が消え、さらに悪化しました。 → 62 §6.1
  • 視野で絞った分は SCROLL では返せない。 節約は「答えが今の画面にある」という ページの性質を担保に借りているので、ページ全体を相手にするなら絞らないでください。 自由な選択なら振動(8.4)、掃引なら答えを通り過ぎます。 → 62 §6.5

8.6 hit-test は判断に入れる、ただし何を渡すかに注意

「押しても効かない候補」は説明文には原理的に書けません(テキストは正常に読めるので)。 document.elementFromPoint 1 回で透明な overlay 下の 14 候補が落ちました。

調べた 4 実装はどれも、これを(自前か Playwright への委譲かは別として) 実行直前のガードとしてしか使っていません —— 聞く前の観測に入れているものは 1 つもありませんでした。 playwright-mcp の --snapshot-boxes だけがジオメトリをモデルに渡しますが、 渡すのは bbox で誰がクリックを受け取るかではありません。

ただし消すか伝えるかはどちらでも同じです(精度は同値、消す側はトークン 4%)。 → 62 §2


やってはいけないことだけの一覧

何が起きるか
順序のある結論を choice で聞く隣接レベルの分割が「低 confidence」に化け、閾値が引けない
noul の criteria をトップレベルに置くサーバーが黙って無視する。エラーは出ない
score の rubric を「ひどさ」で作る軸が混ざって単調にならない
閾値を質問文に書く再校正のたび質問が変わり、過去の測定と比較できない
閉じた世界を仮定する範囲外入力が conf 0.96 で誤ルーティングされる
「該当なし」を choice の選択肢に混ぜる答えのある難問までそこへ逃げる
逃げ道を二重にかける89.8% → 88.0% と下がる
confidence 単独をゲートにするclean も bug も同じ値域に入るので正解ごと捨てる
複数の noul を平均する信号を持つ 1 問が薄まる
原子質問だけで判定する列挙し忘れたクラスに穴が空く
指標を増やして精度を上げようとする7/8 は抜いても失わない。広い 1 個を校正する
共通の閾値を複数の質問に使うスケールが質問ごとに違う。AUC 0.92 の指標が 1 件も拾えない
閾値を誤検出 0 の境界に当てはめる次の draw で越える
例外規定をゲートに渡す逃げ道を探しに行き、見逃しが 0 → 11 件
文脈を足せば精度が上がると思う誤検出は減るが見逃しは増える
ゲートに allow を返させるユーザーの permission 設定を上書き承認する
失敗を allow / deny に倒す黙って承認する / 障害で全停止する
境界に乗った決定をそのまま自動化する些細な入力差で pass/block が入れ替わる
オフラインの正解率で質問文が妥当だと思う23/24 の文言が実運用 1 発目で普通の git push を deny
エージェントに質問を書かせて評価しない同じプロンプトで 10/24〜23/24 に振れる
replay 無しで確率的な実行をテストする期待値が書けず、実装差とモデル差が区別できない
弱いベースラインの効果を一般化する基準を上げると 5-1 が 0-6 に反転する
審判のいない領域を正解率で語る「何も言わない」が 76.5% を取る
バッチ上限を質問数だと思う1220 問は通る。詰まるのは state 32Ki が先
gap が狭いのを閾値で直そうとする答えが分かれていないので校正の外。質問を書き直す
subject から見えない条件を質問文に書く全部の答えが真ん中に来る(gap 0.28)
決定的な絞り込みを狭く書く当たらなかったものはどの閾値でも質問されず、レポートにも出ない
必要な操作を入れないまま精度を測る精度が落ちるのではなく到達不能になる。しかも対象に一度も触らず周回する
要素だけを target にして値はコード側で推測する6 択で +5 手。記憶を持たせないと非停止
空 value の <option> を target に出す満たした要件を捨てる手が conf 0.4〜0.7 で名指しされる
位置の事実で次の手を選ばせる2 周期の振動になり、3 番目以降に到達しない
ゴールとの語彙一致で候補を絞るrecall@20 が 1/10。何もせず先頭 20 件なら 10/10
視野を絞った分を SCROLL で返せると思う効いていた盤面で 2/2 → 0/2
action の結果をキャッシュして再生する壊れたアプリの上で 10/10 再生してゴール到達する
キャッシュのキーを切り詰めた state で作る別のページが同じキーになり、14 手ループした
操作が通ったことを結果が起きたことと同一視する注文を記録しないアプリでもテストは緑のまま通る
「無駄手が 0 だから進んでいる」と思う総当たりも振動も画面を変える。無駄手 0 で予算を使い切る
置かなかったアームについて結論を書く「消すほうが効く」が一度も検証されないまま通る。後で足したら同値だった
1 対 1 で効いた手法を重ねて足し算だと思う足し算ではなく崖。単独なら 2/2、両方欠けると 0/2

最後に — 何が得意で何が苦手か

得意: 関数やデータが自分で名乗っている契約との齟齬。 空トークン同士が認証を通る、10% 引きを 10 円引きにする、書き込み失敗を握り潰す、 クエリを未エンコードで連結する。読めば辻褄が合わないもの。

苦手: 特定の API の挙動を知らないと見えないもの。 .sort() は比較関数なしだと辞書順、splice(at) は末尾まで削除、/g なしは 1 回だけ、 返した promise は try を抜ける。正しい問いを名指しで投げても 0.09〜0.22 で「いいえ」。

型・既存ルール・テストの代わりにはならず、ちょうど補完になる。 そして指標を名指しする価値は苦手な側にだけあります(得意な側は naming の有無にかかわらず 15/15)。 → 21 §3 22 §11.1

行動させる側では、得意/苦手の前に「表現できるか」が来ます。 判定は答えが返るので当たり外れで測れますが、行動は 表現できない終状態は当たりも外れもなく到達不能です(8.1)。 だから8 の結論は 「安くする」ではなく「手数を減らす」に寄りました —— 1 手は数セント未満ですが、1 手は実アプリに対する変更で、失敗すれば取り消しが要ります。 トークンを 67% 落とす機構は担保付きの借金で、手数を 5 手減らす機構は素の利益でした。