モデル更新をまたぐ回帰テスト
GPT-4からGPT-4o、またはClaude 3から3.5へアップグレードする際に、テストスイートを実行します。
「モデル更新をまたぐ回帰テスト」はCoddyKit上の無料AI Prompt Engineeringレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Prompt Engineering学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Prompt Engineeringコースには全4レッスンが含まれています。
モデル更新でプロンプトが壊れる理由
LLMプロバイダーはモデルを定期的に更新します。GPT-4 → GPT-4o → GPT-4o-2024-11-20、Claude 3 → Claude 3.5 → Claude 3.7のような更新です。更新のたびにモデルの動作が変わります。多くのタスクでは改善しますが、特定のプロンプトでは一部が悪化することもあります。
テストスイートがなければ、ユーザーから報告を受けるまでリグレッションに気づけません。テストスイートがあれば、モデル更新から数分以内にリグレッションを検出できます。
モデル更新の問題
モデルの更新によって、次の3種類の変化が起こる可能性があります。
- 改善: 以前は失敗していたテストケースが合格するようになります。良い変化です
- 変化なし: 動作が変わりません。ほとんどのテストが該当します
- リグレッション: 以前は合格していたテストケースが失敗するようになります。調査が必要です
リグレッション率がわずか1%でも重大です。200個のテストケースがあり、モデル更新後に2個が失敗し始めた場合、その2個が最も重要なユースケースである可能性があります。
MODEL_HISTORY = [
{'model': 'gpt-4', 'deployed': '2023-03-14', 'pass_rate': 0.87},
{'model': 'gpt-4-turbo', 'deployed': '2023-11-06', 'pass_rate': 0.91},
{'model': 'gpt-4o', 'deployed': '2024-05-13', 'pass_rate': 0.93},
{'model': 'gpt-4o-2024-11-20', 'deployed': '2024-11-20', 'pass_rate': None}, # to be measured
]
# Goal: measure pass_rate for the new model before deploying to production新しいモデルでテストスイートを実行する
新しいモデルバージョンが発表されたら、古いモデルと新しいモデルの両方に対してフルテストスイートを実行します。合格率を比較し、動作が変わった具体的なテストケースを特定します。
import openai
client = openai.OpenAI(api_key='sk-...')
def run_suite_on_model(test_cases, system_prompt, model):
results = []
for test in test_cases:
resp = client.chat.completions.create(
model=model,
messages=[
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': test['input']}
],
temperature=0
)
output = resp.choices[0].message.content
passed = test['evaluator'](output)
results.append({'id': test['id'], 'passed': passed, 'output': output})
pass_rate = sum(r['passed'] for r in results) / len(results)
return results, pass_rate
old_results, old_rate = run_suite_on_model(TEST_CASES, PROMPT, 'gpt-4o')
new_results, new_rate = run_suite_on_model(TEST_CASES, PROMPT, 'gpt-4o-2024-11-20')
print(f'Old: {old_rate:.1%} | New: {new_rate:.1%} | Delta: {(new_rate-old_rate):+.1%}')モデルバージョン間の結果を比較する
両方のテストスイートを実行した後、状態が変わったケースを特定します。合格から失敗への変化はリグレッション、失敗から合格への変化は改善です。
def diff_results(old_results, new_results):
old_by_id = {r['id']: r for r in old_results}
new_by_id = {r['id']: r for r in new_results}
regressions = []
improvements = []
for test_id, new_r in new_by_id.items():
old_r = old_by_id.get(test_id)
if old_r is None:
continue
if old_r['passed'] and not new_r['passed']:
regressions.append({'id': test_id, 'old_output': old_r['output'], 'new_output': new_r['output']})
elif not old_r['passed'] and new_r['passed']:
improvements.append({'id': test_id})
print(f'Regressions: {len(regressions)}')
print(f'Improvements: {len(improvements)}')
for reg in regressions:
print(f' REGRESSED: {reg["id"]}')
print(f' Old: {reg["old_output"][:60]}')
print(f' New: {reg["new_output"][:60]}')
return regressions, improvements
regressions, improvements = diff_results(old_results, new_results)テストログでモデルバージョンを追跡する
すべてのテスト実行ログには、正確なモデル識別子を含める必要があります。「gpt-4o」だけでなく、完全なバージョン付き文字列「gpt-4o-2024-11-20」を記録します。OpenAIとAnthropicは、エイリアス名(例:「gpt-4o」)を変更せずに、その背後のモデルを頻繁に更新します。そのため、過去の動作をデバッグするにはログが不可欠です。
import openai, json
from datetime import datetime, timezone
client = openai.OpenAI(api_key='sk-...')
def run_test_with_version_tracking(test, system_prompt, model_alias):
resp = client.chat.completions.create(
model=model_alias,
messages=[
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': test['input']}
],
temperature=0
)
output = resp.choices[0].message.content
return {
'test_id': test['id'],
'model_alias': model_alias,
'model_actual': resp.model, # The exact versioned model ID returned by the API
'timestamp': datetime.now(timezone.utc).isoformat(),
'output': output,
'passed': test['evaluator'](output)
}リグレッションを調査する
リグレッションが見つかった場合は、プロンプトを更新する前に調査します。次の点を確認してください。プロンプトが悪化したのでしょうか。それとも、モデルが改善した結果、動作が変わったのでしょうか。
例として、GPT-4oの後継モデルが形式の指示により厳密に従うようになり、以前のテストが少し仕様に準拠していない出力を期待していたために失敗する場合があります。この場合はモデルが改善されており、更新が必要なのはプロンプトではなくテストです。
def investigate_regression(test_id, old_output, new_output, prompt, user_input):
# Step 1: check if old behavior was actually wrong
judge_prompt = (
f'Given this task prompt: {prompt}\n'
f'And this user input: {user_input}\n\n'
f'Which output is better?\n'
f'A) {old_output}\n'
f'B) {new_output}\n\n'
'Reply with A or B and one sentence explanation.'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': judge_prompt}],
temperature=0
)
verdict = resp.choices[0].message.content
print(f'Judge verdict for {test_id}: {verdict}')
# If judge says B (new output) is better: update the test, not the promptプロンプトとテストのどちらを更新するか
リグレッションの調査後は、次の3つの対応から1つを選びます。
- プロンプトを更新する: 同じ動作を実現するために、新しいモデルでは異なる指示の表現が必要です
- テストを更新する: 以前の期待出力が誤っているか、最適ではありません。新しい出力のほうが実際には優れています
- リグレッションを受け入れる: 新しいモデルではこのタスクを安定して実行できません。このユースケースでは古いモデルのエイリアスを使います
REGRESSION_ACTIONS = {
'prompt_updated': {
'when': 'New model ignores instruction the old model followed. Same behavior needed.',
'action': 'Add stronger/rephrased instruction. Re-run tests on new model.'
},
'test_updated': {
'when': 'New model output is objectively better but fails old test criteria.',
'action': 'Update expected output in test. Document the improvement.'
},
'model_pinned': {
'when': 'New model cannot perform this task reliably despite prompt changes.',
'action': 'Pin the model alias to the old versioned ID for this endpoint.'
}
}モデルバージョンを固定する
モデル更新によって許容できないリグレッションが発生した場合は、調査中、モデルを以前のバージョン付きIDに固定します。本番環境ではエイリアス(例:「gpt-4o」)を使わず、必ず特定のバージョンを指定します。
# Bad practice: alias can point to different model versions over time
client.chat.completions.create(
model='gpt-4o', # could be any version at any time
messages=[...]
)
# Good practice: pin to a specific version for production
client.chat.completions.create(
model='gpt-4o-2024-11-20', # guaranteed behavior
messages=[...]
)
# Track in config
MODEL_CONFIG = {
'sentiment_classifier': 'gpt-4o-2024-11-20',
'code_generator': 'gpt-4o-2024-11-20',
'summarizer': 'gpt-4o-mini-2024-07-18',
}CIでリグレッションを自動化する
設定内のモデルバージョンが変更されたときに、リグレッションテストスイートが自動的に実行されるようにします。GitHub ActionsなどのCIを使って、デプロイ前にリグレッションを検出します。
# .github/workflows/prompt_regression.yml (YAML, shown as comment)
# name: Prompt Regression Tests
# on:
# push:
# paths:
# - 'config/models.json' # triggers when model version changes
# - 'prompts/*.txt' # triggers when prompts change
# jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - name: Install dependencies
# run: pip install pytest openai jsonschema
# - name: Run prompt test suite
# run: pytest tests/prompts/ -v --junitxml=results.xml
# env:
# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
# In Python: read model version from config
import json
with open('config/models.json') as f:
MODEL_CONFIG = json.load(f)
MODEL = MODEL_CONFIG['sentiment_classifier']プロバイダー間のリグレッションへの対処
リグレッションテストは、プロバイダーを切り替える場合にも適用されます。GPT-4oからClaudeへ、ClaudeからGeminiへ切り替える場合などです。同じテストスイートを使うことで、新しいプロバイダーの動作の違いに合わせて変更が必要なプロンプトを特定できます。
def cross_provider_test(test_cases, system_prompt, providers):
all_results = {}
for provider, config in providers.items():
results, rate = run_suite_on_model(
test_cases, system_prompt, model=config['model']
)
all_results[provider] = {'rate': rate, 'results': results}
print(f'{provider}: {rate:.1%}')
# Find cases that fail on one provider but not another
for test in test_cases:
outcomes = {
p: next(r for r in all_results[p]['results'] if r['id'] == test['id'])['passed']
for p in providers
}
if not all(outcomes.values()):
failing = [p for p, passed in outcomes.items() if not passed]
print(f' {test["id"]}: FAILS on {failing}')
return all_results合格率の推移を監視する
テスト履歴ログを読み取り、時間の経過に伴う合格率をグラフ化します。低下傾向は、プロンプトの品質低下またはモデルドリフトを示します。急激な低下は、モデル更新やプロンプト変更によるリグレッションイベントを示します。
import json
def plot_pass_rate_trend(history_file='test_history.jsonl'):
runs = []
with open(history_file) as f:
for line in f:
runs.append(json.loads(line))
runs.sort(key=lambda r: r['run_id'])
print('Pass rate trend:')
for run in runs[-10:]: # last 10 runs
bar = '#' * int(run['pass_rate'] * 40)
print(f"{run['run_id'][:10]} {run['model']:30} {run['pass_rate']:.0%} |{bar}|")
# Detect regression
if len(runs) >= 2:
delta = runs[-1]['pass_rate'] - runs[-2]['pass_rate']
if delta < -0.05:
print(f'ALERT: pass rate dropped {delta:.0%} since last run!')
plot_pass_rate_trend()理解度チェック
新しいモデルバージョンによってテストが失敗し、調査の結果、新しいモデルの出力のほうが実際には優れていると判明した場合、正しい対応は何ですか。
まとめ: モデル更新に対するリグレッションテスト
モデル更新時のリグレッションテストにおける主な実践方法は次のとおりです。
- 古いモデルと新しいモデルの両方でフルテストスイートを実行する — 合格率を比較し、具体的な失敗を差分比較します
- 正確なモデルバージョンを追跡する — エイリアスだけでなく、すべてのテストログに記録します
- 各リグレッションを調査する: プロンプトの問題、テストの問題、モデルの能力の問題のどれかを確認します
- 本番環境ではモデルを固定する — エイリアスではなく、特定のバージョン付きIDを使います
- CIで自動化する — モデル設定またはプロンプトファイルの変更をトリガーにします
次のレッスンでは、ツールのサポートを利用して完全なプロンプトテストスイートを構築します。
よくある質問
「モデル更新をまたぐ回帰テスト」レッスンは無料ですか?
はい。「モデル更新をまたぐ回帰テスト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Prompt Engineeringコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Prompt Engineeringコースには全4レッスンが含まれています。
「モデル更新をまたぐ回帰テスト」で何を学びますか?
GPT-4からGPT-4o、またはClaude 3から3.5へアップグレードする際に、テストスイートを実行します。 ブラウザで直接実行するハンズオンコードでAI Prompt Engineeringを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Prompt Engineeringを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Prompt Engineeringは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「モデル更新をまたぐ回帰テスト」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Prompt Engineeringレッスンでコードを書いて実行できますか?
はい。すべてのAI Prompt Engineeringレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- プロンプトのテストケース作成
- アサーションベースのプロンプトテスト
- モデル更新をまたぐ回帰テスト
- プロンプトテストスイートの構築