AI Evals & TestingPublished Jul 2026•9 min read

Regression Testing for Non-Deterministic AI: Golden Datasets, Semantic Assertions, and Drift Detection

How to build Pytest regression suites when LLM outputs vary across every execution.

Md Mehedi Hasan
Md Mehedi Hasan
Senior AI/LLM Engineer · 5+ Yrs Exp
𝕏in
Silicon wafer circuitry representing precision engineering and regression testing

The Testing Paradox of Non-Deterministic Code

In traditional backend engineering, testing is straightforward:

python
def test_calculate_tax():
    assert calculate_tax(100.0) == 15.0

If you apply this paradigm to an LLM:

python
def test_summarize_policy():
    # FAILS: The wording changes on every invocation even with temperature=0
    assert summarize_policy(doc) == "The return window is 30 days."

Because foundation models sample from token probability distributions, exact match assertions trigger constant false alarms, causing teams to abandon testing altogether.


The Three-Tier Assertion Hierarchy

To build resilient regression suites, we separate assertions into three distinct levels:

text
1. Deterministic Structural Assertions (Fast, 0 Cost)
   ├── Is valid JSON?
   ├── Does it conform to Pydantic schema?
   └── Are required keys present?

2. Semantic Embedding Assertions (Fast, Low Cost)
   ├── Cosine similarity to Golden Answer >= 0.88
   └── Entity overlap (Jaccard similarity of extracted entities)

3. LLM-as-a-Judge Rubric Assertions (Deep, Medium Cost)
   ├── Does the answer satisfy all 4 criteria in the grading rubric?
   └── Does it contain any hallucinated assertions?

Building Semantic Pytest Fixtures

Here is our production Pytest test structure:

python
import pytest
from sentence_transformers import SentenceTransformer
import numpy as np

embedding_model = SentenceTransformer("all-MiniLM-L6-v2")

def compute_cosine_similarity(text_a: str, text_b: str) -> float:
    emb_a = embedding_model.encode(text_a)
    emb_b = embedding_model.encode(text_b)
    return float(np.dot(emb_a, emb_b) / (np.linalg.norm(emb_a) * np.linalg.norm(emb_b)))

@pytest.mark.asyncio
async def test_agent_refund_policy_reasoning():
    user_query = "Can I return a opened laptop after 25 days?"
    
    # Execute agent
    response = await run_support_agent(user_query)
    
    # Tier 1: Structural check
    assert response.status == "success"
    assert response.resolution in ["approved", "rejected", "escalate"]
    
    # Tier 2: Semantic check against Golden reference
    golden_answer = "Yes, items can be returned within 30 days, but open boxes incur a 15% restocking fee."
    similarity = compute_cosine_similarity(response.message, golden_answer)
    assert similarity >= 0.85, f"Semantic drift detected. Score: {similarity}"
    
    # Tier 3: Critical constraint check (Regex/Deterministic)
    assert "restocking fee" in response.message.lower()

Monitoring Model Checkpoint Drift

When LLM providers quietly update underlying model weights (e.g., pointing gpt-4o or claude-3-5-sonnet to a new internal checkpoint), prompt outputs can drift without any code changes on your end.

Running this suite nightly on scheduled cron builds detects behavioral regressions before customers experience degraded service.

Tags:#Regression Testing#Golden Datasets#Pytest#Semantic Diff#Model Drift

Enjoyed this engineering playbook?

Explore our other in-depth articles on production AI.

View All 10 Playbooks →
View All →