AIサービスCI/CDの特殊性
従来のソフトウェアのCI/CDは「コードが正しいか」に焦点を当てており、単体テストや統合テストで検証します。しかし、AIサービスの正しさはコードだけでなく、モデルの重み、プロンプト、データにも依存します。これらは「生きている」ものであり、バージョン更新に伴って変化します。AI CI/CDでは追加の検証が必要です:モデルの性能が劣化していないか(新旧バージョンの評価セットでの性能比較)、推論レイテンシが悪化していないか、出力形式が互換性を保っているか。
自動評価パイプライン
AI CI/CDの核心は自動評価です。推奨される階層的テスト戦略:L1-機能テスト(API応答コード、タイムアウト、形式の正確性、秒単位で完了)→L2-ベンチマーク評価(100件の標準評価セットで実行し、過去のベースラインと比較、分単位で完了)→L3-完全評価(1000件以上の評価セットで実行し、敵対的サンプルやエッジケースを含む、時間単位で完了)。L1は各コミットで実行、L2はPR時、L3はリリース前に実行します。
評価パイプラインの実装
import json, time, requests
from openai import OpenAI
client = OpenAI(api_key="your-deepseek-api-key", base_url="https://api.deepseek.com")
class AIEvaluator:
def __init__(self, baseline_file="baseline.json"):
self.baseline = json.load(open(baseline_file)) if baseline_file else {}
self.results = []
def run_eval(self, test_cases, endpoint_url):
for tc in test_cases:
start = time.time()
resp = requests.post(endpoint_url, json={"prompt": tc["input"]})
latency = time.time() - start
if resp.status_code != 200:
self.results.append({"test": tc["name"], "status": "FAIL",
"error": f"HTTP {resp.status_code}"})
continue
output = resp.json()["output"]
score = self._judge(tc, output)
self.results.append({
"test": tc["name"], "status": "PASS" if score >= 0.7 else "FAIL",
"score": score, "latency_ms": round(latency*1000)
})
return self._summary()
def _judge(self, test_case, output):
prompt = f"""AI出力品質を評価(0-1):
入力: {test_case['input']}
期待される概念: {test_case.get('expected', [])}
実際の出力: {output[:1000]}
JSONを返す: {{"score":0.85}}"""
resp = client.chat.completions.create(model="deepseek-chat",
messages=[{"role":"user","content":prompt}], temperature=0)
return json.loads(resp.choices[0].message.content)["score"]
def _summary(self):
passed = sum(1 for r in self.results if r["status"] == "PASS")
total = len(self.results)
avg_latency = sum(r.get("latency_ms", 0) for r in self.results) / total if total else 0
return {
"pass_rate": f"{passed}/{total} ({passed/total*100:.1f}%)",
"avg_latency_ms": avg_latency,
"regression": self._check_regression()
}
def _check_regression(self):
"""ベースラインと比較して劣化を検出"""
if not self.baseline:
return None
current_pass = sum(1 for r in self.results if r["status"] == "PASS")
return current_pass / len(self.results) - self.baseline.get("pass_rate", 1)
evaluator = AIEvaluator()
tests = [{"name":"greeting","input":"你好","expected":["挨拶","友好的"]}]
print(evaluator.run_eval(tests, "http://localhost:8000/chat"))性能ベンチマークとデプロイゲート
効果評価に加えて、性能ベンチマークも重要です:TTFT(最初のトークンまでの時間、ユーザー体験に影響)、スループット(tokens/s、サービス容量に影響)、P50/P95/P99レイテンシ(テールレイテンシは最悪のユーザー体験に影響)、GPUメモリ使用量(デプロイ密度とコストに影響)。デプロイゲートルール:効果スコアはベースラインの97%以上、P95レイテンシはベースラインの120%以下、GPUメモリ使用量はベースラインの110%以下。ゲートを通過しない場合、自動的にデプロイをブロックし、チームに通知します。
GitHub Actions統合例
GitHub ActionsでAI評価を統合:GPU付きセルフホストランナーを使用して評価を実行し、モデルの重みをキャッシュしてパイプラインを高速化します。
ウォーターライン、評価結果はPRコメントとして自動投稿され、マージ前に評価が合格することを要求するブランチ保護ルールを設定します。GPUのないCI環境では、APIプロキシモードを使用できます。CIランナーはローカルでモデルをロードする代わりに、デプロイ済みの評価サービスをAPI経由で呼び出します。評価データのライフサイクル管理
AI評価データセットは不変ではありません。陳腐化したり、「リーク」されたり、拡張が必要になったりします。私たちは評価データのライフサイクル管理プロセスを確立しました:定期的なレビュー(毎月、評価セットの問題が依然として代表的であるか、新しいユーザーの質問パターンを追加する必要があるかを確認)、モデルのブラインドスポット検出(オンラインでのユーザー満足度が低いケースを分析し、典型的な失敗パターンを新しい評価ケースとして評価セットに追加)、不正対策メカニズム(新しいバージョンの評価セットでのモデルのスコアが人為的に「水増し」されていないか監視——例えば、プロンプトに評価セットの問題や回答のキーワードが含まれている場合)、難易度の階層化(タスクの難易度に応じて評価セットをEasy/Medium/Hardの3層に分け、各難易度でのモデルのパフォーマンスを個別に追跡し、簡単なタスクの改善が難しいタスクを犠牲にしないことを保証)。この管理プロセスにより、評価セットが常に実際のユーザーニーズとモデル能力の限界を反映することが保証されます。
評価結果の信頼性と統計的思考
AI評価は「合格/不合格」の二値判断ではなく、不確実性を伴う統計的推定です。100項目の評価セットで85%と90%のスコアの差は単なるサンプリング誤差かもしれません——それだけで「新しいバージョンの方が優れている」と断言することはできません。私たちが導入した統計的厳密性の実践:信頼区間——各評価指標について95%信頼区間を計算し(Bootstrapサンプリングを1000回)、新しいバージョンの信頼区間の下限が古いバージョンの信頼区間の上限よりも大きい場合のみ、有意な改善と見なします;効果量——「有意かどうか」だけでなく「改善の大きさ」も確認します(Cohen's d)。統計的に有意でも実質的に意味のない小さな変動を避けます;多重検定の補正——複数の評価指標を同時に監視する場合、Benjamini-Hochberg補正を使用して偽発見率を制御します。統計的思考は意思決定の質を向上させました——過去の「モデルアップグレード」の30%は、信頼区間分析の導入後、「有意差なし」と判断され、不必要なデプロイリスクを回避しました。
このスキルチェーンを自分で編成してみませんか?
スキルチェーンで開く →