Validation Strategies Advanced
Even with JSON mode and Pydantic schemas, AI output needs validation. Structural validity (it is JSON) does not guarantee semantic correctness (the values make sense). This lesson covers building robust validation pipelines for production.
Validation Layers
Validation Pipeline
# Layer 1: Syntax validation Can the output be parsed as JSON/XML? # Layer 2: Schema validation Does the structure match the expected schema? Are required fields present? Are types correct? # Layer 3: Constraint validation Are values within expected ranges? Do enums match allowed values? # Layer 4: Semantic validation Do the values make logical sense? Is "age: 500" valid JSON but semantically wrong? # Layer 5: Business rule validation Does the output satisfy domain-specific rules?
Retry with Error Feedback
Python
from pydantic import BaseModel, ValidationError import json def parse_with_retry(client, messages, model_cls, max_retries=3): """Parse structured output with validation retries.""" for attempt in range(max_retries): response = client.chat.completions.create( model="gpt-4o", response_format={"type": "json_object"}, messages=messages ) text = response.choices[0].message.content try: data = json.loads(text) result = model_cls.model_validate(data) return result except (json.JSONDecodeError, ValidationError) as e: # Feed the error back to the model messages.append({ "role": "assistant", "content": text }) messages.append({ "role": "user", "content": f"Validation error: {str(e)}. Please fix and try again." }) raise ValueError(f"Failed to get valid output after {max_retries} attempts")
Fallback Parsing
Python
import json import re def robust_json_parse(text): """Try multiple strategies to extract JSON from model output.""" # Strategy 1: Direct parse try: return json.loads(text) except json.JSONDecodeError: pass # Strategy 2: Extract from code block match = re.search(r'```(?:json)?\s*([\s\S]*?)\s*```', text) if match: try: return json.loads(match.group(1)) except json.JSONDecodeError: pass # Strategy 3: Find first { to last } start = text.find('{') end = text.rfind('}') if start != -1 and end != -1: try: return json.loads(text[start:end + 1]) except json.JSONDecodeError: pass raise ValueError("Could not extract valid JSON from response")
Graceful Degradation
Production Pattern: When validation fails, do not just throw an error. Degrade gracefully:
- Retry with error feedback (up to 3 times)
- Try fallback parsing strategies
- Use partial results with defaults for missing fields
- Fall back to a simpler model or prompt
- Queue for human review if all automated approaches fail
Monitoring Parse Failures
Python
class ParseMetrics: def __init__(self): self.total = 0 self.success_first_try = 0 self.success_with_retry = 0 self.failures = 0 self.failure_types = {} def record(self, success, attempt, error_type=None): self.total += 1 if success and attempt == 1: self.success_first_try += 1 elif success: self.success_with_retry += 1 else: self.failures += 1 if error_type: self.failure_types[error_type] = self.failure_types.get(error_type, 0) + 1 @property def success_rate(self): return (self.success_first_try + self.success_with_retry) / max(self.total, 1)
Cost Awareness: Every retry costs additional API tokens. Track retry rates and costs separately. If a particular schema consistently requires retries, improve the prompt or simplify the schema rather than relying on retries.
Lilly Tech Systems