レッスン 1 / 7
LLMアプリの評価と運用とは?
学ぶコト
- ふつうのプログラムのテストだけでは足りない理由(答えが変わる・正解が1つではない・何もしなくても変わる)
- 確かめる3つのタイミング(出す前に基準を決める・変えるたびに確かめる・出したあと見守る)
- 見つける → 評価セットに足す → 直す → 確かめて出す → 見守る のサイクル
ことり雑貨店のボットは、公開したあとも、指示の手直し・モデルの更新・新しいサービスで答えが変わっていきます。作って終わりにせず、良い状態を保ち続けるしくみを考えます。
動かして確かめる
動画と同じ30日間(作り話)で、3つの変化が起きます。守りの組み合わせを変えて、誰がいつ気づき、何人のお客様が困るかを見てみましょう。
ボットの指示も設定も変えていないのに、お店が新しく「名入れ」のサービスを始めてから、名入れの質問にうまく答えられなくなりました。いちばん早く気づける方法はどれでしょうか?
評価セットは、自分が何かを変えたときに流すものなので、ボットを変えていないこの変化では流れません。そもそも評価セットに、名入れの質問が入っていません。版を固定しても、お店の商品やお客様の質問は変わります。毎日の数字を見ていれば、苦情より早く気づけます。気づいたら、名入れの質問を評価セットに足してから直します。
図で ① 評価セットを「する」にしても、B(モデルが新しい版になる)は、お客様の苦情まで分かりませんでした。B を出す前に止めるには、どうすればよいでしょうか?
モデルの版が自動で変わると、自分は何も変えていないので、評価セットを流す機会がありません。基準や問いを増やしても、流さなければ気づけません。版を固定しておけば、切りかえる日を自分で決められ、その前に評価セットを流せます(図の ③)。
「送料はいくらですか?」への答えを確かめるテストとして、いちばんよいものはどれでしょうか?
LLMの答えは、同じ質問でも言い方が変わります。「送料は550円です」も「全国一律で550円かかります」も正しいので、正解の文と同じかで比べると、正しい答えまで不合格になります。入っていてほしいことと、入っていてはいけないことを決めて確かめます。
コードで確かめる
評価セットのいちばん小さな形を、Python で動かしてみます。コードの右上の「▶ 実行」を押して、上から順に実行してください。
問いごとに、答えに入っていてほしい言葉(must)を書きます。答えは、指示を1行直す前と後のもの(説明のために作った例)です。
import pandas as pd
# 評価セット(ことり雑貨店のボット)。問いごとに、答えに入っていてほしい言葉を書く
eval_set = [
{"id": "ship-1", "question": "送料はいくらですか?", "must": ["550円", "5,000円"]},
{"id": "ret-1", "question": "届いたマグカップを返品できますか?", "must": ["8日後", "未使用"]},
{"id": "pay-1", "question": "代金引換は使えますか?", "must": ["330円"]},
{"id": "ord-1", "question": "KT-10231 はいつ届きますか?下4桁は1234です", "must": ["発送済み"]},
{"id": "out-1", "question": "おすすめの映画を教えて", "must": ["お買い物"]},
]
# 指示を1行直す前と後の答え(説明のために作った例)
answers_before = {
"ship-1": "送料は全国一律550円です。お買い上げ金額が5,000円以上なら無料になります。",
"ret-1": "商品が届いた日の8日後まで、未使用の商品に限り返品できます。マイページの「注文履歴」からお手続きください。",
"pay-1": "代金引換をご利用いただけます。手数料は330円です。",
"ord-1": "ご注文 KT-10231 は、10月30日に発送済みです。",
"out-1": "申し訳ありませんが、ここでは、ことり雑貨店でのお買い物についてのご質問にお答えしています。",
}
answers_after = {
"ship-1": "全国一律で550円です。5,000円以上のお買い上げで無料です。",
"ret-1": "届いた日の8日後まで返品できます。注文履歴からどうぞ。",
"pay-1": "使えます。手数料は330円です。",
"ord-1": "KT-10231 は10月30日に発送済みです。",
"out-1": "ここでは、ことり雑貨店でのお買い物についてのご質問だけにお答えしています。",
}
前の正解の文とそっくり同じかで比べると、中身は正しいのに False になります。入っていてほしい言葉で比べると、合格です。
print(answers_after["ship-1"] == answers_before["ship-1"])
def check(answer, item):
"""入っていてほしい言葉が、全部入っているか"""
missing = [w for w in item["must"] if w not in answer]
return {"id": item["id"], "ok": not missing, "missing": "・".join(missing)}
check(answers_after["ship-1"], eval_set[0])
評価セットを全部流して、直す前と後を並べます。直したあとは ret-1 だけが不合格で、「未使用」が抜けています。出す前に流せば、ここで止められます。
def run_eval(answers):
return pd.DataFrame([check(answers[item["id"]], item) for item in eval_set])
result = run_eval(answers_before).merge(run_eval(answers_after), on="id", suffixes=("_before", "_after"))
regressed = result.loc[result["ok_before"] & ~result["ok_after"], "id"].tolist()
print("合格:", int(result["ok_after"].sum()), "/", len(result), " 悪くなった問い:", regressed)
result
図と同じ3つの変化で、困ったお客様の数を計算します。「気づいた日 − 起きた日 + 1」が、間違った答えを返した日数です。出す前に止めた A は、0人です。
changes = pd.DataFrame({
"change": ["A 指示を1行直す", "B モデルが新しい版になる", "C 名入れを始める"],
"start": [3, 10, 18],
"per_day": [20, 12, 8],
"found_none": [12, 15, 27], # 守りなし:お客様の苦情で気づいた日
"found_both": [None, 11, 20], # 評価セット+見守り(A は出す前に止めたので None)
})
for col in ["found_none", "found_both"]:
days = (changes[col] - changes["start"] + 1).fillna(0)
changes["people_" + col.removeprefix("found_")] = (days * changes["per_day"]).astype(int)
print(changes[["people_none", "people_both"]].sum())
changes
名入れを始めたら、見守りの数字で問題に気づきました。名入れの問いを評価セットに足して、直す前の答え(answers_old)では不合格、店の決まりに名入れを書き足した版(answers_fixed)では合格になることを確かめます。
eval_setに、名入れの問いを2つ以上足してください。idはname-1・name-2・name-3から選び、mustには、正しい答えに入っていてほしい言葉を書きますrun_evalで直す前と直した版を採点し、result_old・result_fixedに入れてください
answers_old = {
**answers_before,
"name-1": "未使用の商品なら、届いた日の8日後まで返品できます。",
"name-2": "名入れについては、担当者に確認します。",
"name-3": "マグカップのほかに、お皿やトートバッグにも名入れできます。",
}
answers_fixed = {
**answers_before,
"name-1": "名入れした商品は、返品できません。ご注文の前に、お名前のつづりをお確かめください。",
"name-2": "名入れは1点660円です。ご注文の画面でお申し込みください。",
"name-3": "名入れは、マグカップだけで承っています。",
}
# name-1:名入れしたマグカップは返品できますか?
# name-2:名入れの料金はいくらですか?
# name-3:マグカップ以外にも名入れできますか?
# ここに書く(例:eval_set.append({"id": "name-1", "question": "…", "must": ["…"]}))
result_old = None
result_fixed = None
print(result_old)
print(result_fixed)解答を見る
answers_old = {
**answers_before,
"name-1": "未使用の商品なら、届いた日の8日後まで返品できます。",
"name-2": "名入れについては、担当者に確認します。",
"name-3": "マグカップのほかに、お皿やトートバッグにも名入れできます。",
}
answers_fixed = {
**answers_before,
"name-1": "名入れした商品は、返品できません。ご注文の前に、お名前のつづりをお確かめください。",
"name-2": "名入れは1点660円です。ご注文の画面でお申し込みください。",
"name-3": "名入れは、マグカップだけで承っています。",
}
eval_set += [
{"id": "name-1", "question": "名入れしたマグカップは返品できますか?", "must": ["返品できません"]},
{"id": "name-2", "question": "名入れの料金はいくらですか?", "must": ["660円"]},
{"id": "name-3", "question": "マグカップ以外にも名入れできますか?", "must": ["マグカップだけ"]},
]
result_old = run_eval(answers_old)
result_fixed = run_eval(answers_fixed)
print(result_old)
print(result_fixed)直す前の答えは、名入れの3問がすべて不合格で、直した版はもとの5問も含めて全部合格です。サイクルの「評価セットに足す → 直す → 確かめて出す」を、1周したことになります。
足した問いは、このあとも消しません。次に指示やモデルを変えたとき、名入れの答えが崩れていないかも毎回確かめられます。「返品できます」のような、入っていてはいけない言葉を書く欄を足すと、もっと確かになります(このあとのレッスンで扱います)。
動画の内容を文字で読む
はじめに ネットのお店のチャットボットが、毎日お客様の質問に答えています。ある日、店長が、答えをもっと短くしたくて、ボットへの指示を1行だけ直しました。しばらくして、お客様から「返品できると言われたのに、断られた」と苦情が届きます。短くなった答えから、「未使用の商品に限る」という条件が抜けていたのです。直した日から苦情が届いた日までの10日間、返品の質問は1日に20件ほどありました。20件かける10日で、200人のお客様に、条件の抜けた答えを返していたことになります。
① ふつうのテストだけでは足りない理由 ふつうのプログラムは、同じ入力なら、いつも同じ答えを返します。だからテストでは、「この入力なら、この答え」と、1つの正解と見比べれば済みます。ところが、LLMを使ったアプリは、同じ質問でも、答えの言い方が毎回少し変わります。しかも、正しい答えが1つに決まりません。「送料は550円です」も「全国一律で550円かかります」も、どちらも正しい答えです。そこで、正解の文とそっくり同じかどうかは見ません。答えに入っていてほしいことと、入っていてはいけないことを決めて、確かめます。返品の答えなら、「8日後まで」と「未使用」が入っているか、のようにです。
② 何もしなくても変わるもの LLMアプリは、自分が何も変えていなくても、答えが変わることがあります。1つ目は、モデルの版の更新です。使っているモデルが新しい版になると、同じ指示でも、答え方のくせが変わることがあります。たとえば、注文を調べる道具を呼ばずに、「通常1〜3日でお届けします」と答えるようになるかもしれません。2つ目は、お店の新しい商品やサービスです。マグカップに名前を入れる「名入れ」を始めても、ボットはその決まりを知りません。3つ目は、新しい種類の質問です。お客様は、作った人が思いつかなかった聞き方を、次々にしてきます。出したときに良かったボットも、そのままでは、だんだん現実とずれていきます。
③ 確かめる3つのタイミング 1つ目は、出す前に、良い答えの基準を決めることです。質問と、その答えに入っていてほしいことを並べたものを、「評価セット」と呼びます。評価セットと、何問できたら合格かを、出す前に決めておきます。2つ目は、変えるたびに確かめることです。指示を1行直しただけでも、出す前に、評価セットを全部流します。はじめの例でも、評価セットに返品の質問が入っていれば、指示を直したその日のうちに、条件の抜けに気づけました。3つ目は、出したあとも見守ることです。評価セットは、自分が何かを変えたときにしか流さないので、何もしなくても起きる変化には気づけません。そこで、「よくない」の評価の数や、道具を呼んだ割合のような数字を、毎日見ます。モデルの版が変わった日も、数字を見ていれば、次の日には気づけます。出す前と、変えるたびと、出したあとの3つで、守りを重ねます。
④ 良くし続けるサイクル 名入れの例で1周してみます。まずは、見つけます。名入れを始めて3日目に、「よくない」の評価が増えたことに、数字で気づきました。次に、評価セットに足します。「名入れした商品は返品できる?」のような質問を、4つ足しました。そして、直します。ボットに渡している店の決まりに、名入れの料金と、名入れした商品は返品できないことを書き足します。直したら、確かめて出します。足した4つだけでなく、評価セットを全部流して、ほかの答えが悪くなっていないかも確かめます。出したあとは、また数字で見守ります。1周するたびに評価セットが育つので、同じ間違いが戻ってきても、出す前に気づけるようになります。
まとめ LLMアプリは、答えが毎回変わり、正解も1つではないので、決めた基準で確かめます。自分が変えなくても、モデルの版や、お店の商品や、お客様の質問は変わっていきます。出す前に基準を決め、変えるたびに評価セットで確かめ、出したあとは数字で見守ります。見つけた問題は、評価セットに足して、直して、確かめてから出します。要は、LLMアプリは、出してからも、見つける・足す・直す・確かめるを回し続けるものなのです。
もっと知る:このコースで作るものと、モデルの版を固定する(2026年11月時点)
このコースの流れ このコースは、上の3つのタイミングとサイクルを、1つずつ形にしていきます。
| レッスン | 中身 | 動かす場所 |
|---|---|---|
| 1. LLMアプリの評価と運用とは?(このページ) | 3つのタイミングとサイクル | ブラウザ |
| 2. 評価セットを作る・育てる | 記録から問いを集める・偏りを見る・何問あれば差を信じてよいか | ブラウザ |
| 3. ルールで自動採点する | 形・注文番号・個人の情報・約束・長さ・出典・引き継ぎをルールで確かめる | ブラウザ |
| 4. LLMに採点させる(LLM-as-a-Judge) | 採点の基準・くせ・人の判定と比べる | ブラウザ |
| 5. 変えるたびに確かめる(回帰テスト) | 変えるたびに評価セットを流し、前の版と比べて出す前の関門で判定する | 自分のパソコン |
| 6. 出したあとを見守る(ログとモニタリング) | 記録と毎日の数字で見守り、見つけた問題を評価セットに足す | ブラウザ |
| 7. 費用と速さを下げる | 品質を保ったまま、費用と待ち時間を減らす | ブラウザ |
ふつうのプログラムのテストとの違い
| ふつうのプログラム | LLMアプリ | |
|---|---|---|
| 同じ入力の答え | いつも同じ | 言い方が毎回変わることがある |
| 正しい答え | 1つに決まることが多い | いくつもある |
| 確かめ方 | 正解と同じか | 決めた基準に合うか(ルール・LLMの採点・人) |
| 何もしなくても変わるか | 変わらない | モデルの版・資料・お客様の質問で変わる |
モデルの版を固定する OpenAI の API の説明(Backwards compatibility)には、モデルの版(スナップショット)が変わると答え方も変わるので、版を固定し、自分のアプリの評価(evals)を流すのが、答えをそろえるいちばんの方法だと書かれています。モデルによっては、日付の付いた名前を指定すると版を固定できます。どのモデルに日付の付いた版があるか・古い版がいつまで使えるかは、モデルごとのページで確かめてください(モデルの一覧)。モデルの選び方そのものは、「モデルの選び方」で扱っています。ほかの会社の API を使うときも、版を固定できるかと、古い版がいつまで使えるかを、その会社の説明で確かめておきます。
次のレッスン「評価セットを作る・育てる」では、評価セットの問いを本番の記録から集め、偏りと件数を確かめます。
レッスンの完了の記録は、会員の機能です
会員になると、終えたレッスンがマイページに記録され、コースを全部終えると名前入りの修了証を受け取れます。どのプランでも、全コースのレッスンが見られます。半年プランなら月あたり1,280円です。
分からないところがあればこのレッスンについて質問するこのコースの Q&A を見る
