---
title: "追わない技術 第2回：一番安いモデルを使わない技術"
author: garplab
publisher: TypingTube
license: CC BY 4.0
license_url: https://creativecommons.org/licenses/by/4.0/
license_scope: 「CC BY 4.0」の印から始まる節（仕組み・検証手順・コード）。印の無い本文は著作権を留保
canonical: https://typing-tube.net/articles/owanai-02-no-cheapest-model
series: "追わない技術"
language: ja
---


今回は、**どのモデルに渡すかを決める時点**の話です。前回は「いつ止めるか」でしたが、今回は「**誰に渡すか**」になります。⚠️ 先に、題を否定しておきます。**安いモデルは、使うべきです。**私も使っています。⭐⭐ **やめたのは、一番安いのを「既定」にすることのほうです。**

⚠️ もう 1 つ断っておきます。**この回にもモデルの名前も、価格も、性能の数字も出てきません。**半年で古くなるものは、この連載では書かないと決めています。比べるものは「**高い側**」と「**安い側**」とだけ呼びます。⭐ そのかわり、**倍率**は全部出します。あなたの手元の単価を掛ければ、そのまま判断に使えるはずです。

⭐⭐ そして白状します。**私はこの検証で 2 度、「差が無い」と読み間違えました。**どちらも数字は正しく、読み方が間違っていました。⚠️⚠️ **2 度とも、指摘を受けて初めて気づきました。**そのままなら「杞憂でした」と書いていたはずです。今回はその 2 回も含めて書きます。

一つ思い出してみてください。**あなたが安いモデルに任せた仕事は、本当に安く終わりましたか。** ⚠️ **やり直しの回数まで、数えたうえで言えますか。**

## 予測から始めました —— 「安いほうは、見逃しが増えるはず」

最初に立てた予測はこうです。

> **安いモデルにテストを書かせると、テストは書けるが、見逃しが増える。**

⭐ 題材にテストを選んだのには理由があります。**テストは、失敗が緑で隠れる唯一の成果物**だからです。実装のバグはいつかどこかで落ちますが、**テストの見逃しは何も言いません。**「テストを書かせた」は緑で完了に見え、請求は後から来ます。

物差しは、**過去に実物へ入れて、実物の検査が実際に落とした変異 8 種**を当て直したものにしました。⚠️⚠️ **この実験のために変異を書いてはいけません。**書いた瞬間、見逃し率は書き方ひとつで 0% にも 100% にもなります。

⭐ 比べる相手も置きました。**同じ対象に、人（私）が書いた既存のテスト**です。⚠️ そちらは **8 種のうち 6 種**しか落としません。**天井は満点ではありません。**

条件は 2 つだけです。**モデル**（高い側 / 安い側）と、**範囲**（`call` 1 メソッドに絞る / 「必要だと思うテストを書いて」だけ）。⚠️ **観点は 1 つも渡していません。**

## 1 回目 —— 安い側は、半分が走りませんでした

会話を引き継がない作業者を 12 体そろえて書かせ、変異を入れる前に、まず素のまま走らせました。

| | 走った体 | 走った体だけの検出（8 種のうち何種を落としたか。⚠️ **多いほうが良い**） |
|---|---|---|
| 高い側 | **6 / 6** | 3.33 / 8 |
| ⚠️ 安い側 | **2 / 6** | 3.00 / 8 |

⚠️ 走らなかった 4 体の中身は、**存在しないアサーションを呼ぶ・辞書の行を重複させる**といった壊れ方でした。⭐ **どれも、走らせた瞬間に分かります。**

⭐ そこで私は、走らなかった 4 体を「見逃しとは別枠」として、**検出の集計から外しました。**理屈も一応ありました —— **走らないテストは、その場で気づけて、高いほうへ切り替えられます。**

⚠️⚠️ **これが 1 つ目の間違いです。**

## 間違い 1 —— 走らなかったテストを、母数から落としていました

外した結果、残った安い側は **2 体だけ**です。**その 2 体の平均を「安い側の実力」として、高い側 6 体と並べていました。**

> ⚠️⚠️⚠️ **走らなかったものを母数から落とすと、いちばん危ない側が消えます。**

⭐ 直し方は簡単でした。**現実と同じように差し戻したのです。**エラーの出力だけを返して「直してください」と送る。⚠️ **何が悪いかは言いません**（言えば、それは物差しを渡すことになります）。

⭐⭐ 差し戻しを重ねて、**12 体とも緑にしました。**そのうえで数え直すと、こうなりました。

| | 範囲を絞る | 絞らない |
|---|---|---|
| 高い側 | 3.33 / 8 | 3.33 / 8 |
| 安い側 | **3.33 / 8** | 2.33 / 8 |

⭐⭐ **範囲を絞れば、安い側は高い側と同点です。**私はここで、こう書こうとしました ——「**安いモデルで増えるのは、その場で気づける失敗のほうです。見逃しは増えません。**」

⚠️⚠️ **これも間違いでした。**

## 間違い 2 —— 物差しに、天井がありました

8 項目のうち **3 項目は、12 体とも 0** でした。⚠️ **人が書いた既存のテストは、その 3 つを落とします。**

⭐ 3 つを並べてみると、共通点がありました。**どれも「材料か測り方を、先に自分で用意しないと書けない」項目**です。

- 表の中でいちばん長い見出し語を、自分で辞書に入れる
- 同じ表記に読みを 2 通り持たせる
- 戻り値ではなく、**問い合わせの本数**を数える

⚠️⚠️ **課題は、そんなことを一言も言っていませんでした。**「単体テストを書いてください」としか書いていない。⭐ つまり **12 体とも 0 だったのは、書けなかったからではなく、課題がそこを指していなかったから**かもしれない。

そこで、**課題に 1 段落だけ足しました。**

> **`setup` で作る辞書に現象が入っていなければ、そのテストは何も見ていません。**
> 見たい振る舞いが出せるように、辞書のほうを組み立ててください。
> **戻り値を見るだけでは分からない振る舞いもあります。**そういうものは、測り方のほうを用意してください。

⚠️ **8 項目はどれも名指ししていません。**言っているのは**やり方**だけです。⚠️⚠️ **そして両方の条件に、1 バイトも同じだけ渡しました**（条件の差を増やしてしまうと、測っているものが変わります）。

## 天井が外れたら、結論がひっくり返りました

もう一度 12 体に書かせました。**3 項目とも動きました。**

| | 1 回目（天井あり） | ⭐ 2 回目（天井なし） |
|---|---|---|
| 高い側 | 3.33 / 8 | **5.00 / 8** |
| 安い側 | 2.83 / 8 | **2.83 / 8** |
| 高い側・範囲を絞る | 3.33 / 8 | **5.00 / 8** |
| ⚠️⚠️ 安い側・範囲を絞る | 3.33 / 8（同点） | **3.00 / 8**（同点ではない） |

⭐⭐⭐ **天井を外すと、高い側だけが伸びました。安い側は 2.83 のまま動きません。**

> ⚠️⚠️ **「絞れば同点」は、両方が簡単な項目しか取れず、そこで並んでいただけでした。**

⚠️ ついでに、**範囲の絞り方は見逃しには効きませんでした**（高い側は 5.00 と 5.00 で差 0、安い側は 3.00 と 2.67）。⭐ **1 回目で範囲が効いていたのは「そもそも走るか」のほう**で、見逃しの側ではありません。

⚠️⚠️ **最初の予測は当たっていました。**ただし、**当たっていると分かるまでに、母数の落とし方と物差しの天井を 2 つとも直す必要がありました。**

## 差し戻しは、テストを消させます

差し戻しの回数は、**高い側が 2 回の実験とも 0 回、安い側は 1 回目が 6 回・2 回目が 10 回**でした。⭐ ここまでは想像どおりです。

⚠️⚠️⚠️ **想像していなかったのは、直し方のほうです。**「実装は正しい。テストを直してください」と送ると、**返ってきた直しのうち 3 件は、赤いテストを消したものでした。**

- 「上限ちょうどは通す」という 1 件を、**削除**
- 全角の正規化を見る 1 件を、**削除**
- 重複した辞書の行を消さず、**例外だけを飲む書き方に変更**

⭐ どれも緑になります。⚠️⚠️ **そして、消えた 1 件はそのまま見逃しになります。**

> ⚠️⚠️⚠️ **「実装は正しい」と言って差し戻すと、直すより消すほうへ倒れます。**

⚠️ これはモデルの話ではなく、**差し戻し方の話**です。⭐ ただし、**差し戻しが要ったのは安い側だけ**でした。だから結果としては、モデルの側に効いています。

## 原理: 安いかどうかは「単価の比 × 量の比」で決まります

⭐ ここまでの数を、判断に使える形へ畳みます。**1 回の応答の値段ではなく、1 つの仕事が終わるまでの総額**で見ます。

同じ課題で、安い側が高い側の何倍を使ったかです。

| 数えたもの | 範囲を絞る | 絞らない | 全体 |
|---|---|---|---|
| 初めて読ませた量 | 1.64 倍 | 1.24 倍 | **1.48 倍** |
| ⭐ 使い回せた量 | 9.71 倍 | 2.62 倍 | **5.29 倍** |
| 書いた量 | 0.26 倍 | 0.38 倍 | **0.31 倍** |
| 手番（差し戻し込み） | 3.33 倍 | 2.00 倍 | **2.67 倍** |

⚠️⚠️ **安い側は、単価が安いだけではありません。量そのものが多いのです。**やり直すたびに会話が伸び、伸びたぶんが次の手番でもう一度流れます。

⭐⭐ **使い回した部分は安く課金されますが、ゼロではありません。**ここを 0 と見なすと、リトライは「ほぼタダ」に見えます。⚠️ **5.29 倍のものを 0 と数えれば、どんな結論でも作れます。**

⭐ **書いた量だけは安い側のほうが少ない**（0.31 倍）ので、**出力の重い仕事なら向きが変わります。**

> ⭐⭐⭐ **値段は「単価の比 × 量の比」。単価の比はカタログに書いてありますが、量の比は測らないと分かりません。**

⚠️⚠️ **だから「一番安いから既定」は、片方だけを見た判断です。**⭐ 同じ理由で「一番高いから既定」も片方だけです。**既定を置かず、用途ごとに 2 つとも数えます。**

⚠️ 前作で、私は**翻訳の用途では安い側を使う**と書きました。**その判断はいまも変えていません。**あの用途では、安い側のほうが手数まで少なかったからです。⭐ **今回の結果は、それを打ち消しません。**打ち消さないことこそが要点で、**用途が変われば向きも変わる**というだけです。

## 検証手順: 単価の比と、量の比を、別々に数える

**前提**

- 同じ課題を、**2 つのモデルへ同じ材料で渡せる**こと。⚠️ 材料が 1 バイトでも違うと、モデルの差ではなく材料の差を測ります
- **合否が機械で出る物差し**があること。⚠️⚠️ 人が読んで採点すると、**測る回数が費用になって続きません**
- **使ったトークンが、新しく読ませた量・使い回した量・書いた量に分けて残る**こと。⚠️ 分かれていないなら、この手順の後半は使えません

**所要時間**: 半日（物差しが既にある場合）

**手順**

1. 物差しにする変異を、**過去に実物へ入れて実物の検査が落としたもの**から採ります。⚠️⚠️ **この実験のために書いてはいけません**
2. **人が書いた既存のテストを、同じ物差しに掛けます。**⭐ これが天井です。⚠️ 満点なら物差しが緩いので、変異を足してください
3. 同じ課題を **2 つのモデル × 2 つの範囲 × 3 回**渡し、書かせます。⚠️ 走らせるのはこちらで、書き手には走らせません
4. **変異を入れない状態で全部を走らせます。**⚠️⚠️ **赤い体を「集計から外す」のは禁止です。**エラーの出力だけを返して差し戻し、**全部が緑になるまで繰り返します**
5. 緑になったものへ、**変異を 1 種ずつ当てて走らせます。**⭐ 変異 1 種につき全員ぶんを 1 回で回せます
6. **差し戻しの回数**と、**新しく読ませた量・使い回した量・書いた量**を、モデルごとに合計します
7. 手元の単価表を掛けて、**完了までの総額**を出します

**合格条件**（すべて満たすこと）

- 手順 2 で、**人の書いたテストが落とせない変異が 1 つ以上ある**（満点なら天井です）
- 手順 4 のあと、**全部の体が緑**である（1 体でも赤いまま数えると、いちばん危ない側が母数から消えます）
- 手順 5 で、**全員が 0 の項目が、全体の半分未満**である。⚠️⚠️ **半分以上あるなら、課題がそこを指していません**（材料と測り方を自分で用意する、と課題に書き足してください）
- 手順 6 で、**使い回した量が 0 でない**（0 なら分けて取れていません）

**合格しなかったとき**

- **人のテストが満点** —— ⚠️ 物差しが緩いか、実物の検査が落とした変異を採っていません
- **赤い体が緑にならない** —— ⭐ そこが「高いほうへ切り替える」判断点です。⚠️ **回数を費用として記録してから切り替えます**
- **全員が 0 の項目が半分以上** —— ⚠️⚠️ 天井です。**課題の骨に 1 段落足して、両方の条件へ同じだけ渡し、測り直します**
- **使い回した量が 0** —— ⚠️ 記録が分かれていません。分けて取れないなら、**量の比は出さずに手番だけで判断します**

**後始末**

- 一時的に置いたテストを消します。⚠️ **記録（トークンの内訳）は残します**
- ⚠️ 結果は**日付つきで残します**。記録は増え続けるので、**同じ道具を明日走らせると数が動きます**

## 注意: この数字が言えないこと

⚠️⚠️ **1 つの枡が 3 体しかありません。**向きだけを読んでください。**5.00 と 2.83 の差は、順位としては読めますが、倍率として持ち出せる精度ではありません。**

⚠️ **比べたのは 2 点だけ**です。高い側と安い側を 1 つずつ選んだので、**「安いほど落ちる」という直線は、ここからは引けません。**

⚠️⚠️ **8 項目のうち 1 つは、天井を外したあとに 12 体とも 0 になりました**（⭐ **天井があったときは、12 体のうち 1 体が落としていた項目**です）。**人が書いたテストも、それは落とせません。**⭐ つまり**まだ天井が残っています。**「どこまで差があるか」は、今回の物差しでは測り切れていません。

⚠️ **差し戻しは「実装が正しい」という前提で送っています。**⭐⭐ **その前提そのものが、テストを消させました。**実務では実装が間違っていることもあるので、**この差し戻し方をそのまま真似しないでください。**

⚠️ **書き手にテストを走らせていません。**⭐ 走らせられれば、安い側の赤は自分で気づけたはずです。**「走らせない丸投げ」の数字だと思ってください。**

⚠️⚠️ 最後に、**題材のコードは、コメントがかなり厚いものでした。**壊したときに何が起きるかまで、実装自身が書いています。⭐ **コメントの薄いコードでは、両方とももっと落ちるはずです。**そのまま持ち出せる数ではありません。

## 既定にしなくなったもの

- ⭐⭐ **「安いほうで足りるだろう」で渡すのをやめました。** ⚠️ それは予測です。**単価の比はカタログにありますが、量の比は測らないと出ません**
- ⭐ **走らなかった結果を、集計から外すのをやめました。** ⚠️⚠️ 外した瞬間、**いちばん危ない側が母数から消えます**。差し戻して、全部を緑にしてから数えます
- ⚠️ **「差が無い」と書く前に、物差しが動くかを見るようにしました。** ⭐ **全員が 0 の項目が半分あるなら、それは差が無いのではなく、測れていないだけです**
- ⚠️⚠️ **「実装は正しい。テストを直せ」とだけ言って差し戻すのをやめました。** ⭐ 消して緑にする道が、そこに開いています
- ⭐ **やめていないこともあります** —— **用途ごとに安い側を選ぶこと**です。⚠️ 前作で選んだ用途は、いまも安い側のままです

安いモデルは安い、という感じは、たいてい正しく見えます。⚠️ **請求のうち、1 回ぶんの単価しか見ていないからです。** ⭐⭐ **見えないのは、やり直した回数と、そのたびに読み直された会話のほうでした。**

---

次回は「最新モデルを使わない技術」。**私は、新しいモデルが出た日に乗り換えるのをやめました。**強いモデルでないと通らなかった仕事が、モデルを変えずに通るようになったからです。
