あなたは、モデルのAPIを一度も呼び出すことなく、LLM駆動の機能に対して28個のユニットテストを実行できる内部ツールを構築しました。モデルをモック化可能なインターフェースでラップし、決定論的、ヒューリスティック、およびLLMベースの3つの評価レイヤーを追加することで、これを実現しました。

標準的なアサーションは、LLMが文章を生成した瞬間に機能しなくなります。同じプロンプトでも実行ごとに異なる文章が生成される可能性があるため、assertEqual(output, expected) は、モデルが正しく動作していても失敗としてフラグを立ててしまいます。多くのエンジニアリンググループは、検証なしで機能をリリースするか、あるいは、常に変化するターゲットを静的なライブラリであるかのように扱い、モデルそのものをテストしようとします。

なぜこの問題が重要なのか

LLMは現在、メールの送受信、サポートへの返信、コンテンツ生成といった、顧客向けのワークフローの中に組み込まれています。たった一つのハルシネーション(事実の捏造)や、漏洩した識別子によって、ブランドの評判が損なわれたり、プライベートなデータが露出したり、コンプライアンス違反を引き起こしたりする可能性があります。信頼できるテスト戦略がなければ、チームは不安定な失敗(flaky failures)の追跡に時間を浪費するか、本番環境で初めて表面化するバグをリリースすることになります。

アプローチ:モデルの責任範囲を縮小する

最初のステップは、LLMが実際に何を行うかを制限することでした。著者のシステムでは、モデルはアウトリーチメッセージのドラフト作成のみを行います。すべてのルーティングロジック、状態管理、および安全性チェックは、通常のコード内に留められます。モデルを単一の明確に定義された出力に限定することで、周囲のシステムは決定論的でテスト可能な状態を維持できます。

これを可能にするために、LLMはプロバイダー・インターフェースの背後に配置され、テストではフェイク(偽)バージョンを使用できるようになっています。本番環境では実装が外部APIを呼び出しますが、テストスイートでは軽量なフェイクが定型的なレスポンスを返します。残りのコードはインターフェースとのみやり取りするため、ネットワークに一切触れることなく、ユニットテストによってワークフロー全体を検証できます。その結果、28個のテストによって検証可能な、予測可能なコアが実現します。

正直な評価ハーネス

スコープを絞ったとしても、モデルの出力は非決定論的なままです。そのため、著者は、各レイヤーが異なる種類のリスクを処理する3層の評価ハーネスを構築しました。

  • レイヤー1 – 決定論的なチェック 単純な正規表現ルールを使用して、誤った建物IDや禁止トークンなどの具体的なエラーを検出します。これらのチェックは高速で、バイナリ(合格/不合格)の結果を返します。

  • レイヤー2 – ヒューリスティックなチェック スクリプトがハルシネーションによる数値や日付を探し、明らかな事実の捏造をフラグ立てします。数値的な手がかりがない誤った主張は見逃してしまうことがあり、著者はその限界を率直に認めています。

  • レイヤー3 – LLMジャッジ 二次的なモデルがトーンやプロフェッショナリズムを評価します。このステップは別の確率的なシステムに依存しているため、決定論的なルールでは不可能な、主観的な側面に対してのみ使用されます。

このハーネスの鍵は、評価に使用されるデータセットにあります。著者は、既知の失敗パターン(特定の罠やドメイン知識)をエンコードしたため、ハーネスは実際に発生したミスを正確にテストできます。これは魔法のような「万能なもの」ではなく、的を絞ったセーフティネットなのです。

チームにとっての意味

  • LLMの役割を小さく保つ。 責任範囲を狭めることで、分離とテストが容易になります。
  • ルーティング、状態、安全性はコードに置く。 従来のロジックは決定論的であり、完全にテスト可能です。
  • モデルをモック化可能なインターフェース経由で公開する。 ユニットテストは外部呼び出しなしで実行されるため、テストスイートは高速かつ信頼性が高くなります。
  • 評価を階層化する。 決定論的なルールから始め、既知のハルシネーションに対してヒューリスティックを追加し、主観的な品質チェックのためにLLMジャッジを予約します。
  • 限界を明示する。 どのレイヤーも完璧を保証するものではありません。ハーネスは、明示的に検出するようにプログラムされたものしかキャッチできません。

反論:モデル自体をユニットテストすることは依然として不可能である

著者は、モデルは常に変化するターゲットであることを認めています。LLMジャッジのレイヤーでさえ、評価しようとしているものと同じ非決定論性を引き継いでいます。その結果、システムは、すべてのハルシネーションやポリシー違反がリリース前にキャッチされることを決して保証できません。このアプローチはリスクを軽減するものであり、排除するものではありません。また、新しい失敗モードが現れるにつれて、評価データを最新の状態に保つチームの能力に依存しています。

まとめ

LLMの正確な出力をアサーションするような古典的なユニットテストを書くことはできませんが、モデルの影響範囲を限定し、インターフェースを置換可能にし、階層化された透明なチェックを通じて出力をスクリーニングするシステムを構築することは可能です。その組み合わせにより、不安定になりがちなコンポーネントを、より大規模でテスト可能なアプリケーションの予測可能な一部へと変えることができるのです。