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]
Scoreは4つの親(Trace/Observation/Session/DatasetRunItem)に対して source で区別されて付与される。これにより3系統の評価+ユーザーフィードバックを一元表現

Managed Evaluator — UIでLLM-as-a-Judgeを宣言

Managed Evaluator は Langfuse の UI だけで LLM-as-a-Judge(LLMで生成結果を採点するEvaluator)を作れる仕組みです。

  1. Template選択: Helpfulness / Hallucination / Toxicity / Correctness など既成から選ぶ or カスタム作成
  2. Prompt編集: 評価用プロンプトを調整(内部もPrompt Management配下で管理される)
  3. 変数マッピング: Traceのどのフィールド(input / output / retrieved_docs など)をプロンプトに渡すか
  4. モデル設定: GPT-4o / Claude / Gemini などJudge用モデルを選ぶ
  5. 対象選択: "全Trace" / "特定Nameのみ" / "taskタグ付き"などフィルタを設定
  6. 有効化: 対象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 = 正解データの集合、DatasetRun = 1回の実験、DatasetRunItem = 各ゴールド1件に対する実行結果(Trace + Score)。比較はDatasetRun単位で行う

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マイグレーションまで一気に見ます。

理解度チェック

問題 0 / 50%
Q1

Langfuse における Score の source が "ANNOTATION" の場合、想定される主な書き込み元は?

キーボード: 1〜4 で選択、Enter で回答