LLMアプリの評価は"3系統"ある
Promptを作って動かしたら、次はどれだけ良いかを測る段階です。 LLMアプリの評価は単純なテスト1種類では済まず、以下の3系統を組み合わせます。
| 系統 | 目的 | Langfuseでの表現 | 代表的なsource |
|---|---|---|---|
| オンライン評価 | 本番Traceに自動で品質スコアを付与 | Managed / Custom Evaluator がTraceを見てScoreを書く | EVAL |
| 人間評価 (Annotation) | 専門家やラベラーが目視で採点 | UIのAnnotation Queue から付けるScore | ANNOTATION |
| オフライン評価 (Experiment) | ゴールドセット(Dataset)に対する回帰テスト | DatasetRun + Experiment Runner | EVAL / API |
| ユーザーフィードバック | エンドユーザーの👍👎 | フロントから直接Score APIを叩く | API |
Scoreの再確認(第4章のおさらい)
Scoreは name / value / dataType (NUMERIC / CATEGORICAL / BOOLEAN) / source を持つ評価レコードで、
Trace / Observation / Session / DatasetRunItem のどれにも付与可能です。
「なぜ4箇所にも付けられるようにしたか」の理由は、上の3系統の評価が異なる粒度で働くからです。
graph TB USER[ユーザーフィードバック] -->|source: API| SCORE_T[Score on Trace<br/>name: user_rating] EVAL[Managed Evaluator] -->|source: EVAL| SCORE_O[Score on Observation<br/>name: hallucination] HUMAN[人間ラベラー] -->|source: ANNOTATION| SCORE_S[Score on Session<br/>name: csat] EXP[Experiment] -->|source: EVAL| SCORE_D[Score on DatasetRunItem<br/>name: exact_match]
Managed Evaluator — UIでLLM-as-a-Judgeを宣言
Managed Evaluator は Langfuse の UI だけで LLM-as-a-Judge(LLMで生成結果を採点するEvaluator)を作れる仕組みです。
- Template選択: Helpfulness / Hallucination / Toxicity / Correctness など既成から選ぶ or カスタム作成
- Prompt編集: 評価用プロンプトを調整(内部もPrompt Management配下で管理される)
- 変数マッピング: Traceのどのフィールド(input / output / retrieved_docs など)をプロンプトに渡すか
- モデル設定: GPT-4o / Claude / Gemini などJudge用モデルを選ぶ
- 対象選択: "全Trace" / "特定Nameのみ" / "taskタグ付き"などフィルタを設定
- 有効化: 対象Traceが作成されるたびにWorkerが自動でJudgeを走らせ、Score(source=EVAL)を書く
| Managed Evaluatorテンプレート | 測定内容 | 出力型 |
|---|---|---|
| Helpfulness | 回答がどれだけ有益か(5段階) | NUMERIC |
| Hallucination | コンテキストに基づくか幻覚か | BOOLEAN / NUMERIC |
| Toxicity | 不適切な内容を含むか | BOOLEAN |
| Correctness | 正解と照合した正しさ(参照ありの場合) | NUMERIC |
| Conciseness | 冗長性 | NUMERIC |
| Relevance | 質問に対する関連性 | NUMERIC |
| Language | 指定言語で答えているか | CATEGORICAL |
Custom Evaluator とOSSライブラリ連携
組織固有の採点軸(例: 社内ポリシー遵守、商品リファレンスの正確さ)は Custom Evaluator で実装します。 Python/TSで関数を書き、Ingestion Pipeline後にScore APIを叩く形です。
from langfuse import Langfuse
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy
lf = Langfuse()
def ragas_evaluator(trace):
# Trace から input/output/context を抽出してRagas dataset形式に
ds = {
"question": [trace.input],
"answer": [trace.output],
"contexts": [trace.metadata["retrieved_docs"]],
}
result = evaluate(ds, metrics=[faithfulness, answer_relevancy])
lf.score(
trace_id=trace.id,
name="faithfulness",
value=float(result["faithfulness"][0]),
data_type="NUMERIC",
source="EVAL",
)
lf.score(
trace_id=trace.id,
name="answer_relevancy",
value=float(result["answer_relevancy"][0]),
data_type="NUMERIC",
source="EVAL",
) | OSSライブラリ | 主な評価指標 | Langfuse連携 |
|---|---|---|
| Ragas | Faithfulness, Answer Relevancy, Context Precision/Recall | Trace結果をRagasに渡し、Score APIで書き戻す |
| UpTrain | Hallucination, Response Conciseness, Factual Accuracy | 同上 |
| DeepEval | G-Eval, Bias, Toxicity, Summarization | pytest互換。DatasetRunItemにScoreを書き戻す |
| promptfoo | 正規表現/JSON比較/LLM Judge/Toxicity検出 | CIで動かし結果をLangfuse DatasetRunと紐付ける |
Dataset と DatasetRun — ゴールドセットでの再現評価
Datasetは「期待する入力と出力のペア(正解)」のコレクションです。 ここに対するDatasetRunを回すことで、Promptやモデルを変えた際の性能差を定量評価できます。
graph TB DS[Dataset 'rag-golden-50'] DI1[DatasetItem #1<br/>input: Q1, expected: A1] DI2[DatasetItem #2<br/>input: Q2, expected: A2] DI3[DatasetItem ...] DS --> DI1 DS --> DI2 DS --> DI3 RUN[DatasetRun 'run-v5-gpt4o'] DS --> RUN DRI1[DatasetRunItem #1] DRI2[DatasetRunItem #2] RUN --> DRI1 RUN --> DRI2 DRI1 --> TR1[Trace] DRI1 --> SC1[Score<br/>exact_match=1] DRI2 --> TR2[Trace] DRI2 --> SC2[Score<br/>exact_match=0]
Datasetの作り方
- UIから手作り: 本番Traceの中から "これは模範回答" と判断したものをDatasetに追加
- Annotation Queueから昇格: 人間がレビューしたTraceをそのままDatasetItem化
- CSV/JSON import: 既存のQA集をインポート
- API経由: テストケース生成スクリプトから投入
Experiment Runner SDK と pytest 統合
Experiment Runnerは「Datasetを舐めて各itemに対してアプリを実行し、Scoreを集計する」SDK APIです。 pytestと統合することで、PRごとに回帰テストが回ります。
import pytest
from langfuse import Langfuse
from my_app import rag_query
lf = Langfuse()
@pytest.mark.parametrize("item", lf.get_dataset("rag-golden-50").items)
def test_rag_golden(item):
run = lf.start_dataset_run_item(
dataset_name="rag-golden-50",
run_name="run-v5-gpt4o",
item_id=item.id,
)
output = rag_query(item.input) # @observeでtrace自動生成
run.link(trace_id=lf.get_current_trace_id())
run.score(name="exact_match", value=1.0 if output == item.expected_output else 0.0)
assert output == item.expected_output Promptfoo + GitHub Actions
Promptfoo はYAMLでテスト定義を書ける評価フレームワークで、Langfuseと双方向連携します。 GitHub Actionsで「PR時に全プロンプトを評価 → 結果をLangfuse DatasetRunとして登録 → 回帰があれば落ちる」フローを組めます。
# promptfooconfig.yaml
prompts:
- "{{prompt.content}}" # Langfuseから取得したPrompt
providers:
- openai:gpt-4o-mini
tests:
- vars: { question: "日本の首都は?" }
assert:
- type: contains
value: 東京
- type: llm-rubric
value: "回答は100文字以内で簡潔"
outputPath: ./results.json
extensions:
- langfuse:
projectId: "${LANGFUSE_PROJECT_ID}"
datasetName: "rag-golden-50"
runName: "ci-${GITHUB_SHA}" # .github/workflows/prompt-eval.yml
name: Prompt Evaluation
on: pull_request
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install -g promptfoo
- run: promptfoo eval -c promptfooconfig.yaml
env:
LANGFUSE_PUBLIC_KEY: ${{ secrets.LANGFUSE_PUBLIC_KEY }}
LANGFUSE_SECRET_KEY: ${{ secrets.LANGFUSE_SECRET_KEY }}
- run: promptfoo assert 結果の見方 — UIで何を見るか
| ビュー | 用途 |
|---|---|
| DatasetRun一覧 | 実験ごとに集計Scoreを並べて比較。モデル/Promptバージョン/Paramの違いが可視化 |
| DatasetRunItem詳細 | 個別ケースの入出力・Score・紐づくTraceに1クリックで飛べる |
| Score分布 | スコアのヒストグラム。平均だけでなく尾部(低スコア)を見るのが実務のコツ |
| Diff比較 | 2つのDatasetRun間で回答の差分を表示。regressionしたケースを即座に特定 |
まとめ
次章はいよいよ本番運用 — Self-hostedでDocker Compose / Helm / Terraform から始めて、UTCの罠、ClickHouseバージョン、HTTPS、バックアップ、v2→v3マイグレーションまで一気に見ます。
理解度チェック
Langfuse における Score の source が "ANNOTATION" の場合、想定される主な書き込み元は?
キーボード: 1〜4 で選択、Enter で回答