本記事では、従来型のCI/CDゲートが本番AIシステムには不十分である理由を説明します。ベースライン評価、ドリフト検知、シャドウ検証、コスト/レイテンシガードレールを用いたLLMパイプライン向けの実践的なリリースゲーティング手法を紹介します。焦点は予防:本番インフラでの実際のプラットフォームエンジニアリングの教訓に基づき、サイレントなAI回帰をユーザーに届く前に検知することです。
金曜日の午後に更新したRAGパイプラインをデプロイしました。すべての評価がパスし、類似度スコアも良好に見えましたが、月曜日の朝には、埋め込みモデルのドリフトにより、古いチャンクを新しいものより優先するようになり、システムが時代遅れの価格データを自信たっぷりに推奨していました。アラートは鳴らず、テストは失敗せず、ダッシュボードは緑のままで、出力はゴミ同然でした。
その瞬間、私は「緑」をリリースの合図として信頼するのをやめました。私たちに欠けていたのはデプロイの問題ではなく、リリースゲートの問題でした。従来型CI/CDは決定論的なソフトウェア向けに作られたものです。LLMは確率論的です。私たちのゲートもそれに合わせる必要があります。
なぜ従来型CI/CDはLLMパイプラインで失敗するのか
通常のソフトウェアデリバリーでは、ゲートはシンプルです:ユニットテストがパスし、結合テストがパスし、セキュリティチェックがパスしたら、デプロイ。バイナリです。ビルドは緑か赤かです。このモデルは、出荷するものが「振る舞い」である場合に破綻します。
「従来型CI/CDは決定論的なソフトウェア向けに作られたものです。LLMは確率論的です。私たちのゲートもそれに合わせる必要があります。」
本番AIシステムは、今日の関連性スコアが0.82、明日が0.79、来週が0.74になることがあります。どの単一の実行も失敗閾値を超えないため、ユーザーがすでに悪化した回答を体験しているにもかかわらず、誰もページャーを鳴らされません。
私は20年以上にわたり、本番インフラ、OpenStackプライベートクラウド、データベースマイグレーション、CI/CDパイプライン、ミッションクリティカルシステムを管理してきました。従来のDevOpsでは、99.9%のアップタイムを報告しながら金融トランザクションの0.1%をドロップするサーバーは健全ではないことを学びました。それはバグを隠しています。LLM評価も同じです。集計スコアは局所的な失敗を覆い隠します。
最初の失敗モードは評価ドリフトです:スコアは徐々に低下しますが、ハードな閾値に達するほどではありません。2番目は分布シフトです:実際のユーザーは、きれいな評価データセットが想定していなかった短く、乱雑で、奇妙な質問をします。3番目はコンテキストポイズニングです:取得したドキュメントは変わったのに、テストは昨日の質問のままです。
従来型CI/CDゲートは「テストはパスしたか?」と尋ねます。LLMリリースゲートは「振る舞いは許容範囲内に留まったか?」と尋ねます。通常のデプロイゲートは期待出力と実際の出力を比較します。AI CI/CDゲートは、候補の振る舞いを履歴および本番の振る舞いと、コスト、レイテンシ、リスクとともに比較します。従来型ゲートは例外に対して高速に失敗します。LLMリリースゲートはドリフトに対して慎重に失敗します。
「従来型ゲートは例外に対して高速に失敗します。LLMリリースゲートはドリフトに対して慎重に失敗します。」
この区別が重要である理由は、本番AIシステムは爆発するのではなく腐敗していくからです。
私はllm-eval-drift-release-gates-AGENTを構築しました。AIリリースをインフラリリースのように扱うゲート、すなわち測定可能で、再現性があり、可能であれば退屈なものにしたかったからです。退屈は過小評価されています。それによりエンジニアは眠れます。
LLMリリースゲートの構造
最初のゲートはベースライン評価スイートです。固定データセットを候補パイプラインに対して実行し、関連性、忠実性、安全性、根拠性、およびドメイン固有のチェックをスコアリングします。目的はモデルが完璧であることを証明することではありません。回帰を顧客事例になる前に捉えることです。
以下は、私が使用するパターンの簡略化されたPython構造です:
from enum import StrEnum
from pydantic import BaseModel, Field
class GateStatus(StrEnum):
PASS = "pass"
WARN = "warn"
BLOCK = "block"
class EvalResult(BaseModel):
run_id: str
status: GateStatus
scores: dict[str, float] = Field(default_factory=dict)
drift_detected: bool = False
reason: str | None = None
StrEnumを使用することで、複数継承に依存せずにenumを文字列ネイティブに保てます。これはリリースツールでは重要です。なぜならステータス値はしばしばログ記録、シリアライズ、CI内での比較、またはダッシュボードへの受け渡しが行われるからです。
これにより、関連性の崩壊、忠実性の低下、安全でない出力、または不正な評価結果などの明らかな失敗を捉えます。ハードに失敗した場合はパイプラインを即時ブロックし、警告をトリガーした場合は手動承認を必要とします。私はサイレントな警告が好きではありません。それらはただの、きれいなシャツを着た将来のインシデントです。
2番目のゲートは評価ドリフト検知です。固定閾値だけでは不十分です。関連性が0.91から0.86に低下した場合、0.80の閾値はすべて問題ないと判断します。ユーザーは同意しないかもしれません。
そこで、現在のスコアを最近のデプロイからのローリングベースラインと比較します:
def detect_score_drift(
current: dict[str, float],
baseline: dict[str, float],
max_drop_pct: float = 5.0,
) -> tuple[bool, list[str]]:
failures: list[str] = []
for metric, base_value in baseline.items():
current_value = current.get(metric)
if current_value is None:
failures.append(f"Missing metric in current run: {metric}")
continue
drop_pct = ((base_value - current_value) / base_value) * 100
if drop_pct > max_drop_pct:
failures.append(f"{metric} dropped {drop_pct:.2f}%")
return bool(failures), failures
評価スイートは木曜日の夜11時に6%の関連性低下を検知しました。これがなければ、その低下は月曜日までに200人のユーザーに到達していたでしょう。ゲートはビジネスコンテキストを知りませんでした。知る必要もありませんでした。候補が最後の既知の正常リリースより悪かったことを知っていたのです。
3番目のゲートはシャドウトラフィック検証です。完全ロールアウト前に、実際のトラフィックの小さい割合を候補パイプラインにルーティングします。ユーザーは依然として本番の回答を受け取りますが、システムは比較のために候補の出力を記録します。
これはAIに適用されたカナリアデプロイメントです。私はインフラロールアウトでも同じパターンを使用しており、eks-canary-deployment-pipelineリポジトリの背後にあるアイデアも含まれます。LLMの場合の違いは、HTTP 200のみを比較するのではなく、回答品質、取得したコンテキスト、レイテンシ、および候補が本番と疑わしい理由で異なるかどうかを比較する点です。
ジャッジモデルは有用ですが、私はそれを唯一の権威にしません。それはシグナルを生成します。リリースポリシーが決定を下します。
4番目のゲートはコストとレイテンシです。完璧にスコアリングしても3倍のコストがかかるモデルは、有効なリリースではありません。RAGパイプラインに2秒のレイテンシを追加するのも準備ができていません。この考え方は、私のenterprise-rag-guardrails-costopsの作業にも影響を与えました。
def enforce_runtime_budget(
total_tokens: int,
latency_ms: int,
max_tokens: int = 8000,
max_latency_ms: int = 2500,
) -> None:
if total_tokens > max_tokens:
raise RuntimeError(f"token budget exceeded: {total_tokens}")
if latency_ms > max_latency_ms:
raise RuntimeError(f"latency budget exceeded: {latency_ms}ms")
これが失敗した場合、システムはリリースをブロックするか、手動承認にルーティングします。私はデプロイ中にレイテンシと交渉しないことを学びました。それは後で必ず勝つからです。
これら4つのゲートはLLMデプロイメントを完璧にするものではありません。緑のバッジ付きでナンセンスを出荷しにくくするものです。
既存のCI/CDへの組み込み
リリースゲートは別個のサイエンスプロジェクトであるべきではありません。プラットフォームチームがすでにGitHub ActionsやGitLab CIを使用している場合、LLMデプロイメントパイプラインはそのワークフローに適合させるべきです。
パターンは意図的に退屈です:
YAML pipeline: - build - unit-tests - eval-baseline - drift-check - shadow-validate - cost-guard - deploy - post-deploy-monitor
これはdevsecops-pipeline-github-actionsの作業から拡張した形状です。アプリケーションをビルドします。決定論的テストを実行します。ベースライン評価スイートを実行します。ローリングベースラインと比較します。シャドウトラフィックに対して検証します。コストとレイテンシバジェットをチェックします。すべてのゲートが同意した場合のみデプロイします。
ここでMLOpsガードレールがプラットフォームエンジニアにとって有用になります。別個のデプロイメント宗教は必要ありません。チームがすでに信頼しているパイプライン内に、追加のチェックセットが必要なだけです。
以下はCIから呼び出せる小さなPythonエントリポイントです。重要な詳細は、必要なレポートが欠落または不正な形式の場合に正常に失敗することです。生のスタックトレースはリリース戦略ではありません。
import json
import sys
from pathlib import Path
def load_scores(path: str) -> dict[str, float]:
payload = json.loads(Path(path).read_text(encoding="utf-8"))
return {key: float(value) for key, value in payload.items()}
current = load_scores("reports/current_eval.json")
baseline = load_scores("reports/baseline_eval.json")
drifted, reasons = detect_score_drift(current, baseline, max_drop_pct=5.0)
if drifted:
print("LLM release gate blocked:", "; ".join(reasons))
sys.exit(1)
print("LLM release gate passed")
最良のリリースゲートは、チームが実際に使用するものです。設定にMLのPhDが必要なら、それはゲートではなく壁です。
私はクラウドコンピューティングセキュリティのPhDを追求していますが、金曜日に毎回学位論文を必要とするデプロイメントプロセスは望みません。ゲートはCIで実行できるほどシンプルで、悪いリリースをブロックできるほど厳格で、オフィスの装飾品にならないよう柔軟であるべきです。
その最後の部分を認めたくはないですが、学ぶのに時間がかかりました。
間違っていた点と、次に異なる点
私の最初のバージョンは痛いほど厳格でした。すべてのデプロイがブロックされました。評価スイートは小さな文言の変更、無害な書式の違い、境界線上のスコア低下について不満を述べました。チームはゲートを完全にバイパスし始めました。それは私の責任でした。
すべてをブロックするゲートは、ゲートがないより悪いです。少なくともゲートがなければ、人々はリスクを取っていることを知っています。悪いゲートがあれば、彼らはシステムを無視することを学びます。
「すべてをブロックするゲートは、ゲートがないより悪いです。悪いゲートがあれば、人々はシステムを無視することを学びます。」
2番目の間違いは、合成クエリのみで評価したことです。それらはきれいで、完全で、礼儀正しいものでした。実際のユーザーはそうではありません。実際のユーザーは3語を入力し、製品名をスペルミスし、断片を貼り付け、与えていないコンテキストを理解することをシステムに期待します。
評価は素晴らしく見えました。本番はそうではありませんでした。
3番目の間違いは、評価データセットをバージョン管理しなかったことです。新しいエッジケースを追加したとき、古いリリースを新しいベースラインと比較する能力を失いました。今ではTerraform stateをバージョン管理するように評価セットをバージョン管理しています:慎重に、バックアップ付きで、軽度の偏執症とともに。
ボリビアのコチャバンバから構築したことも設計に影響を与えました。巨大な評価実行に燃やす無制限のクラウドクレジットはありませんでした。それにより、サンプリングの最適化、ジャッジコールのキャッシュ、高速PRチェックと重い夜間チェックの分離を余儀なくされました。
制約はより良いエンジニアリングを生み出します。厄介ですが、真実です。
コードだけでなく、自信を出荷する
従来型ソフトウェアでは、コードを出荷し、振る舞いを検証します。AIでは、振る舞いを出荷し、整合性を検証します。リリースゲートは、そのギャップを埋める方法です。
LLMリリースゲートは、本番AIシステムから不確実性を取り除くことはありません。何もできません。しかし、プラットフォームチームに、評価ドリフト、分布シフト、コンテキストポイズニング、コストスパイク、レイテンシ回帰をユーザーが気づく前に捉える実用的な方法を提供します。
それはエージェント型ワークフローの信頼性にとって重要です。AI CI/CDにとって重要です。「すべてのテストがパスした」だけではもはや十分ではないからです。
私はllm-eval-drift-release-gates-AGENTを構築しました。緑のパイプラインが私に嘘をつくのにうんざりしたからです。このリポジトリは、このリリースゲートパターンのためのオープンソース、オフラインファーストのリファレンス実装です。意図的に小さく作られているので、自分のCI/CD環境で検査し、実行し、壊し、強化できます。
リポジトリ: https://github.com/fdaniel-alvarez-dev/llm-eval-drift-release-gates-AGENT,
フォークして、壊して、改善してください。最悪のリリースゲートは、構築しなかったものです。
テクノロジーは急速に進化します。エピソードを見逃さないでください。すべてのポッドキャスト、インタビュー、デモなどをストリーミングするために、私たちのYouTubeチャンネルを購読してください。
Group Created with Sketch.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.