top of page

Tutorial 4: Track Prompt Regression Over Time

  • Contributor
  • 4 days ago
  • 2 min read

You changed the prompt. Something got better. Did something also get worse? Track to know.

Step 1: Establish a Baseline (10 min)

Before any change, run your full eval set on the current prompt. Record:

{
  "timestamp": "2026-10-01T10:00:00Z",
  "prompt_version": "v1.0",
  "model": "claude-sonnet-4-6",
  "total_cases": 100,
  "passed": 87,
  "failed": 13,
  "per_case": [
    {"id": "case-001", "passed": true, "score": 4.5},
    ...
  ]
}

This is the baseline. Compare against this.

Step 2: Version Your Prompts (5 min)

Treat prompts like code:

PROMPTS = {
    "v1.0": "You are a helpful assistant. Answer the question concisely.",
    "v1.1": "You are a helpful assistant. Answer concisely and cite sources.",
    "v1.2": "You are an expert assistant. Provide concise, sourced answers.",
}

Or in files: prompts/answer_question_v1.0.txt. Git-tracked.

Step 3: Run Eval on Each Version (15 min)

results_v10 = run_eval(eval_set, prompt=PROMPTS["v1.0"])
results_v11 = run_eval(eval_set, prompt=PROMPTS["v1.1"])
results_v12 = run_eval(eval_set, prompt=PROMPTS["v1.2"])

save_results({
    "v1.0": results_v10,
    "v1.1": results_v11,
    "v1.2": results_v12,
})

Same eval set; different prompts. Apples-to-apples.

Step 4: Compare Pass Rates (5 min)

v1.0: 87/100 = 87%
v1.1: 92/100 = 92% (+5%)
v1.2: 89/100 = 89% (+2%)

v1.1 wins on pass rate. But...

Step 5: Find the Regressions (10 min)

def find_regressions(baseline, new):
    regressed = []
    for case_id in baseline:
        if baseline[case_id]["passed"] and not new[case_id]["passed"]:
            regressed.append(case_id)
    return regressed

Cases that PASSED in v1.0 but FAIL in v1.1.

Even with 92% pass rate, you may have new failures. Find them.

Step 6: Categorize Regressions (10 min)

def categorize(regressions, eval_set):
    by_category = defaultdict(list)
    for case_id in regressions:
        category = eval_set[case_id]["category"]
        by_category[category].append(case_id)
    return by_category

Is the regression in one specific area? Maybe v1.1 fixed some cases but broke a category.

Step 7: Set a Regression Threshold (5 min)

Decide: how many regressions are acceptable?

def is_safe_to_ship(baseline, new):
    regressions = find_regressions(baseline, new)
    new_failures = len(regressions)
    improvements = find_improvements(baseline, new)
    
    if new_failures > 5:
        return False, f"{new_failures} regressions; too many"
    if len(improvements) <= new_failures:
        return False, "Net improvement is zero"
    
    return True, f"+{len(improvements)} improvements, -{new_failures} regressions"

Net wins. Acceptable threshold for regressions.

Step 8: Track Scores, Not Just Pass/Fail (10 min)

Pass/fail is binary; score is continuous:

v10_avg_score = avg(r["score"] for r in v10_results)
v11_avg_score = avg(r["score"] for r in v11_results)

# v1.1 may pass more but score lower (worse quality on the passing ones)

Track both. Continuous metrics catch subtle regressions.

Step 9: Track Across Time (10 min)

Persist results:

import json
from datetime import datetime

def save_eval_run(version, results):
    record = {
        "timestamp": datetime.now().isoformat(),
        "version": version,
        "results": results,
    }
    with open("eval-history.jsonl", "a") as f:
        f.write(json.dumps(record) + "\n")

Build history. Trends over time.

def plot_trend():
    history = load_history()
    for entry in history:
        print(f"{entry['timestamp']}: {entry['pass_rate']:.1%}")

If pass rate drops over 5 versions, regression.

Step 10: Alert on Regression (5 min)

CI integration:

# In CI pipeline
python eval.py --prompt v1.2 > result.json
python compare.py result.json baseline.json
# Exit 1 if regression beyond threshold

Fail the build; require explicit approval to ship a regression.

What You Just Did

Tracked prompt regression over time. Versioning + automated comparison + thresholds. The eval set keeps you honest as you iterate.

Common Failure Modes

Same prompt, different model. Compare across models too; sometimes the model is the variable.

Eval set drift. You change the eval set; old results no longer comparable.

No versioning. Can't compare; can't rollback.

Local pass, prod failure. Eval set doesn't cover real distribution.

Ignore small regressions. They compound over time.

bottom of page