DEV Community

Cover image for Write Evals Before You Ship Your LLM Feature, Not After the First Bug Report
Kuldeep Paul
Kuldeep Paul

Posted on

Write Evals Before You Ship Your LLM Feature, Not After the First Bug Report

Write Evals Before You Ship Your LLM Feature, Not After the First Bug Report

TL;DR

  • Teams that deploy AI features based solely on manual playground testing face unpredictable prompt regressions, silent schema drift, and unexpected provider errors in production.
  • Writing evals before shipping establishes objective release criteria, combining fast deterministic assertions with calibrated model-graded judges.
  • Real-world golden datasets can be systematically curated from production request logs captured at the gateway layer rather than hand-crafted in isolation.
  • An AI gateway provides the critical runtime control plane: logging full payloads, running shadow traffic to benchmark candidate prompts, and enforcing hard guardrails on every request.

Production AI applications running without structured evaluations experience silent regression rates that compromise downstream reliability within weeks of deployment. When engineers modify system prompts, switch model checkpoints, or alter tool schemas, traditional unit tests do not catch semantic degradations or subtle format drift. To prevent catastrophic production failures, modern engineering teams must write evals before shipping rather than waiting for customer bug reports. Bifrost, an open-source AI gateway written in Go by Maxim AI, provides the unified infrastructure required to capture production traces, mock model outputs during testing, and enforce release safety at scale.


The Cost of Vibes-Based QA in Production AI

Vibes-based quality assurance refers to the practice of testing LLM applications by manually entering five to ten sample prompts into a web playground, glancing at the completions, and deciding the implementation feels ready for deployment. This manual inspection gives engineers a false sense of security while leaving production endpoints exposed to wide distributions of unhandled edge cases.

Unlike deterministic software where a specific input path yields an identical execution trace, large language models operate probabilistically. A prompt change that appears to improve complex reasoning on three sample queries can quietly break JSON parsing on twenty other inputs. Stanford University's Center for Research on Foundation Models highlighted this multi-dimensional sensitivity in their Holistic Evaluation of Language Models (HELM) framework, proving that model performance fluctuates dramatically across accuracy, robustness, and calibration under minor prompt variations.

Manual Playground Inspection:
Prompt v1.1 ──> 5 Ad-hoc Tests ──> "Looks good" ──> Deploy ──> Production Regressions

Automated Pre-Ship Evaluation:
Prompt v1.1 ──> CI Eval Harness (200 Test Cases) ──> Assertions + Judge Scoring ──> Gate Passed ──> Deploy
Enter fullscreen mode Exit fullscreen mode

When teams lack pre-ship evaluations, debugging becomes purely reactive. Customer complaints trigger hurried prompt revisions, which inadvertently introduce regressions elsewhere in the application lifecycle. Breaking this cycle requires treating model prompts, context retrieval logic, and tool declarations with the same rigorous testing standards applied to backend application code.


What It Means to Write Evals Before You Ship

Writing evaluations before shipping means treating prompt and model behavior as an explicit engineering contract backed by verifiable test suites and automated deployment gates. An evaluation (or "eval") consists of three core components: an input prompt, an execution environment, and an evaluation rubric that produces a deterministic or quantitative score.

In their comprehensive engineering guide on demystifying evals for AI agents, Anthropic divides evaluations into two operational categories: capability evals and regression evals. Capability evals assess whether an agent or model can accomplish a novel, complex task; these often begin with lower pass rates during initial prototyping. In contrast, regression evals ensure that previously validated behaviors remain intact across continuous prompt updates, provider migrations, and application refactors.

To write evals before shipping effectively, engineering organizations must establish concrete acceptance thresholds across multiple operational dimensions before merging pull requests.

Evaluation Dimension Primary Risk Prevented Evaluation Mechanism Latency Impact in CI
Output Syntax & Schema Downstream JSON/XML parsing crashes Exact matching, Pydantic/Zod schemas < 5 milliseconds
Domain Correctness Factual errors, outdated facts, hallucinated citations Deterministic lookups, semantic embedding distance 10 to 50 milliseconds
Instruction Adherence Neglecting system constraints or formatting rules Model-graded rubrics (LLM-as-a-judge) 500 to 2,000 milliseconds
Tool Execution Safety Malformed parameters, unauthorized API calls Schema validators, static parameter assertions < 10 milliseconds
Toxicity and Policy PII leakage, policy violations, unsafe text Gateway guardrails, regex pattern scanners 5 to 20 milliseconds

By defining these metrics prior to production release, software teams shift AI testing from subjective qualitative opinions to objective statistical verification.

A multi-layered glass pyramid displaying distinct testing stages, with precise mechanical filters at the base and refine


The Three-Tier Evaluation Pyramid for LLM Applications

Effective testing strategies distribute evaluation responsibilities across a hierarchy of tests. Attempting to evaluate every single pull request using expensive model calls causes continuous integration pipelines to grind to a halt. Instead, teams should implement a three-tier evaluation pyramid composed of fast deterministic checks, model-graded evaluations, and canary shadow routing.

                  ▲
                 / \
                /   \     Tier 3: Online Shadow Runs
               /     \    Canary traffic & production metrics
              /-------\
             /         \   Tier 2: Model-Graded Judges
            /           \  Rubric scoring via LLM-as-a-judge
           /-------------\
          /               \ Tier 1: Deterministic Checks
         /                 \ Unit assertions, regex, JSON schema
        /-------------------\
Enter fullscreen mode Exit fullscreen mode

Tier 1: Fast Deterministic Assertions

Deterministic evaluations execute locally without sending requests to external frontier models. These tests verify non-negotiable structural requirements:

  • Validating that responses parse as valid JSON matching a defined schema.
  • Enforcing regex patterns for mandatory fields, identifiers, or formatting keys.
  • Checking for the strict absence of banned substrings, internal system instructions, or unredacted tokens.
  • Measuring output token length to ensure concise completions within application limits.

Because Tier 1 tests run in milliseconds at zero marginal inference cost, they run on every code commit and pull request.

Tier 2: Model-Graded Evaluations (LLM-as-a-Judge)

When testing qualitative aspects such as tone, semantic relevance, or summary completeness, code assertions alone prove insufficient. Tier 2 employs a secondary, high-capability model to evaluate the primary model's generation against a structured rubric. As surveyed in academic research on reliable LLM-as-a-judge architectures, automated judges provide scalable, reproducible scores that correlate closely with expert human judgment when rubrics are tightly constrained with binary pass-fail criteria.

Tier 3: Production Shadow Routing

Pre-ship offline evaluations cannot completely replicate the entropy of real-world end-user inputs. Tier 3 routes a duplicated slice of live production requests to candidate prompts or models asynchronously, measuring latency, schema reliability, and error rates without affecting end users.


Structuring Deterministic and Model-Graded Evaluators

Building an evaluation harness does not require complex proprietary software; it requires clean test architecture. The following implementation illustrates how to write automated test cases combining deterministic validation with an evaluation rubric executed through OpenAI-compatible APIs.

import json
import pytest
from pydantic import BaseModel, Field
from openai import OpenAI

# Connect via Bifrost gateway endpoint for routing, tracing, and caching
client = OpenAI(
    base_url="http://localhost:8080/v1",
    api_key="bifrost-virtual-key"
)

class SupportExtraction(BaseModel):
    ticket_id: str = Field(description="Alphanumeric support ticket identifier")
    priority: str = Field(description="Severity: low, medium, high, critical")
    customer_intent: str = Field(description="Summary of the issue")
    escalation_required: bool

GOLDEN_CASES = [
    {
        "input": "Urgent: Customer DB on cluster-9 went down after patch 4.2. Ticket #TCK-99182.",
        "expected_id": "TCK-99182",
        "expected_priority": "critical",
        "expected_escalate": True
    }
]

def generate_extraction(text: str) -> str:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Extract customer ticket metadata as strict JSON."},
            {"role": "user", "content": text}
        ],
        temperature=0.0
    )
    return response.choices[0].message.content

@pytest.mark.parametrize("test_case", GOLDEN_CASES)
def test_support_extraction_deterministic(test_case):
    raw_output = generate_extraction(test_case["input"])

    # Tier 1: Deterministic schema and value checks
    data = json.loads(raw_output)
    validated = SupportExtraction(**data)

    assert validated.ticket_id == test_case["expected_id"]
    assert validated.priority == test_case["expected_priority"]
    assert validated.escalation_required == test_case["expected_escalate"]

def evaluate_with_judge(user_input: str, output: str, reference: str) -> bool:
    # Tier 2: Model-graded judge with structured scoring
    judge_prompt = f"""
    Evaluate the model output based on the reference instruction.
    User Input: {user_input}
    Model Output: {output}
    Reference: {reference}

    Return JSON with two keys:
    - "reasoning": brief explanation
    - "pass": boolean (true if correct intent was captured, false otherwise)
    """
    judge_response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": judge_prompt}],
        response_format={"type": "json_object"},
        temperature=0.0
    )
    result = json.loads(judge_response.choices[0].message.content)
    return result["pass"]
Enter fullscreen mode Exit fullscreen mode

In this implementation, the gateway endpoint provides a drop-in replacement for upstream SDKs, allowing test runners to communicate seamlessly while capturing metrics across runs.


Building High-Fidelity Golden Datasets from Gateway Traffic

An evaluation suite is only as reliable as the dataset it evaluates. Synthetic test cases conceived by engineers during initial development rarely mirror the linguistic diversity, typos, prompt injections, and complex phrasing present in actual user queries.

To construct a high-fidelity golden dataset, teams should capture production traffic directly at the API boundary. By routing inference requests through an infrastructure gateway, organizations systematically log completions, token counts, and downstream user reactions.

                      ┌──────────────────────┐
                      │ Production AI Client │
                      └──────────┬───────────┘
                                 │
                                 ▼
                      ┌──────────────────────┐
                      │   Bifrost Gateway    │
                      └────┬────────────┬────┘
                           │            │
            Proxy Request  │            │ Stream Telemetry
                           ▼            ▼
             ┌─────────────────┐    ┌───────────────────────────┐
             │ Upstream Models │    │ OpenTelemetry / S3 Logs   │
             │ (Claude, OpenAI)│    │ Full Request/Response Data│
             └─────────────────┘    └─────────────┬─────────────┘
                                                  │
                                                  ▼
                                    ┌───────────────────────────┐
                                    │ Golden Dataset Repository │
                                    │ Edge Cases & Regressions  │
                                    └───────────────────────────┘
Enter fullscreen mode Exit fullscreen mode
  1. Log Real-World Requests: Configure the gateway to stream raw prompt and completion payloads to centralized storage using structured log exports or OpenTelemetry OTLP integration.
  2. Filter by Outlier Signatures: Isolate sessions characterized by high user latency, multiple retry attempts, explicit negative user feedback thumbs-down signals, or client-side JSON decoding failures.
  3. Anonymize and Sanitize: Run automated regex and data access redactions to strip Personally Identifiable Information (PII) before storing records in public or internal test repositories.
  4. Curate Benchmark Slices: Build focused golden sets of 50 to 200 diverse exemplars categorized by task (such as edge-case tool calls, adversarial prompt injections, or multilingual prompts).

Once established, every production bug reported by users should be converted directly into a new, permanent test fixture in the golden dataset, preventing that specific failure mode from ever recurring.

A network gateway node splitting a stream of data packets, routing one primary current through a brightly illuminated ma


Continuous Evaluation and Shadow Routing at the Gateway Layer

Automating test suites inside GitHub Actions or GitLab CI guarantees that known regression cases pass before merge. However, validating performance against real-world traffic patterns requires runtime evaluation techniques.

Using Bifrost, engineering teams implement dynamic routing rules to test new model iterations without risking user experience. The gateway's routing engine allows developers to declare weighted provider distributions, mirror requests to shadow deployments, or configure automatic fallbacks across more than 20 supported providers.

{
  "routing_rules": [
    {
      "name": "candidate-eval-shadow",
      "conditions": {
        "virtual_key": "prod-web-app"
      },
      "target": {
        "provider": "openai",
        "model": "gpt-4o"
      },
      "shadow": {
        "provider": "anthropic",
        "model": "claude-3-5-sonnet",
        "sample_rate": 0.15
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

In this architecture, 15% of production requests directed to the primary model are asynchronously duplicated to a candidate model. The primary caller receives the verified model's completion with zero additional latency, while the gateway records the shadow response to telemetry stores for comparative offline evaluation.

When testing simulated failure states in development, teams can also engage the gateway's mocker plugin. The mocker intercepts requests matching target rules and serves deterministic, pre-configured responses, enabling engineers to verify application resilience against timeouts, rate limits, and corrupted payloads without incurring vendor API expenses.


Preventing Silent Regressions with Gateway Guardrails and Budgets

Pre-ship evals verify expected functional quality, but production guardrails provide an indispensable defense against real-time anomalies. Even a thoroughly evaluated prompt can produce unexpected behavior when exposed to unconventional inputs.

At the gateway layer, teams define strict runtime safety parameters that halt problematic requests before they consume downstream resources or expose toxic content to end users. Bifrost integrates native content moderation and credential filtering alongside external providers like AWS Bedrock Guardrails and Azure Content Safety through its enterprise guardrails system.

Furthermore, governance policies must limit the blast radius of runaway loops or unexpected prompt token expansion. By allocating granular virtual keys to specific services, teams configure automated rate limits and token budgets. If an unoptimized prompt causes token consumption to spike during integration testing, the gateway enforces budget thresholds before runaway costs accumulate.

Beyond routing, Bifrost applies governance and security controls (virtual keys, budgets, guardrails, audit logs) centrally, and Bifrost Edge extends that same governance and security to AI traffic on employee machines, with endpoint enforcement on each device. This endpoint visibility ensures that local testing performed by engineers using CLI tools or desktop agents adheres to the identical guardrails and security configurations governing cloud infrastructure.

┌────────────────────────────────────────────────────────────────────────┐
│                        CENTRAL GOVERNANCE HUB                          │
│                                                                        │
│   Bifrost Gateway: Virtual Keys • Token Budgets • Central Guardrails   │
└───────────────────┬────────────────────────────────┬───────────────────┘
                    │                                │
                    ▼                                ▼
       ┌────────────────────────┐       ┌────────────────────────┐
       │   Cloud Application    │       │    Developer Laptop    │
       │     Infrastructure     │       │     (Bifrost Edge)     │
       │                        │       │                        │
       │ Enforced Gateway Rules │       │ Local CLI & Desktop AI │
       │ OpenTelemetry Tracing  │       │ Endpoint Guardrails    │
       └────────────────────────┘       └────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Step-by-Step CI/CD Release Gate Pipeline

To operationalize pre-ship evaluations, engineering teams integrate evaluation scripts into their standard pull request workflows. A merge must be blocked if any core regression check fails or if model-graded quality scores drop below established statistical baselines.

name: LLM Feature Evaluation Gate

on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'src/agents/**'
      - 'tests/evals/**'

jobs:
  run-evals:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements-eval.txt

      - name: Spin up Bifrost Gateway
        run: |
          docker run -d -p 8080:8080 \
            -e BIFROST_CONFIG_PATH=/config/bifrost.json \
            maximhq/bifrost:latest

      - name: Run Tier 1 Deterministic Unit Evals
        run: pytest tests/evals/test_deterministic.py -v

      - name: Run Tier 2 Golden Regression Suite
        env:
          OPENAI_API_BASE: "http://localhost:8080/v1"
          OPENAI_API_KEY: ${{ secrets.BIFROST_VIRTUAL_KEY }}
        run: |
          python scripts/run_eval_matrix.py \
            --dataset datasets/golden_v3.jsonl \
            --min-pass-rate 0.98 \
            --max-cost-delta 0.05
Enter fullscreen mode Exit fullscreen mode

Running tests against a locally orchestrated or staging gateway ensures that all requests benefit from semantic caching, drastically accelerating repeat test suite execution while preventing identical evaluation runs from consuming unnecessary API credits.


Evaluation Architecture Comparison

When designing an evaluation framework, teams must balance test execution speed, operational expense, and grading nuance across their tooling stack.

Evaluation Approach Execution Latency Cost per 100 Runs Determinism Best Applied To
Exact String Match / Regex < 1 millisecond $0.00 100% Deterministic ID validation, output formats, token redaction
Pydantic / JSON Schema 2 to 10 milliseconds $0.00 100% Deterministic Structured entity extraction, tool call signatures
Semantic Embedding Distance 20 to 80 milliseconds $0.001 High (> 99%) Factual similarity, documentation retrieval scoring
Small Model Judge (e.g., GPT-4o-mini) 400 to 900 milliseconds $0.02 Moderate (90-95%) Coherence, conversational tone, simple categorization
Frontier Model Judge (e.g., GPT-4o, Claude 3.5) 1,000 to 3,000 milliseconds $0.40 High with temperature 0.0 Complex reasoning, multi-step agent plans, coding accuracy
Human Expert Review Hours to days > $50.00 Variable Edge case calibration, novel rubric development

Combining these layers into an automated sequence provides comprehensive coverage without exhausting engineering budgets.


Frequently Asked Questions

What is the difference between an eval and a standard software unit test?

A standard unit test verifies deterministic code by executing a function and comparing the output against an exact expected result. An LLM eval tests probabilistic outputs generated by a language model, measuring properties like schema validity, factual correctness, and semantic intent using a combination of code assertions and calibrated model-graded judges.

How many test cases should be in a golden dataset before shipping?

Teams should begin with 30 to 50 curated examples covering core functional requirements and known failure modes before deploying an initial feature. As production traffic scales, this golden repository should expand to between 100 and 300 test cases, incorporating anonymized edge cases and real-world anomalies captured from gateway logs.

Why should evaluations run through an AI gateway rather than direct provider SDKs?

Routing evaluation traffic through an AI gateway like Bifrost provides automated retry logic, provider failover, request payload logging, and semantic caching. Caching identical prompt evaluations eliminates redundant upstream API expenses, while gateway logging creates an immutable audit trail comparing performance metrics across different prompt versions.

How can teams eliminate non-determinism in model-graded evaluations?

Non-determinism is minimized by setting the evaluation model's temperature parameter to 0.0, utilizing binary pass-fail rubrics rather than wide numerical scales, and forcing the judge model to output its reasoning before rendering a judgment. Running judges multiple times across controversial edge cases further stabilizes scoring distributions.

How does an evaluation pipeline detect semantic regressions that still return valid JSON?

While schema validators confirm syntactic structure, semantic regressions require Tier 2 model-graded evaluations or embedding similarity tests. A model judge evaluates whether the extracted data accurately matches the underlying facts of the source text, catching instances where valid JSON contains incorrect values or hallucinated details.

What role do guardrails play if pre-ship evals already pass?

Pre-ship evals validate expected model behavior on known test datasets, while runtime guardrails protect against unanticipated production anomalies. Gateway guardrails continuously scan incoming prompts for malicious injections and inspect outbound responses for sensitive credentials or policy violations, providing a persistent runtime safety net.


Getting Started with Pre-Ship LLM Evaluations

Deploying high-reliability AI products requires abandoning ad-hoc playground inspections in favor of structured, repeatable testing. By establishing a tiered evaluation pipeline that combines immediate schema validation with model-graded regression suites, engineering organizations detect behavioral regressions before code touches production infrastructure.

Leveraging an enterprise gateway provides the operational foundation for this workflow, enabling centralized request capture, synthetic response mocking, and strict runtime guardrails. Teams planning their infrastructure architecture can consult the LLM Gateway Buyer's Guide or examine sustained throughput metrics in published performance benchmarks.

Engineering teams evaluating production-ready AI gateways can request a Bifrost demo or inspect the codebase directly in the open-source GitHub repository.


Sources

Top comments (0)