AI Engineering๐Ÿค– LLMsReliability & Evaluation
๐Ÿ›ก๏ธ
Running AI agents in production? Harness governs spend, access, and audit trailsโ€”so your team maintains control while agents safely handle production workflows. Visit โ†’

LLM Reliability, Structured Outputs & Evaluation

Deploying LLMs in production requires transforming non-deterministic text generation into reliable, predictable application components. Software engineers must enforce strict output formatting at the model layer and deploy robust, programmatic evaluation suites to detect model drift and regression.


๐Ÿ›ก๏ธ Structured Outputs & Schema Enforcement

Raw LLM text outputs frequently violate structure requirements (e.g., missing brackets in JSON, hallucinated keys, or conversational filler like โ€œHere is your JSON:โ€). AI engineers use two primary strategies to enforce structure:

1. Guided Logit Generation

Instead of parsing text post-generation, guided logit generation enforces structure during the decoding process. Tools like Outlines or PydanticAI construct a Finite State Machine (FSM) or Context-Free Grammar (CFG) from a Pydantic schema or regex.

At each token-generation step $t$, the FSM identifies which vocabulary tokens are syntactically valid next states. The logits of invalid tokens are set to $-\infty$, ensuring the model cannot select them.

  • Pros: Guarantees 100% syntactically valid JSON/regex outputs.
  • Cons: Requires hosting control over the inference engine (e.g., running vLLM) to modify logit-bias samplers.

2. Native Tool & Function Calling

For serverless APIs (OpenAI, Anthropic), logit-level access is hidden. Instead, developers pass a JSON Schema under the tools or response_format API parameters. The providerโ€™s gateway uses internal logit-masking and specialized fine-tuning to force the model to output schema-compliant text.


๐Ÿ’ป Schema Validation & Self-Correction Loop

When working with APIs that do not support logit-level constraints, formatting failures can still happen. The Python script below shows how to enforce structured outputs using Pydantic validation, catch parsing exceptions, and feed compiler error traces back to the model in an automated retry loop.

import json
import logging
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field, ValidationError
import openai
 
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("SelfCorrectionLoop")
 
# 1. Define the target structured output schema
class APIEndpointSpec(BaseModel):
    endpoint_path: str = Field(description="The REST endpoint path, starting with a slash.")
    http_method: str = Field(description="Must be GET, POST, PUT, or DELETE.")
    required_parameters: List[str] = Field(default=[], description="List of mandatory query parameter names.")
    response_fields: List[str] = Field(description="Fields included in the returned JSON payload.")
 
class SystemSpecCollection(BaseModel):
    endpoints: List[APIEndpointSpec]
 
# 2. Implement the validator and self-correction router
class ReliableLLMParser:
    def __init__(self, openai_client: openai.OpenAI):
        self.client = openai_client
 
    def generate_specs(self, prompt: str, max_retries: int = 2) -> Optional[SystemSpecCollection]:
        system_prompt = (
            "You are a backend API architect. You must describe the endpoints "
            "as a JSON object matching this schema:\n"
            f"{json.dumps(SystemSpecCollection.model_json_schema(), indent=2)}\n"
            "Output ONLY valid raw JSON. Do not include markdown code block backticks."
        )
 
        current_prompt = prompt
        previous_output = ""
 
        for attempt in range(max_retries + 1):
            try:
                logger.info(f"Generating schema - Attempt {attempt + 1}")
                
                # Append error context to the user prompt if this is a retry
                if attempt > 0:
                    user_payload = (
                        f"Your previous output failed validation. Schema parsing threw this error:\n"
                        f"{error_message}\n"
                        f"Previous Output:\n{previous_output}\n"
                        "Please correct the errors and output the complete JSON object."
                    )
                else:
                    user_payload = current_prompt
 
                response = self.client.chat.completions.create(
                    model="gpt-4o-mini",
                    messages=[
                        {"role": "system", "content": system_prompt},
                        {"role": "user", "content": user_payload}
                    ],
                    temperature=0.0  # Force deterministic generation
                )
                
                raw_text = response.choices[0].message.content.strip()
                previous_output = raw_text
 
                # Attempt parsing and schema validation
                parsed_json = json.loads(raw_text)
                spec_collection = SystemSpecCollection.model_validate(parsed_json)
                logger.info("Successfully generated and validated schema.")
                return spec_collection
 
            except (json.JSONDecodeError, ValidationError) as e:
                error_message = str(e)
                logger.warning(f"Attempt {attempt + 1} failed: {error_message}")
                if attempt == max_retries:
                    logger.error("Max retries reached. Output validation failed.")
                    raise RuntimeError(f"Unable to generate valid schema: {error_message}")
 
# Example Usage:
# client = openai.OpenAI(api_key="your-api-key")
# parser = ReliableLLMParser(openai_client=client)
# specs = parser.generate_specs("Build endpoints for a book catalog GET books and POST book purchase.")

๐Ÿ“Š LLM Evaluation Methodologies

Evaluating non-deterministic text outputs cannot be done with simple unit tests. Instead, AI engineers use evaluation frameworks to run tests at scale.

1. LLM-as-a-Judge

This pattern uses a larger model (e.g., Claude 3.5 Sonnet or GPT-4o) to grade outputs generated by smaller production models.

  • Single-Answer Grading: The judge reviews a single output against a rubric (e.g., grading helpfulness on a scale from 1 to 5) and provides a score.
  • Pairwise Elo Rating: The judge is shown a prompt and two anonymized outputs (Model A and Model B). The judge decides which is better, updates the modelsโ€™ relative Elo ratings, and logs the decision. This mimics human preference evaluation (RLHF).

2. G-Eval (Criteria-Based Evaluation)

G-Eval is a framework that uses Chain-of-Thought (CoT) prompts to guide the judge model. Instead of asking for a raw score, the evaluation process runs in three phases:

  • Phase 1: Criteria Selection: Define the target metric (e.g., Coherence, Relevance).
  • Phase 2: Step-by-Step Prompting: Instruct the judge to generate its own evaluation steps (e.g., โ€œStep 1: Check if the answer addresses all parts of the questionโ€ฆโ€).
  • Phase 3: Weighted Token Probabilities: The judge outputs a numerical score. To mitigate token sampling bias, you can extract the log probabilities of the score tokens to calculate a weighted average:

\[\text{G-Eval Score} = \sum_{s=1}^{S} s \cdot P(\text{score} = s)\]

Where $s$ represents the individual score value and $P(\text{score} = s)$ is the probability of the model outputting that score token.

3. Reference-Free vs. Reference-Based Metrics

                     [Evaluation Pipeline]
                               โ”‚
             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
             โ–ผ                                   โ–ผ
     [Reference-Based]                   [Reference-Free]
     - Evaluates against gold standard   - Evaluates system outputs directly
     - Metrics: ROUGE, BLEU, BERTScore   - Metrics: Faithfulness, Relevance
  • Reference-Based Evaluation: Matches outputs against a curated โ€œgold standardโ€ ground truth. Commonly uses overlap metrics like ROUGE-L (longest common subsequence), BLEU (n-gram precision), or BERTScore (embedding similarity). These are useful for extraction or translation tasks, but struggle to grade creative generation.
  • Reference-Free Evaluation: Grades outputs without using ground-truth answers. It evaluates the output against the input context. For example, in RAG systems, it evaluates Faithfulness (checking if the output is supported only by the retrieved context) and Answer Relevance (checking if the output directly answers the userโ€™s question).

โš–๏ธ Mitigating Judge Bias

Using LLMs as judges introduces systematic biases that must be mitigated:

  1. Position Bias: Judges tend to favor the first option shown in pairwise evaluations.
    • Mitigation: Run the evaluation twice, swapping the order of candidates (A/B and B/A), and average the scores.
  2. Verbosity Bias: Judges favor longer, more detailed responses, even if they contain irrelevant information.
    • Mitigation: Enforce strict character/word count boundaries in prompt templates, or instruct the judge to penalize fluff.
  3. Self-Favoring Bias: Models tend to award higher grades to outputs generated by models from their own family (e.g., GPT-4 judging GPT-3.5 outputs).
    • Mitigation: Use a different model family for evaluation than the model being evaluated (e.g., use Claude 3.5 Sonnet to judge OpenAI model outputs).

๐Ÿš€ 10K+ page views in last 7 days
Developer Handbook 2026 ยฉ Exemplar.