ai-eval-ci
CI/CDパイプライン内でAIエージェントとLLM評価を実行し、AI出力品質が低下した際にビルドを失敗させる自動化された品質ゲートを構築できます。「AIエージェントをテストしたい」「CIに評価機能を追加したい」「プロンプトの劣化を検出したい」「モデルを比較したい」「LLM出力品質を評価したい」「AI品質ゲートをセットアップしたい」「デプロイ前にエージェントをベンチマークしたい」といった要望に対応します。Cobalt、Promptfoo、Braintrustなどの評価フレームワーク、LLM-as-judge スコアリング、閾値ベースのアサーション、GitHub Actionsとの連携に対応しています。
description の原文を見る
Run AI agent and LLM evaluations in CI/CD pipelines — automated quality gates that fail the build when AI output quality drops. Use when someone asks to "test my AI agent", "add evals to CI", "catch prompt regressions", "compare models", "evaluate LLM output quality", "set up AI quality gates", or "benchmark my agent before deploying". Covers eval frameworks (Cobalt, Promptfoo, Braintrust), LLM-as-judge scoring, threshold-based assertions, and GitHub Actions integration.
SKILL.md 本文
CI における AI Eval
概要
コードをテストするのと同じ方法で AI エージェントと LLM 出力をテストします。CI で実行される自動評価により、ベースラインと比較し、品質が低下したときにビルドを失敗させることができます。ダッシュボードを手動でチェックする必要はありません。npx eval run --ci を実行して、赤または緑のビルドが表示されるだけです。
使用する場合
- AI 機能を本番環境にデプロイする前に品質ゲートを追加する
- システムプロンプトまたはモデルが変更されたときにプロンプト回帰をキャッチする
- モデルパフォーマンスを比較する(GPT-4o vs Claude Sonnet vs ローカル Llama)
- RAG パイプラインの精度をテストデータセットに対して検証する
- エージェントツール呼び出しの精度とレイテンシをベンチマークする
手順
戦略 1: Promptfoo(構成駆動型評価)
Promptfoo は最も人気のあるオープンソース評価フレームワークです。YAML でテストケースを定義し、複数のプロバイダーに対して実行し、比較マトリックスを取得します。
# promptfooconfig.yaml — 評価構成
# 3 つのモデル全体で顧客サポートエージェントをテストし、品質アサーションを実施
description: "Customer support agent eval"
providers:
- id: openai:gpt-4o
- id: anthropic:messages:claude-sonnet-4-20250514
- id: ollama:llama3.1:8b
prompts:
- |
You are a customer support agent for a SaaS product.
Respond helpfully and accurately. If you don't know, say so.
Customer message: {{message}}
tests:
- vars:
message: "How do I reset my password?"
assert:
- type: llm-rubric
value: "Response explains the password reset process clearly"
- type: not-contains
value: "I don't know"
- type: latency
threshold: 3000 # Must respond within 3 seconds
- vars:
message: "Can I get a refund for my annual plan?"
assert:
- type: llm-rubric
value: "Response acknowledges the refund request and explains the policy"
- type: not-contains
value: "I'm an AI" # Don't break character
- vars:
message: "Your product deleted all my data!"
assert:
- type: llm-rubric
value: "Response shows empathy, takes the issue seriously, and offers next steps"
- type: sentiment
threshold: 0.3 # Must not be dismissive
- vars:
message: "What's the weather in Tokyo?"
assert:
- type: llm-rubric
value: "Response politely redirects to product-related topics"
- type: not-contains
value: "Tokyo" # Should not answer off-topic questions
# 評価をローカルで実行
npx promptfoo@latest eval
# しきい値付きで CI で実行 — いずれかのテストが失敗した場合はゼロ以外で終了
npx promptfoo@latest eval --ci --output results.json
# 2 つのプロンプトバージョンを比較
npx promptfoo@latest eval --prompts prompt-v1.txt prompt-v2.txt --share
戦略 2: カスタム評価フレームワーク(TypeScript)
完全な制御が必要な場合 — カスタムスコアリングロジック、データベースバックアップテストセット、ドメイン固有メトリクス。
// eval.ts — CI 統合を備えたカスタム AI 評価フレームワーク
/**
* AI エージェント/LLM に対して評価スイートを実行します。
* 各評価は入力、期待される動作、およびスコアリング基準を定義します。
* スコアがしきい値を下回った場合、コード 1 で終了します。
*/
import OpenAI from "openai";
interface EvalCase {
name: string;
input: string;
rubric: string; // 「良い」状態の定義
threshold: number; // 最小スコア 0-1
metadata?: Record<string, unknown>;
}
interface EvalResult {
name: string;
score: number;
pass: boolean;
output: string;
reasoning: string;
latencyMs: number;
}
const openai = new OpenAI();
/**
* LLM-as-judge を使用して AI 出力をスコアリングします。
* スコア 0-1 と推論を返します。
*/
async function judge(output: string, rubric: string): Promise<{ score: number; reasoning: string }> {
const response = await openai.chat.completions.create({
model: "gpt-4o-mini", // 判定用の低コストモデル
messages: [
{
role: "system",
content: `You are an eval judge. Score the AI output against the rubric.
Return JSON: {"score": 0.0-1.0, "reasoning": "brief explanation"}
Score 1.0 = perfect match. Score 0.0 = complete failure.`,
},
{
role: "user",
content: `Rubric: ${rubric}\n\nAI Output:\n${output}`,
},
],
response_format: { type: "json_object" },
temperature: 0, // 決定的な判定
});
return JSON.parse(response.choices[0].message.content!);
}
/**
* AI エージェントに対して単一の評価ケースを実行します。
*/
async function runEval(
agentFn: (input: string) => Promise<string>,
evalCase: EvalCase
): Promise<EvalResult> {
const start = Date.now();
const output = await agentFn(evalCase.input);
const latencyMs = Date.now() - start;
const { score, reasoning } = await judge(output, evalCase.rubric);
return {
name: evalCase.name,
score,
pass: score >= evalCase.threshold,
output: output.slice(0, 200),
reasoning,
latencyMs,
};
}
/**
* すべての評価を実行し、CI に対して適切なコードで終了します。
*/
async function runSuite(
agentFn: (input: string) => Promise<string>,
cases: EvalCase[]
): Promise<void> {
console.log(`Running ${cases.length} evals...\n`);
const results: EvalResult[] = [];
for (const evalCase of cases) {
const result = await runEval(agentFn, evalCase);
results.push(result);
const icon = result.pass ? "✅" : "❌";
console.log(`${icon} ${result.name}: ${result.score.toFixed(2)} (threshold: ${evalCase.threshold}) [${result.latencyMs}ms]`);
if (!result.pass) {
console.log(` Reasoning: ${result.reasoning}`);
}
}
// 概要
const passed = results.filter((r) => r.pass).length;
const failed = results.filter((r) => !r.pass).length;
const avgScore = results.reduce((s, r) => s + r.score, 0) / results.length;
console.log(`\n📊 Results: ${passed} passed, ${failed} failed (avg score: ${avgScore.toFixed(2)})`);
// CI 終了コード
if (failed > 0) {
console.log("\n❌ Eval suite FAILED — quality below threshold");
process.exit(1);
} else {
console.log("\n✅ Eval suite PASSED");
}
}
export { runSuite, EvalCase };
戦略 3: GitHub Actions 統合
# .github/workflows/ai-eval.yml
name: AI Eval
on:
pull_request:
paths:
- "prompts/**"
- "src/agents/**"
- "eval/**"
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- name: Run AI evals
run: npx tsx eval/run.ts --ci
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Post results to PR
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('eval/results.json'));
const body = results.map(r =>
`${r.pass ? '✅' : '❌'} **${r.name}**: ${r.score.toFixed(2)} (${r.latencyMs}ms)`
).join('\n');
github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: `## AI Eval Results\n\n${body}`
});
例
例 1: RAG チャットボットに品質ゲートを追加
ユーザープロンプト: 「RAG カスタマーサポートボット向けの自動評価を設定してください。50 個の既知の Q&A ペアの精度をテストし、精度が 85% を下回った場合はデプロイを失敗させるべきです。」
エージェントが実行すること:
- 50 個の既知の Q&A ペアからテストデータセットを作成する
- 各テストに対して llm-rubric アサーションを含む promptfoo 構成を書く
- 合格しきい値を 0.85 に設定する
prompts/またはsrc/agents/への PR で実行する GitHub Actions ワークフローを追加する- PR コメントとして評価結果を投稿する
例 2: 切り替え前にモデルを比較
ユーザープロンプト: 「GPT-4o から Claude Sonnet への切り替えを検討しています。評価スイートを両方のモデルに対して実行し、どちらがより優れたパフォーマンスを発揮しているかを示してください。」
エージェントが実行すること:
- 両方のプロバイダーで promptfoo を構成する
- 既存の評価スイートを両方のモデルに対して実行する
- テストごとのスコア、レイテンシ、およびコストを含む比較テーブルを生成する
- スコアとコスト比に基づいて推奨する
ガイドライン
- すべてのプロンプト変更を評価する — プロンプトをコードのように扱い、デプロイ前にテストする
- LLM-as-judge で十分です — GPT-4o-mini は数セント程度で、人間の判断とよく相関します
- 判定には temperature 0 を使用する — 決定的なスコアリングはノイズを削減します
- テストセットを多様に保つ — ハッピーパス、エッジケース、敵対的入力、オフトピック
- 現実的なしきい値を設定する — 0.7 から始めて、エージェントが改善するにつれて厳しくする
- 時間経過に伴うスコアを追跡する — 結果をログに記録して徐々的な品質低下を検出する
- 評価コストを本番コストから分離する — 評価は安価な判定モデルを使用し、本番は最良のものを使用する
- 評価結果をキャッシュする — 変更されていないテストを再実行しない; 入力 + プロンプトをハッシュしてキャッシュキーにする
- PR で評価を実行する(主要ブランチだけではなく) — マージ前に回帰をキャッチする
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- TerminalSkills
- ライセンス
- Apache-2.0
- 最終更新
- 2026/5/4
Source: https://github.com/TerminalSkills/skills / ライセンス: Apache-2.0