0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LLMの評価が毎回ブレる問題、ルーブリック方式で解決した話

0
Posted at

TL;DR

  • LLM(Gemini)による評価レポートの点数が毎回ブレて困ってた
  • プロンプトにルーブリック(評価基準表)を組み込んで解決
  • ついでに低スコア時の自動アラート機能も実装
  • 評価の一貫性が劇的に改善した

背景:「なんでこんなに点数がブレるんだ...?」

業務でGemini APIを使って、音声データから評価レポートを自動生成するシステムを運用していました。最初は「LLMすげー!」って感じで喜んでたんですが、しばらく使ってると気になることが...

同じような内容なのに、点数がバラバラ。

  • 月曜日の評価:82点
  • 水曜日の同じような内容:67点
  • 金曜日:74点

「えっ、なにこれ...」ってなりますよね。

当時のプロンプトはこんな感じでした:

prompt = """
あなたはエキスパートです。
以下のテキストを100点満点で評価してください。

【評価観点】
- 良かった点
- 改善点
- その他
"""

これ、今見ると「そりゃブレるわ」って感じなんですが、当時は「LLMが賢いから大丈夫でしょ」って思ってました。甘かった。

何が問題だったのか

根本的な問題は**「評価基準が曖昧すぎる」**こと。

LLMからすると、「良かった点って何?どれくらい良かったら何点?」っていう基準がないわけです。そりゃ毎回違う点数になりますよね。

さらに厄介なのが、低スコアの評価レポートが出ても気づくのが遅れること。通知は全部同じ形式だったので、「あ、レポートできたんだ〜」くらいの感覚で、中身をちゃんと見るのは後回しになりがちでした。

解決策を考える

調べてみると、教育現場では**ルーブリック(評価基準表)**っていう手法があるらしい。

「これだ!」と思って、プロンプトに評価基準を具体的に埋め込むことにしました。

スコア配分を決める

まず、何を評価するのか整理して、点数配分を決めました:

評価項目 配点
ヒアリング力 30点
提案力 25点
共感・寄り添い力 20点
インサイト発見力 15点
専門知識の活用 10点

各項目をさらに細分化して、合計12個のサブ項目に分けました。

5段階評価基準

そして、各項目に5段階の評価基準を設定:

評価 点数 基準
優秀 100% 期待を大きく上回る
良好 80% 期待を上回る
標準 60% 期待通り
要改善 40% やや不足
不足 20% 大きく不足

例えば「ヒアリング力 - 基本情報の聞き取り(10点満点)」なら:

  • 優秀(10点): 留学目的・期間・予算・英語力・希望国を全て確認し、深掘りしている
  • 良好(8点): 上記5項目中4項目を確認
  • 標準(6点): 3項目を確認
  • 要改善(4点): 2項目のみ
  • 不足(2点): 1項目以下

ここまで具体的にすると、LLMも判断しやすくなります。

実装してみた

プロンプトの書き換え

ルーブリックをプロンプトに埋め込みました。結構長くなりますが、仕方ない:

def get_prompt(self, transcription_text):
    return f"""あなたはエキスパートです。
以下の評価基準(ルーブリック)に従って100点満点で評価してください。

## 評価基準(ルーブリック)合計100点

### 1. ヒアリング力(30点)

**1-1. 基本情報の聞き取り(10点)**
- 優秀(10点): 留学目的・期間・予算・英語力・希望国を全て確認し、深掘りしている
- 良好(8点): 上記5項目中4項目を確認している
- 標準(6点): 上記5項目中3項目を確認している
- 要改善(4点): 上記5項目中2項目のみ確認
- 不足(2点): 1項目以下、またはほとんど確認していない

# ... 他の項目も同様に定義 ...

## 出力形式

# 評価レポート

## Overall Score: XX/100

## スコア内訳

| 評価項目 | 得点 | 満点 | 評価 |
|---------|------|------|------|
| ヒアリング力 | XX | 30 | 優秀/良好/標準/要改善/不足 |
|  └ 基本情報の聞き取り | XX | 10 | ... |
# ... 以下同様 ...

音声書き起こしテキスト:
{transcription_text}
"""

プロンプトが長くなってトークン数増えるのが少し気になりましたが、評価の一貫性の方が重要だと判断しました。

スコア抽出機能

レポートテキストから正規表現でスコアを抜き出します。シンプル:

import re

def extract_score(self, report_text):
    match = re.search(r'Overall Score:\s*(\d+)/100', report_text)
    if match:
        score = int(match.group(1))
        return score
    return None

低スコア時の自動アラート

せっかくなので、スコアが75点未満の時は自動でSlackメンションを飛ばすようにしました:

def create_and_save(self, ...):
    # レポート生成
    report_text = self.generate(transcription)

    # スコア抽出
    score = self.extract_score(report_text)

    # 保存
    report_file = self.save(...)

    # スコアに応じて通知を切り替え
    if score is not None and score < 75:
        # 低スコア: メンション付き
        self._send_low_score_notification(...)
    else:
        # 通常通知
        self.send_notification(...)

Slack通知はBlock Kitで見やすく:

blocks = [
    {
        "type": "header",
        "text": {"type": "plain_text", "text": "📊 評価レポート完了"}
    },
    {
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": f"<@{USER_ID}> ⚠️ *スコア: {score}/100(要改善)*\n\n"
                    f"スコアが75点未満のため、ご確認ください。"
        }
    },
    # ... ボタンなど ...
]

ハマったポイント

プロンプトの長さとコスト

ルーブリックを詳細に書くとプロンプトが長くなります。最初は「トークン数増えてコストやばいのでは」と心配しましたが、計算してみたら大したことなかったです。

むしろ、評価のブレで何度もやり直す方がコスト高かったので、結果的にはコスト削減になりました。

5段階評価の点数配分

最初は「優秀(10点), 良好(7点), 標準(5点)...」みたいに不均等な配分にしてたんですが、LLMが混乱するみたいで、素直に「100%, 80%, 60%, 40%, 20%」の等間隔にしたら安定しました。

テスト運用の重要性

いきなり本番運用せず、「【テスト】」って明記したメッセージで様子見したのは正解でした。最初の1週間くらいで何度か調整が必要だったので。

結果とわかったこと

評価の一貫性が劇的に改善

同じような内容の評価が、以前は±15点くらいブレてたのが、±3点くらいに収まるようになりました。これはデカい。

詳細スコアが超便利

12項目の詳細スコアが出るので、「どこを改善すればいいか」が一目瞭然になりました。

例えば:

  • ヒアリング力:28/30(優秀)
  • 提案力:15/25(標準)← ここ弱い
  • 共感・寄り添い力:18/20(良好)

みたいな感じで、改善ポイントが明確になります。

低スコアへの対応が早くなった

メンション付きアラートのおかげで、問題のあるケースにすぐ気づけるようになりました。以前は「あれ、この前のレポート、実は点数低かったんだ...」って後から気づくパターンが多かったので。

意外な副次効果

ルーブリックを作る過程で、「そもそも何を評価したいのか」を整理できたのが良かったです。チーム内で「評価基準ってこれで合ってる?」って議論するきっかけにもなりました。

これから試したいこと

閾値の動的調整

今は75点未満でアラートですが、環境変数で調整できるようにしてあります:

ALERT_THRESHOLD = int(os.environ.get('SCORE_ALERT_THRESHOLD', '75'))

運用しながら、最適な閾値を探っていく予定。

評価基準の定期的な見直し

ビジネス要件が変われば評価基準も変わるはず。四半期に1回くらい見直す仕組みを作りたいですね。

まとめ

LLMの評価のブレ、「まあLLMだし仕方ないか」って諦めてたんですが、ルーブリックを入れるだけでかなり改善しました。

ポイントは:

  • 評価基準を具体的に(数値で)示すこと
  • 5段階評価は等間隔がおすすめ
  • スコアを抽出して活用すると色々できる

LLMを業務で使う時、「賢いから適当に指示しても大丈夫でしょ」って思いがちですが、人間と同じで「明確な基準」があった方が良い仕事してくれるんだなと実感しました。

同じような課題で困ってる人の参考になれば幸いです。

参考

技術スタック

  • Python 3.14
  • Google Cloud Run
  • Vertex AI (Gemini 2.5 Pro)
  • Slack API
  • Google Drive API / Firestore
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?