Stage 7 · Master
Running AI Reliably (LLMOps)
Evals & Regression Testing
Golden datasets, LLM-as-judge, and catching quality regressions in CI.
Why Evals Matter
Traditional software has unit tests. AI systems have evals. An eval is a test that measures whether the model produces correct, useful output for a given input. Without evals, every prompt change is a shot in the dark.
Types of Evals
| Eval Type | What It Measures | How |
|---|---|---|
| Accuracy | Is the answer correct? | Compare to expected output |
| Groundedness | Is the answer in the context? | LLM-as-judge |
| Relevance | Does the answer address the query? | LLM-as-judge |
| Safety | Does the answer follow safety rules? | Rule-based + LLM |
| Latency | How fast is the response? | Timing measurement |
Building Golden Datasets
golden_dataset = [
{
"id": "alert-triage-001",
"input": {
"alert_name": "HighCPU",
"service": "checkout",
"description": "CPU usage 95% on checkout pods for 15 minutes"
},
"expected": {
"severity": "P2",
"confidence_min": 0.7,
"must_contain": ["checkout", "CPU"],
"must_not_contain": ["P1", "data loss"]
},
"category": "alert-triage"
},
{
"id": "runbook-001",
"input": {
"query": "How do I failover Redis?"
},
"expected": {
"must_contain": ["failover", "redis-cli"],
"must_cite_source": true,
"source_must_be": "runbooks/redis-failover.md"
},
"category": "runbook-lookup"
},
]Golden datasets have inputs, expected outputs, and specific constraints. They are your regression test suite for AI behavior.
LLM-as-Judge
def llm_judge(query, answer, context, criteria):
"""Use a stronger model to evaluate the answer quality."""
response = openai.ChatCompletion.create(
model="gpt-4",
temperature=0.0,
messages=[
{
"role": "system",
"content": f"""You are an evaluation judge. Rate this answer on criteria: {criteria}.
Score 1-5:
1 = Completely wrong or irrelevant
2 = Partially correct but major issues
3 = Acceptable with minor issues
4 = Good, meets expectations
5 = Excellent, exceeds expectations
Return JSON: {{"score": int, "reasoning": "string", "issues": ["string"]}}"""
},
{
"role": "user",
"content": f"Query: {query}\n\nContext: {context}\n\nAnswer: {answer}"
}
]
)
return json.loads(response["choices"][0]["message"]["content"])LLM-as-judge is useful for subjective criteria like helpfulness and groundedness that cannot be checked with exact match.
CI Integration
import pytest
@pytest.mark.parametrize("eval_case", golden_dataset, ids=lambda e: e["id"])
def test_ai_quality(eval_case, rag_pipeline):
"""Run golden dataset evals in CI."""
result = rag_pipeline.answer(eval_case["input"])
# Exact checks
expected = eval_case["expected"]
if "severity" in expected:
assert result["severity"] == expected["severity"]
if "must_contain" in expected:
for term in expected["must_contain"]:
assert term.lower() in result["answer"].lower()
if "must_not_contain" in expected:
for term in expected["must_not_contain"]:
assert term.lower() not in result["answer"].lower()
# LLM-as-judge for subjective criteria
if "groundedness_min" in expected:
score = llm_judge(
eval_case["input"], result["answer"],
result["context"], "groundedness"
)
assert score["score"] >= expected["groundedness_min"]Run evals in CI on every prompt change. If scores drop, the change is blocked.
Evals can miss edge cases and LLM-as-judge can be wrong. Supplement automated evals with periodic human review of random samples.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.