ブログに戻る

AI入門ガイド

20件のケースでプロンプトをテストする:Pythonでの完全一致採点

20件のテストセットを作り、短いPythonスクリプトでプロンプトを完全一致で採点し、プロンプトの変更が実際に役立ったかどうかを確認します。

投稿者: CheapTokens Team公開日 読了時間 8 分

カバー:モデルの答えを期待するラベルと比較するコードスニペット、「20件のケースでプロンプトをテストする」というタイトル
このページの内容
  1. テストセットを作る:20件のメッセージと期待する答え
  2. スクリプト:各ケースを送り、比較し、外したものを出力する
  3. 1つだけ変えて、もう一度実行する
  4. 同じセット、別のモデル:スコアは保たれるか?
  5. 完全一致が通用しなくなる場所

プロンプトが試した2つのメッセージでうまくいったからといって、それが機能する証拠になるでしょうか?まだです。既知の答えがある実際のメッセージ20件で実行し、いくつ正解するかを数え、外したものを読みましょう。短いPythonスクリプトでカウントでき、プロンプトを変更したときに、その変更が役立ったかどうかを勘に頼らず同じスクリプトで教えてくれます。

Anthropicの成功基準の定義と評価の構築に関するドキュメント(2026年11月7日確認)は、独自の言葉でこう述べています。テストはタスクに固有のものにし、厄介なケースを含め、手作業で採点する少数の質問よりも自動採点の質問を多くすることを優先せよ、と。以下は、今日実行できるその最小バージョンです。

4つのステップ:20件のケースをモデルに送り、答えを完全一致で比較し、精度スコアと外したケースを得る
手法全体:ケースを入力し、1つの数値と外したケースのリストを出力

テストセットを作る:20件のメッセージと期待する答え#

顧客のメッセージをポジティブ、ネガティブ、混在の3つの山に分類するとします。cases.csvというファイルを開き、実際に受け取りうるメッセージを20件書き出し、それぞれにあなた自身が付けるラベルを書きます。何かを実行する前にラベルを書いてください。モデルの答えを「正解」としてコピーすると、テストはモデルに同意するしかなくなります。

csv
text,label
"Arrived a day early and works perfectly. Thank you!",positive
"The charger stopped working after two days.",negative
"Great screen, but the battery barely lasts until lunch.",mixed
"Support answered in five minutes and fixed it.",positive
"I waited three weeks and nobody replied.",negative
"Oh great, another update that deletes my settings.",negative
"Nice design. Pity about the price.",mixed
"Exactly what I ordered.",positive
"The box was crushed, but the headphones inside are fine and sound great.",mixed
"Refund took 10 days. Never again.",negative
"Love it!!",positive
"Cheaply made, but honestly it does the job for the price.",mixed
"Worst purchase of the year.",negative
"Five stars. Would buy again.",positive
"Fast delivery, wrong colour.",mixed
"It's fine. Not amazing, not terrible.",mixed
"Zero issues in six months of daily use.",positive
"The app crashes every time I open the camera.",negative
"Thanks for nothing.",negative
"Setup was a pain, but once it ran, it's been rock solid.",mixed

内訳を見てください:ポジティブ6件、ネガティブ7件、混在7件。皮肉な表現(「Oh great, another update…」「Thanks for nothing.」)と境界線上のもの(「It's fine. Not amazing, not terrible.」)は意図的に入れています。明らかなメッセージだけで作ったセットは100%を記録し、何も教えてくれません。実際のものは自分の受信箱、チケット、チャットのエクスポートから取ってください。エッジケースは、あなたがすでに厄介だと知っているものです。

なぜ20件か?これは開始時のサイズであり、ドキュメントの数字ではありません(彼らの例では1,000件のツイートを使っています)。20件は手書きで30分で書けるほど小さく、1つの幸運な答えがスコアを決めてしまわないほど大きいのです。

スクリプト:各ケースを送り、比較し、外したものを出力する#

計画は4つのステップで、コードはそれを1行ずつたどります。

順番に並べる

評価ループを順に並べる

  1. CSVから20件のケースを読み込む
  2. 各メッセージをプロンプトとともにモデルに送る
  3. 答えをラベルと比較する
  4. 精度と失敗したケースを出力する

このループは、最初のモデル呼び出しの前にラベルが存在する場合にのみ機能し、失敗が最後に来るのは、それがあなたが読んで行動するものだからです。

SDKはpip install openaiでインストールし、キーはCHEAPTOKENS_API_KEYという環境変数に保持します。OpenAI互換のAPIならどれでも同じように動作します。ここではスクリプトがCheapTokensとGLM-5.3-Flashを指しています(セットアップの詳細はドキュメントにあります)。

python
import csv
import os
from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'zai-org/GLM-5.3-Flash'

PROMPT = (
    'Classify the customer message as positive, negative or mixed. '
    'Answer with one word only.'
)


def ask(text):
    r = client.chat.completions.create(
        model=MODEL,
        temperature=0,
        max_tokens=100,
        messages=[
            {'role': 'system', 'content': PROMPT},
            {'role': 'user', 'content': text},
        ],
    )
    return r.choices[0].message.content or ''


with open('cases.csv', newline='', encoding='utf-8') as f:
    cases = list(csv.DictReader(f))

correct, failed = 0, []
for case in cases:
    got = ask(case['text'])
    ok = got.strip().strip('.').lower() == case['label']
    correct += ok
    if not ok:
        failed.append((case['text'], case['label'], got.strip()))

print(f'Accuracy: {correct}/{len(cases)} = {correct / len(cases):.0%}')
for text, want, got in failed:
    print(f'- {text!r}: expected {want}, got {got!r}')

3つの詳細はもう一度見る価値があります。

  • **temperature=0**は答えをできるだけ安定させ、スコアの変化がサイコロではなくプロンプトから来るようにします。
  • **max_tokens=100**は1語のラベルには過剰すぎます。答えが空で返ってきたり途中で切れたりした場合は、finish_reasonを確認してください。finish_reason "length" ガイドに何を増やすべきかが示されています。
  • 採点の行はドキュメントのレシピで、strip().lower()を正解と比較し、さらに1つ追加しています。末尾のピリオドも取り除きます。なぜなら「Negative.」と「negative」はあなたにとって同じ答えだからです。

実行すると何が見えるでしょうか?精度の行が1行、そして外したケースごとに1行です。最初の実行でこう出力されたとします:

text
Accuracy: 14/20 = 70%
- 'Oh great, another update that deletes my settings.': expected negative, got 'positive'
- "It's fine. Not amazing, not terrible.": expected mixed, got 'positive'
…

(ここでの数値は出力の形を示すための仮定です。あなたの結果は異なります。)皮肉や境界線上の表現は、短いプロンプトが外しがちなメッセージです。ポイントは、もう推測する必要がないということです。リストがあるのです。

1つだけ変えて、もう一度実行する#

ここからが役立つ部分です。外したケースを読み、原因を1つ選び、プロンプトを1か所変更して再実行します。一度に1つの変更にしてください。そうしないと、どの編集がスコアを動かしたか分かりません。

比較する

プロンプト v1 → v2:ルールと2つの例を追加

v1

text
Classify the customer message as positive, negative or mixed.
Answer with one word only.

v2

text
Classify the customer message as positive, negative or mixed.
Sarcasm counts as negative. Praise plus a complaint is mixed.
Answer with one word only.

Message: Lovely colour, but it arrived with a scratch.
Answer: mixed

Message: Sure, thanks a lot. Now my order is lost.
Answer: negative

2つの例は架空のもので、それは意図的です。cases.csvのメッセージを例としてプロンプトに入れないでください。そうするとモデルはあなたが渡した答えで採点され、スコアが水増しされます。これはfew-shot技法です。テストセットが、それが報われたかどうかを教えてくれます。v1プロンプトがまだ曖昧なら、測定する前に良いプロンプトの書き方から始めましょう。

v2をPROMPTに貼り付け(複数行には三重引用符が使えます)、スクリプトをもう一度実行します。Accuracy: 17/20 = 85%と出力されたとします。3件多く正解したので、v2は良さそうです。それをどれだけ信頼すべきでしょうか?20件のセットでは各ケースが5ポイントの価値があるので、70% → 85%は3件のメッセージです。これは良いヒントであり、証明ではありません。スコアが下がったなら、同じくらい価値のあることを学び、より悪いプロンプトを出荷するのを避けられたのです。

同じセット、別のモデル:スコアは保たれるか?#

同じファイルが2つ目の問いに答えます。このタスクに必要以上のモデルにお金を払っていないか?1行変えてもう一度実行します:

python
MODEL = 'deepseek-ai/DeepSeek-V4-Flash-0731'

20件のケースでの精度が一致すれば、リーダーボードではなく自分のデータから、このタスクの答えが出たことになります。下がったなら、何かを決める前にどのメッセージが壊れたかを見てください。

これにはいくらかかるのでしょうか?ケースあたり約150入力トークン(指示とメッセージ)と、1語のラベルに約5出力トークンと仮定します。20件のケース1回の実行で3,000入力と100出力トークンになり、GLM-5.3-Flashで$0.000064になります。セットを10回再実行すると200リクエストです。下の表はそのワークロードをすべてのモデルについて示し、OpenRouterを並べています。このワークロードでは、入力と出力を合計すると、表のすべてのモデルがOpenRouterの参照価格より安くなります。

モデル リクエストあたり 200 リクエスト OpenRouter、200 リクエスト
GLM-5.3-Flash $0.0000032 $0.00064 $0.0011
DeepSeek-V4-Flash $0.0000028 $0.00055 $0.0022
MiniMax-M2.7 $0.0000083 $0.0017 $0.0198

リクエストあたり 150 入力トークンと 5 出力トークン。100万トークンあたりの価格は現行のもので、当社の料金体系に合わせて変動します。OpenRouter 列は同じワークロードの参考価格を示しています。

この記事ではそのうち2つを使っています。GLM-5.3-Flashは390Kコンテキストと回答あたり最大8,192出力トークン、DeepSeek-V4-Flashは380Kコンテキストと最大32,768出力トークンで、1日いつでも同じ価格、他のすべてのモデルと同じ残高です。現在の価格は価格ページに、全リストはモデルにあります。

完全一致が通用しなくなる場所#

完全一致が機能するのは、正解が固定リストの1語だからです。要約、書き換え、説明などの自由形式の出力には、比較する単一の文字列がありません。ドキュメントは別のルート、モデルによる採点を説明し、答えを書いたモデルとは別のモデルを採点者として使うことを推奨しています。それでもまずは完全一致から始めましょう。短く固定された答えを強制できる場所では、それが実行できる最も安く明確なテストです。ドキュメントの経験則は、自動採点の質問が多いほど手作業採点の少ないものより優れているというものです。

もう1つの習慣:v2が改善しなくなったら、同じ20件のメッセージに対して磨き続けないでください。5〜10件の新しいもの、理想的には本番環境からの実際の外したケースを追加し、もう一度実行します。

クイックチェック

v2は17/20を記録し、v1は同じケースで15/20を記録しました。最も安全な結論は何でしょうか?

答えを表示

答え: v2は良さそうだが、20件中2件は小さな差:新しいケースを追加して再実行する

各ケースは5ポイントなので、2件の差はもっと詳しく見るべきシグナルであり、判定ではありません。v2が一度も見たことのない新しいケースが次のチェックです。