本文へ移動
メニュー

学ぶ

コースロードマップコンペお知らせ

スタアカについて

プラン受講者の声法人の方

会員の方

ログインマイページQ&A(質問と回答)ライトプランの解約クレジットカードの変更

サポート

お問い合わせ

ブラックフライデーセール全プラン30%引き(12月1日(火)まで)プランを見る

レッスン 1 / 7

LLMアプリの評価と運用とは?

所要時間 30分無料で見られます

学ぶコト

  • ふつうのプログラムのテストだけでは足りない理由(答えが変わる・正解が1つではない・何もしなくても変わる)
  • 確かめる3つのタイミング(出す前に基準を決める・変えるたびに確かめる・出したあと見守る)
  • 見つける → 評価セットに足す → 直す → 確かめて出す → 見守る のサイクル

ことり雑貨店のボットは、公開したあとも、指示の手直し・モデルの更新・新しいサービスで答えが変わっていきます。作って終わりにせず、良い状態を保ち続けるしくみを考えます。

動かして確かめる

動画と同じ30日間(作り話)で、3つの変化が起きます。守りの組み合わせを変えて、誰がいつ気づき、何人のお客様が困るかを見てみましょう。

図を読み込んでいます…

確認1問クイズ

ボットの指示も設定も変えていないのに、お店が新しく「名入れ」のサービスを始めてから、名入れの質問にうまく答えられなくなりました。いちばん早く気づける方法はどれでしょうか?

確認1問クイズ

図で ① 評価セットを「する」にしても、B(モデルが新しい版になる)は、お客様の苦情まで分かりませんでした。B を出す前に止めるには、どうすればよいでしょうか?

確認1問クイズ

「送料はいくらですか?」への答えを確かめるテストとして、いちばんよいものはどれでしょうか?

コードで確かめる

評価セットのいちばん小さな形を、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)では合格になることを確かめます。

  1. eval_set に、名入れの問いを2つ以上足してください。id は name-1・name-2・name-3 から選び、must には、正しい答えに入っていてほしい言葉を書きます
  2. 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 を使うときも、版を固定できるかと、古い版がいつまで使えるかを、その会社の説明で確かめておきます。

次のレッスン「評価セットを作る・育てる」では、評価セットの問いを本番の記録から集め、偏りと件数を確かめます。