LAB NOTES #7

黙って間違えないオーケストレータ — 20,000回まわして分かった、効いたことと効かなかったこと

2026-08-03 ・ 生データ全公開: teai.io/api/v1/bench

LAB NOTES #4 で「安いモデルを2つ束ねたら単体最強に並んだ」を書きました。今回はその続きです。目標を「正答率」から「黙って間違えない率」に変えました。

LLM の一番困る壊れ方は、間違えることではなく間違えたのに自信満々で返すことです。受け取る側に見分けがつきません。そこで100%正しく答えるのは諦めて、「答えるときは正しく、怪しいときは怪しいと言う」に目標を変えました。

結果(182問・実走3回)

実走正答要確認黙って間違えコスト
1回目99.5%1.10%0件$0.173
2回目100.0%1.10%0件$0.176
3回目100.0%1.65%0件$0.168

ただしこれを「間違えないモデルができた」とは書きません。コードで検算できたのは正解の76%で、残り24%は素通りしています。標本は182問×数回なので、364件で0件が抑えるのは真の発生率およそ0.8%まで(95%)。0%の証明ではありません。

一致だけでは足りなかった

最初は「ベンダーの違う2モデルが一致したら正しい」で作りました。89% → 99.7% まで上がります。残った誤答を全部見たら、4件すべてが「2モデルが同じ間違いをして一致した」ものでした。

設問正解両モデル誤りの型
税込33,333円(8%)の税抜・切り捨て3086330864切り捨てを切り上げた
45.9㎡は何坪(小数第3位四捨五入)13.8813.89切り上げた
2026-02-26 の翌日から4営業日目03-0403-022日早い
2026-05-29 の翌日から4営業日目06-0406-022日早い

誤りは端数処理の方向営業日の数え方の2種類だけ。どちらも「よくある慣習」なので、ベンダーを分けても同じ癖を共有して揃って外します。一致を信号にする設計の構造的な穴でした。

コードは答えを作る側でなく、検算する側に置く

最初はコードで計算させて結果を採用しました。黙って間違える率が 0.27% → 4.12% に悪化しました。抽出条件が外れると、コードが自信満々に間違えるからです。

役割を逆にしました。コードは答えを作らず、モデルの答えを検算するだけ。判定は3値です。

肝は Unknown を「正しい」と混同しないこと。そして「動かない」が既定なので、抽出が外れても答えは壊れません。壊れ方は「不要な⚠️が出る」だけで「間違った答えを返す」にはならない。この非対称性のおかげで、検算の範囲を安心して広げられます(48% → 76%)。

法令の数値はコードに焼き込まなかった

検算を広げた後も黙って間違えが残りました。「継続勤務1.5年の年次有給休暇は何日か」で、正解11日に対し安い2モデルが揃って「10」。設問文に表が無い、つまり暗記の問題です。

労基法39条の付与日数表をコードに書けば一発で直ります。やりませんでした。法改正のたびに、正しい答えを弾く側に回るからです。代わりに「答えの数値が設問文の数から四則演算で導けるか」を判定し、導けなければ記憶依存=危険側として裁定役へ上げました。これで黙って間違えは6回連続で0件になりました。

選定と検証を分けたら、最良に見えた構成が崩れた

14モデルの回答テキストを1回集めておけば、束ね方の評価に追加のAPI呼び出しは要りません。1,820構成を総当たりしました。

ただし1,820から「一番良いもの」を選べば、測定のばらつきに合っただけの勝者が必ず出ます。そこで選定は片方の実行回だけで行い、もう片方(選定に使っていない回)で検証しました。

「安い1本 → 必要なときだけ上げる」構成正答要確認黙って間違え
選定に使った回100.0%0.0%0.00%
検証した回98.9%0.0%1.10%

選定回では全1,820構成で最良でした。正答100%、要確認0%、黙って間違え0%。検証回で1.10%黙って間違えました。裁定役の答えを他と突き合わせずに受け入れる構造なので、裁定役が外れるとそのまま出ます。この手順が無ければ本番に入れていました。

裁定役を替えたらコストが半分になった

裁定役正答(2回)黙って間違え要確認コスト
gemini-3.1-pro(変更前)100.0 / 99.5%0.00 / 0.00%1.1%$0.340
inkling(変更後)100.0 / 100.0%0.00 / 0.00%1.1%$0.179

効かなかったこと

プロンプト調整は汎用的には効かない

8変種 × 3モデル × 182問 × 2回 = 8,736回。同じ指示がモデルによって逆に働きます。

指示flashinklingglm-5.2
端数処理の桁と方向を書き写してから適用+3.3+0.8−2.2
日付や個数は1つずつ書き出してから数える+3.0+1.4−1.9
設問文に表があれば使う行を引用する−6.6+0.8−0.8
手順は指示せず「急がず見直せ」だけ−7.1+1.4+1.9

「急がず見直せ」は glm-5.2 を +1.9 にし、flash の日付設問を 58% → 0% に壊しました。効くのは「そのモデルが実際に持っている手順の穴」に当てたときだけです。flash の「祝日表つき営業日」は列挙させると 58% → 95%(+37pt)、日付以外への影響は ±0 でした。

部品を良くしたら、全体が悪くなった

では flash に列挙を指示すれば全体も良くなるはず — と思って実走しました。

構成正答要確認コストp90
現行(全部baseline)98.4〜100%0.55〜3.30%$0.171〜0.1836.4〜11.7s
flash だけ列挙98.4〜99.5%3.30〜5.49%$0.206〜0.22714.4〜14.7s
3本とも列挙98.9〜99.5%5.49〜8.79%$0.189〜0.2045.0〜13.2s

列挙させると出力が長く・形が揺れるため、もう1本との一致が減り、裁定役への持ち上げが増えます。黙って間違えは3構成とも0件でしたが、要確認とコストと遅さが増えるだけでした。本番は現行のまま据え置きました。

自分たちのベンチマークが間違っていた(5件)

モデルが揃って間違えたように見えたときは、まず自分の正解を疑うほうが当たります

設問何を間違えたか
源泉徴収 1,234,567円100万円超の税率が変わることを無視した正解を置いていた
割増賃金 時給1450円×9時間端数処理を指定せず単純な切り捨てを正解にしていた。実務の慣習(昭和63.3.14 基発150号)を知っているモデルほど不正解になった
容積率 45㎡×150%67.5 を 67 に切り捨てていた。指定容積率に切り捨ての定めはない
コード「空白の畳み込み」テスト入力を二重にエスケープし、タブではなくバックスラッシュ文字を渡していた。全28回とも不正解になった
コード「中央値」浮動小数の丸めを避けることを仕様文に書いていなかった。素直な実装が全部落ちた

5件とも正しく答えたモデルを不正解にする形をしています。「高いモデルが最安モデルに負けた」と色めき立って中身を見たら、負けていたのはこちらでした。コードの2件を直したら、コード実装の正答率は 62% → 94〜100% に変わりました。

生データ

認証不要・CORS 開放です。設問と正解ごと出します。正解を隠したまま「うちのベンチではこうでした」は検証できません。実際この評価セットの誤り5件は、外から見れば分かるものでした。

curl https://teai.io/api/v1/bench                    # 全結果 + 1,820構成の評価
curl https://teai.io/api/v1/bench/eval/jp-business   # 評価セット(設問と正解)

表で見るなら teai.io/bench。実装の詳しい話は teai.io/blog/sure-mode

使い方

curl https://teai.io/v1/chat/completions \
  -H "Authorization: Bearer $TEAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"shitate/orchestrator:sure","messages":[{"role":"user","content":"..."}]}'

怪しいときは答えの頭に ⚠️ 要確認 が付きます。付かなければ4段の関門を通っています。

前回: LAB NOTES #4「安いモデルを2つ束ねたら、単体最強に並んだ」