top of page

Tutorial 6: Monitor for Prompt Drift

  • Contributor
  • 4 days ago
  • 2 min read

You didn't change anything. Production quality dropped anyway. The model changed underneath, or the inputs changed. Detect both.

Step 1: Identify Drift Sources (5 min)

Things that drift even when your code doesn't:

  • Model updates: vendor pushes a new version

  • Input distribution: users ask different questions

  • External data: RAG corpus changes

  • Token costs: vendor pricing change

  • Latency: vendor infrastructure changes

Each requires different detection.

Step 2: Pin Model Versions (5 min)

# Bad
client.messages.create(model="claude-sonnet")

# Good
client.messages.create(model="claude-sonnet-4-6")

Pin to specific versions. When upgrading, opt in deliberately and run eval.

Step 3: Track Output Distribution (15 min)

Compute statistics on production outputs:

def track_output_stats(outputs):
    return {
        "avg_length": mean(len(o) for o in outputs),
        "length_p50": median(len(o) for o in outputs),
        "length_p99": percentile(99, [len(o) for o in outputs]),
        "format_consistency": pct_match_schema(outputs),
        "common_phrases": top_ngrams(outputs, n=3, k=20),
    }

Per day. If avg length suddenly jumps 30%, something changed.

Step 4: Track Input Distribution (10 min)

def track_input_stats(inputs):
    return {
        "topic_distribution": classify_topics(inputs),
        "avg_length": mean(len(i) for i in inputs),
        "language_distribution": detect_languages(inputs),
    }

If suddenly 30% of inputs are in Spanish but your prompt is English-only, your eval set may be wrong now.

Step 5: Periodic Eval Replay (15 min)

Daily or weekly, run your eval set against production prompts:

def daily_eval_check():
    eval_set = load_eval_set()
    current_prompt = get_production_prompt()
    
    results = run_eval(eval_set, current_prompt)
    
    historical = load_history()
    latest_baseline = historical[-7:]  # last week's runs
    avg_baseline = mean(r["pass_rate"] for r in latest_baseline)
    
    if results["pass_rate"] < avg_baseline - 0.05:
        alert("Pass rate dropped 5% vs. weekly baseline")

Same inputs, same prompt, same model — if results drift, something external moved.

Step 6: Anomaly Detection on Metrics (10 min)

def detect_anomaly(metric_values):
    mean_val = mean(metric_values[:-1])
    std_val = std(metric_values[:-1])
    latest = metric_values[-1]
    
    z_score = (latest - mean_val) / std_val
    return abs(z_score) > 3  # 3 sigma

Alert on:

  • Pass rate

  • Avg score

  • Avg output length

  • Avg latency

  • Cost per request

Step 7: Sample Production Outputs for Review (15 min)

Sample randomly:

def daily_sample(n=20):
    today_outputs = load_today_outputs()
    sample = random.sample(today_outputs, n)
    save_for_review(sample)

A human spot-checks. Catches qualitative drift that metrics miss.

Step 8: Track User Feedback Trends (10 min)

def feedback_trend(days=30):
    feedback = load_feedback(days=days)
    daily_rates = group_by_day(feedback)
    
    for day, rate in daily_rates.items():
        print(f"{day}: {rate['positive']/rate['total']:.1%}")

If thumbs-up drops 5% week-over-week, drift.

Step 9: Alerting Rules (10 min)

alerts = {
    "pass_rate_drop": {
        "metric": "daily_eval_pass_rate",
        "condition": "< baseline - 0.05",
        "severity": "high",
    },
    "latency_spike": {
        "metric": "p99_latency",
        "condition": "> baseline * 1.5",
        "severity": "medium",
    },
    "cost_spike": {
        "metric": "daily_cost",
        "condition": "> baseline * 1.3",
        "severity": "medium",
    },
}

Document; route to right team; rehearse responses.

Step 10: Response Playbook (5 min)

When drift detected:

  1. Confirm (re-run eval; check for noise)

  2. Identify cause:

    • Model version changed?

    • Input distribution shift?

    • Vendor incident?

  3. Mitigate:

    • Roll back to known-good prompt

    • Pin to older model

    • Patch prompt for new input distribution

  4. Post-mortem

  5. Update detection if you missed signal

What You Just Did

Production drift detection. Eval replay + distribution tracking + anomaly alerts. The model doesn't ask permission to change; you catch it.

Common Failure Modes

No baseline. Can't detect "different" without a reference.

Floating model. No version pin; vendor updates ship to you silently.

Eval set rot. Eval set no longer represents prod traffic.

Alert fatigue. Too many alerts; signal lost.

Reactive only. Catch drift at +20% regression; should've caught at +5%.

bottom of page