一つの judge では足りない

LLM の出力を評価するのに別の LLM を使う「LLM-as-a-Judge」は広く使われています。人手評価と高い相関が出ることが多く、スケールもします。

ただし一種類の judge に頼ると、観測できない側面が出てきます。意味的な正確さ(書いてある内容が事実と合っているか)は LLM judge が得意でも、「文章が十分に具体的か」という定量的な判断はぶれやすい。さらに「特定ドメインで許されない表現か」のような専門的な分類は、汎用 LLM より小さな専用分類器の方が安定することすらあります。

評価したい観点は本来複数あります。それを一つの judge に押し込めず、観点ごとに適した judge を割り当て、束ねるのが現実的な設計です。

共通インターフェースで束ねる

まず器を決めます。すべての judge が同じ形の入力を受け、同じ形の結果を返すようにします。

@dataclass
class JudgeResult:
    label: Literal["OK", "NG", "UNCERTAIN"]
    confidence: float          # 0.0〜1.0
    reason: str

class BaseJudge(ABC):
    @abstractmethod
    async def evaluate(self, ctx: JudgeContext) -> JudgeResult: ...

この器さえ揃えれば、呼び出し側は judge の中身を意識せずに動かせます。「このケースには LLM judge と分類器の両方を走らせ、どちらかが NG なら失敗」という組み合わせも、ループで回すだけになります。以下、三種類を具体的に見ます。

① LLM judge — 意味的な正確さ

LLM に「この出力は入力と矛盾していないか」を判定させます。設計上の勘所が二つあります。

本番のチェックロジックを再利用する。 評価専用のプロンプトを別に書くと、本番でユーザーに見せる品質と、テストで測っている品質がずれていきます。本番の正確性チェック処理をそのまま judge として呼べば、本番とテストが同じ物差しになります。

生成と評価で別モデルを使う。 同じモデルに生成も採点もさせると、モデルが自分の出力に甘い点を付ける preference leakage が起きてスコアが水増しされます。生成と評価のモデルは必ず分けます。

② メトリック judge — 定量的な観点

LLM に判断させるまでもなく、関数で数値化できる観点はメトリック judge に任せます。

class MetricJudge(BaseJudge):
    def __init__(self, metric_fn: Callable[[str], float]):
        self.metric_fn = metric_fn

    async def evaluate(self, ctx: JudgeContext) -> JudgeResult:
        score = self.metric_fn(ctx.text)
        label = "OK" if score >= 0.5 else "NG"
        return JudgeResult(label, confidence=score, reason=f"score={score:.3f}")

「具体性スコア」「文字数の妥当性」「特定キーワードの含有」など、ルールベースで測れるものが対象です。LLM 呼び出しがないので計算コストはほぼゼロ。大量のケースを一次スクリーニングするのに向きます。

③ ドメイン分類器 judge — 専門的な分類

特定ドメインの判定(有害表現の検出など)には、ローカルで動く軽量な分類器を judge にします。

class ClassifierJudge(BaseJudge):
    async def evaluate(self, ctx: JudgeContext) -> JudgeResult:
        score = self.classifier.classify(ctx.text)   # 0.0〜1.0
        label = "NG" if score > self.threshold else "OK"
        return JudgeResult(label, confidence=score, reason=f"risk={score:.3f}")

本番環境に機械学習ライブラリを載せられない場合でも、評価パイプラインの中でだけ動かせばよいので、Naive Bayes のような軽量モデルを judge として使えます。LLM を呼ばないため、これも大量バッチで遅延しません。

三軸を組み合わせる

三つを同じインターフェースに揃えた効果が、ここで効いてきます。

LLM judge       → 内容は事実と整合しているか
メトリック judge → 形式・分量は妥当か
分類器 judge     → ドメイン固有のNGに触れていないか

一つの出力をこの三軸で同時に測り、どれか一つでも NG なら不合格、とすれば、単一の judge では取りこぼす側面までカバーできます。観点が増えたら judge を一つ足すだけで、評価の枠組みはそのまま使えます。

まとめ

  • 評価したい観点は複数ある。一つの judge に押し込めず、観点ごとに適した judge を割り当てる
  • 共通インターフェース(同じ入力・同じ JudgeResult)で束ねると、組み合わせと差し替えが容易になる
  • LLM judge: 意味的な正確さ。本番ロジックを再利用し、生成とは別モデルで評価する
  • メトリック judge: 関数で測れる定量観点。高速・低コスト
  • 分類器 judge: ドメイン特化の軽量モデル。LLM を呼ばず専門判定できる