📻

Ragas の custom metrics を使ってみる

に公開

こんにちは。Ubie でソフトウェアエンジニアをしております、爲岡 (@zettaittenani) と申します。
Ubie には 2025 年の 2 月に入社し、1 年弱働いています。主に、医学ドメインに関するデータにまつわるエンジニアリングを担当しており、現在は LLM に関連する機能開発を行っています。

概要

Ubie では、信頼性のある医学情報や医学関連記事を取得・利用することで、 LLM の情報を増強する RAG システムを運用しています。
開発にあたっては様々な課題に直面していますが、現在は特に、RAG パイプラインにおける品質劣化時の原因特定が困難であること が、開発のイテレーションを回す際の大きな課題です。
継続的な改善を行うためには、自動化された客観的評価が不可欠であると感じています。

このような課題に対処するための手段として、LLM as a Judge の考えに基づいた RAG の精度評価のフレームワークを利用して、品質劣化を特定し、評価を行うという方法があります。
多くの RAG 評価フレームワークでは、 RAG triad にて指定されている一般的な RAG 評価指標をデフォルトでサポートしていますが、これら以外にも、独自の評価指標を用意したいシーンが想定されます。たとえば、質問に対する回答が丁寧な日本語になっているか、など。

現在 Ubie で導入を検討している Ragas (Retrieval augmented generation assessment) では、 custom metrics という機能を利用して独自の評価指標が定義できるようなので、そのハンズオンを行ってみました。
※ 繰り返しになりますがあくまでトライアルであり、実際の導入に関しては検討中になります。
https://docs.ragas.io/en/stable/howtos/customizations/metrics/_write_your_own_metric/

環境構築

まずは評価を実行する環境を Docker で構築します。
Python 環境と、LLM を利用するためのライブラリをセットアップします。
今回は LLM as a Judge のモデルとしては Gemini (gemini-2.5-flash) を利用しました。

ディレクトリ構成

.
├── docker-compose.yml
├── Dockerfile
└── eval.py

Dockerfile

Ragas と Gemini を LangChain 経由で呼ぶためのライブラリをインストールします。
各ライブラリのバージョンに関しては、動作を保証するため、動作確認ができたもので固定しています。

Dockerfile
FROM python:3.11-slim

WORKDIR /app

RUN pip install --no-cache-dir \
    ragas==0.4.1 \
    langchain-google-genai==4.1.1 \
    pandas==2.3.3 \
    datasets==4.4.1

CMD ["python", "eval.py"]

docker-compose.yml

Google の API key を環境変数として渡します。
Google AI Studio にて API key が発行できます (無料枠あり)。
https://aistudio.google.com/

docker-compose.yml
version: '3'
services:
  ragas-eval:
    build: .
    volumes:
      - .:/app
    environment:
      - GOOGLE_API_KEY={your_google_api_key}  # ここに API キーを設定

custom metrics の実装

試しに 「日本語の丁寧さ (Politeness)」 を評価する指標を作ってみます。
デフォルトの Ragas 指標は英語プロンプトがベースなので、日本語のニュアンス (タメ口になっていないか、乱暴ではないか) を正しく測るために、Gemini に日本語で judge してもらいます。
今回は retrieval はフォーカス対象ではないので dummy で埋めています。

eval.py
import asyncio
import os

from ragas.metrics.base import SingleTurnMetric
from ragas.dataset_schema import SingleTurnSample
from ragas import RunConfig
from datasets import Dataset
from ragas import evaluate
from langchain_google_genai import ChatGoogleGenerativeAI

# 1. LLM (Gemini) の設定
judge_llm = ChatGoogleGenerativeAI(
    model="gemini-2.5-flash",
    temperature=0
)

# 2. custom metrics の定義
class JapanesePolitenessMetric(SingleTurnMetric):
    name: str = "japanese_politeness"
    _llm: ChatGoogleGenerativeAI

    def __init__(self, llm: ChatGoogleGenerativeAI):
        super().__init__()
        self._llm = llm

    def init(self, run_config: RunConfig):
        pass

    async def _single_turn_ascore(self, sample: SingleTurnSample, callbacks: any) -> float:
        response_text = sample.response

        prompt = f"""
        あなたは日本語の流暢さと礼儀正しさを評価する審査員です。
        以下のAIの回答が、ユーザーに対して礼儀正しい「です・ます調(敬語)」で書かれているか評価してください。

        採点基準:
        - 1.0: 非常に丁寧で適切な敬語が使われている。
        - 0.5: 敬語とタメ口が混ざっている、または少し不自然。
        - 0.0: タメ口である、または攻撃的・失礼な表現が含まれる。

        評価対象の回答:
        "{response_text}"

        出力は数値(0.0, 0.5, 1.0)のみを返してください。それ以外の文字は不要です。
        """

        result = await self._llm.ainvoke(prompt)

        try:
            score = float(result.content.strip())
            return score
        except ValueError:
            return 0.0

# 3. テストデータの準備
data = {
    "user_input": [
        "RAGとは何ですか?",
        "RAGとは何ですか?",
        "RAGとは何ですか?"
    ],
    "response": [
        "RAGは検索拡張生成のことです。外部データを参照して回答を生成する技術ですよ。", # 1. 良い例
        "RAGは検索拡張生成のことだ。外部データを使う。", # 2. タメ口
        "知らん。" # 3. 不適切
    ],
    "retrieved_contexts": [["dummy"], ["dummy"], ["dummy"]]
}

dataset = Dataset.from_dict(data)

# 4. 評価の実行
async def main():
    print("評価を開始します...")

    politeness_metric = JapanesePolitenessMetric(llm=judge_llm)

    results = evaluate(
        dataset=dataset,
        metrics=[politeness_metric],
        llm=judge_llm
    )

    print("\n=== 評価結果 ===")
    df = results.to_pandas()
    print(df)

if __name__ == "__main__":
    asyncio.run(main())

実装のポイント

  1. SingleTurnMetric の継承:
    Ragas v0.2 系以降では、複数回の会話ではない、シンプルな single turn の会話の評価においては、 SingleTurnMetric を継承することが推奨されています。
    _single_turn_ascore メソッドを override して、独自の採点ロジックを記述しています。

  2. Gemini に渡すプロンプト:
    prompt 変数の中で、採点基準を明確に言語化しています。指定の数値のみ返すよう指示することで parse しやすくしています。

実行結果

Docker コンテナを立ち上げて実行します。

$ docker compose run ragas-eval

出力

=== 評価結果 ===
                                            response  japanese_politeness
0  RAGは検索拡張生成のことです。外部データを参照して回答を生成する技術ですよ。                  1.0
1                RAGは検索拡張生成のことだ。外部データを使う。                  0.5
2                                           知らん。                  0.0

意図どおりの結果が出力されました!

まとめ

Ragas の custom metrics 機能を使うことで、デフォルトの指標では測れない独自の評価指標を自動テストに組み込むことができます。
このような指標を定義し、それを改善するための開発サイクルを回すことで、目的に合った RAG システムが構築できるとよいですね。

Ubie テックブログ

Discussion