sue@blog ~ /posts/llm-output-verification-implementation
$ cd ../
$ cat post.metadata

LLM出力の検証 ③実装編 — 多段防御パイプラインの組み立て

date: 2026-06-26 LLM ハルシネーション 実装
第2回で並べた手法を、実際に動く1本のパイプラインに組む。安価→高価の多段防御をコードに落とし、オフライン回帰テスト×オンライン監視で二層化し、HITL(人手レビュー)で継続改善ループを回す。最後に、コスト爆発・コンテキスト依存・過信といった実装の地雷も。
「LLM出力の検証」連載(全3回)
① 全体像② 手法カタログ③ 実装編(この記事)

手法を1つ選んで終わり、にはならない——というのが連載の結論だった。実装の主役は個別アルゴリズムの選定よりも、それらをコスト/精度/レイテンシのトレードオフの中でどう配置・接続するかだ。2024–2025の実務的コンセンサスは、多段防御(cascade)+オフライン/オンラインの二層化+HITL+ゴールデンデータセットによる回帰テスト。順に組んでいく。

1. ランタイム: 安価→高価の多段防御

1リクエストを最も安価なチェックから流し、しきい値を超える低信頼ケースだけを上位の高価な検証へエスカレーションする。これにより大半のリクエストは安い段で片付き、高価なLLMジャッジや人手は本当に怪しいものにだけ使われる。

ランタイム検証パイプライン ユーザー入力 ① 入力ガード(決定論・<5ms) regex / ブロックリスト / プロンプトインジェクション検知 本体LLM 生成 RAGグラウンディングで「言える主張」を文脈に縛る(予防) ② 安価フィルタ(全件・低コスト) スキーマ検証 / 意味エントロピー / NLI忠実性(MiniCheck・HHEM) 信頼度 ≥ 閾値? (較正済みスコア) ③ LLM-as-judge / 強モデルで再検証 低信頼のみ。NGなら → ④ 人手レビュー(HITL)へ No(低信頼) 出力を返す 大半はここで完了 Yes
図1: ランタイムの多段防御。安価な段(①②)で全件を捌き、較正済み信頼度が閾値を割ったものだけ③④へ。研究例ではカスケード化で品質を保ちつつ大幅なコスト削減が報告されている。

骨組みをコードにすると、こうなる。フレームワークに依らない素朴な関数で十分だ。

# 多段防御: 安価な段から順に流し、怪しいものだけ上位へ
def verify(output, context, schema):
    # ① 決定論ルール(<1ms) — 形式・安全の門前払い
    if not schema_ok(output, schema):        # JSON Schema / Pydantic
        return reject("schema_violation")
    if moderation_flagged(output):           # Llama Guard / Moderation API
        return reject("unsafe")

    # ② 軽量シグナル(低コスト・全件) — 文脈忠実性スコア
    faith = minicheck_score(output, context)  # 小型グラウンディング判定器
    if faith >= 0.90:
        return accept(output, faith)         # 自信を持って通す

    # ③ しきい値未満だけ高価な検証へエスカレーション
    verdict = llm_judge(output, context)       # LLM-as-judge / 強モデル
    if verdict.ok:
        return accept(output, faith)

    # ④ それでも確証が持てなければ人手へ
    enqueue_human_review(output, context, faith, verdict)
    return defer("needs_human")
閾値は較正してから
カスケードの肝は faith >= 0.90 の閾値だが、スコアが較正(calibration)されていないと誤ルーティングが起きる。「0.9と言ったら本当に約9割正しい」状態か、ECE(期待較正誤差)と信頼性図で必ず確認する。RLHF後のモデルは過信しがちなので、必要なら temperature scaling で後付け補正してから閾値を引く。

2. 形式は「制約」で殺す — 構造化出力

「JSONが壊れる/必須欠落/enum外」は検知ではなく制約で予防するのが一番安い。制約付きデコード(Outlines・OpenAI Structured Outputs)か、生成後スキーマ検証+自動リトライ(Instructor)。ツール呼び出しの引数検証にも同じ仕組みが効く。

from pydantic import BaseModel, field_validator
import instructor

class Answer(BaseModel):
    summary: str
    citations: list[str]            # 引用元IDは必須
    @field_validator("citations")
    def non_empty(cls, v):
        if not v: raise ValueError("引用が空")  # → instructorが自動リトライ
        return v

client = instructor.from_openai(OpenAI())
ans = client.chat.completions.create(
    model="...", response_model=Answer, max_retries=2, messages=[...])

ただし形式の正しさは内容の真偽を保証しない。「引用IDが入っている」ことと「その引用が主張を裏付けている」ことは別。後者は第2回の引用検証(ALCE/AIS)やNLI照合で別途確かめる。

3. 二層化: オフライン回帰テスト × オンライン監視

ランタイムのガードだけでは「自分が変更を入れた瞬間に静かに劣化した」を捕まえられない。検証を2層に二重化する。

継続改善ループ ― 二層化 + HITL フィードバック ① オフラインCIゲート ゴールデンで回帰検出 → 合格のみデプロイ ② 本番+オンライン監視 5–10%サンプリング ドリフト・異常を検知 ③ HITLレビュー 不確実性サンプリングで 低信頼だけ人手判定 ④ ゴールデン更新 修正を正解例に追加 =回帰テスト強化 人手の修正がゴールデンに蓄積 → 次の回帰テストを強化し、自動段の精度が上がる(複利)
図2: 二層化+HITLの継続改善ループ。①は「自分が起こす変化」、②は「自分に降りかかる変化」を捕まえる。③の人手修正が④ゴールデンに蓄積し①へ還る——修正が積もるほど自動段が賢くなり、人手必要量が逓減する。

4. HITL: 人手を「全部」でなく「怪しいものだけ」に

人手は最終的なground truthだが、最も高コスト・低スループット。だから全件レビューは破綻する。自動検証で「低信頼/閾値ギリギリ/新規パターン」とフラグされたトレースを、不確実性サンプリング(uncertainty sampling)で高価値ケースに絞ってレビューキューへ集約する(担当割当・Slack/Webhook通知)。人間の修正は二重に効く——(1)ゴールデンに新規正解例として追加され回帰テストを強化、(2)プロンプト/ガード改善やLLMジャッジ自体の較正の学習信号になる。アクティブラーニングでレビュー量を3〜5割削減しつつ品質維持できるという報告もある。

5. すぐ使えるOSS早見表

ツール系統用途
selfcheckgptA自己一貫性検知(NLI/Prompt/BERTScore)
LM-Polygraph / UQLMA不確実性推定手法の統一実装・ベンチ
Lookback-LensA-2attention比率で文脈幻覚検知
RAGASB-2RAG忠実性/関連性(reference-free)
MiniCheckB-2小型・GPT-4並みグラウンディング判定
HHEM-2.1-Open / LeaderboardB-2事実整合スコア+LLM幻覚率トラッキング
AlignScore / SummaCB-2NLIベースの忠実性チェック(軽量・決定論)
FActScore / SAFEB-1長文の事実性評価(KB/検索)
RefChecker / Patronus LynxB-2細粒度・説明付きの忠実性判定
DeepEvalC/BG-Eval・faithfulness・hallucination指標群
Prometheus-eval / G-EvalC-1オープン評価LM/CoT+確率加重評価
Guardrails AI / NeMo GuardrailsD-1入出力バリデータ+reask/レール
Llama Guard / Granite GuardianD-2安全分類+RAG groundedness
Outlines / InstructorD-3制約付きデコード/スキーマ検証+リトライ
LlamaFirewall / Lakera GuardD-4プロンプトインジェクション・攻撃検知
Langfuse / Arize Phoenix / LangSmith運用トレース→評価→HITLレビュー連携

6. 実装の落とし穴

  1. コスト爆発: SAC3・CoVe・オリジナルの意味的エントロピー・複数審査員は生成回数が掛け算的に増える。安価な決定論ルール(regex/スキーマ)を前段に置き、LLM呼び出し回数そのものを減らすのがコスト最適化の要。多段化はレイテンシ(3層で約350〜400ms)と保守の複雑性も増やす。
  2. LLMジャッジのバイアス: 位置バイアス(先/後を不当に優遇)、冗長性バイアス(長い回答を好む)、自己選好バイアス(同系列モデルを高評価)。評価器と本番が同系統モデルだと盲点を共有する。位置入替の平均化・複数審査員・基準明文化・少数の人手ラベルで継続検証する。
  3. 「忠実だが不正解」の取りこぼし: RAG忠実性検証は出典自体の誤りを防げない。検索品質(インデックス・チャンキング)が悪いと「根拠付きの誤答」を量産する。事実性・忠実性・検証可能性は別軸として分離評価する。
  4. ホワイトボックス手法の制約と非英語: SEP・INSIDE・SAPLMA・Lookback Lensは隠れ状態/attentionが要るので外部APIには使えない。さらに日本語などでは claim分解・NLI判定の精度が落ちやすいのは実務上の盲点。日本語データでの自前検証を必ず挟む。
  5. ゴールデンデータセットの陳腐化: 回帰テストの根幹だが、入力分布が変われば形骸化する。オンライン監視とHITLで継続的に拡充する運用が前提。
  6. ガードレールは破られる: encoding/evasion型攻撃で回避されうるし、誤検知で正当な入力を弾く over-refusal のUX低下もある。層ごとに fail-open/fail-closed を設計し、継続更新する。
連載のまとめ
LLM出力検証に銀の弾丸はない。「おかしさ」を5種類に分解し(第1回)、4系統の手法を棚卸しし(第2回)、それらを安価→高価の多段防御として組み、オフライン回帰テストとオンライン監視で二層化、HITLでground truthを供給して継続改善ループを回す(第3回)。個別アルゴリズムの選定以上に、この運用アーキテクチャの設計こそが本番の信頼性を決める。

主な参考文献

連載
① 全体像② 手法カタログ③ 実装編(この記事)
sue@blog ~ $ _