Lesson 15 of 20

Error Handling & Recovery

Output Validation

3 min read

Never trust LLM outputs blindly. Validation ensures agent responses are safe, accurate, and properly formatted before reaching users.

Validation Layers

LLM Response → Format Check → Content Safety → Factual Verify → Business Rules → User

Schema Validation

Ensure outputs match expected structure:

from pydantic import BaseModel, validator
from typing import List, Optional

class AgentResponse(BaseModel):
    answer: str
    confidence: float
    sources: List[str]
    tool_calls: Optional[List[dict]] = None

    @validator('confidence')
    def confidence_range(cls, v):
        if not 0 <= v <= 1:
            raise ValueError('Confidence must be between 0 and 1')
        return v

    @validator('answer')
    def answer_not_empty(cls, v):
        if not v.strip():
            raise ValueError('Answer cannot be empty')
        return v

def validate_response(raw_output):
    try:
        return AgentResponse.parse_raw(raw_output)
    except ValidationError as e:
        return {"error": "Invalid response format", "details": str(e)}

Content Safety Checks

class ContentValidator:
    def __init__(self):
        self.blocked_patterns = [
            r'\b(password|secret|api[_-]?key)\s*[:=]\s*\S+',
            r'\b\d{16}\b',  # Credit card patterns
            r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'  # Emails
        ]

    def check_pii_leakage(self, text):
        """Check for accidentally exposed sensitive data"""
        for pattern in self.blocked_patterns:
            if re.search(pattern, text, re.IGNORECASE):
                return False, "Potential sensitive data detected"
        return True, None

    def check_harmful_content(self, text):
        """Use moderation API or classifier"""
        result = moderation_api.check(text)
        if result.flagged:
            return False, f"Content flagged: {result.categories}"
        return True, None

Factual Consistency

Verify claims against known facts:

class FactChecker:
    def __init__(self, knowledge_base):
        self.kb = knowledge_base

    def verify_claims(self, response, context):
        """Cross-reference response claims with source context"""
        claims = extract_claims(response)
        verified = []

        for claim in claims:
            # Check if claim is supported by context
            support = self.kb.find_support(claim, context)

            if support.score > 0.8:
                verified.append({"claim": claim, "status": "verified"})
            elif support.score > 0.5:
                verified.append({"claim": claim, "status": "partial"})
            else:
                verified.append({"claim": claim, "status": "unverified"})

        return verified

    def add_verification_note(self, response, verification):
        """Annotate response with verification status"""
        unverified = [v for v in verification if v["status"] == "unverified"]

        if unverified:
            note = f"\n\n⚠️ {len(unverified)} claim(s) could not be verified against sources."
            return response + note

        return response

Business Logic Validation

class BusinessValidator:
    def __init__(self, rules):
        self.rules = rules

    def validate(self, response, context):
        errors = []

        for rule in self.rules:
            if not rule.check(response, context):
                errors.append(rule.error_message)

        return len(errors) == 0, errors

# Example rules
rules = [
    Rule(
        name="price_sanity",
        check=lambda r, c: not contains_price(r) or price_in_range(r, 0, 10000),
        error_message="Price out of acceptable range"
    ),
    Rule(
        name="date_validity",
        check=lambda r, c: not contains_date(r) or date_is_valid(r),
        error_message="Invalid date in response"
    ),
    Rule(
        name="no_competitors",
        check=lambda r, c: not mentions_competitors(r),
        error_message="Response mentions competitor products"
    )
]

Validation Pipeline

Combine all validators:

class ValidationPipeline:
    def __init__(self):
        self.validators = [
            ("schema", SchemaValidator()),
            ("safety", ContentValidator()),
            ("facts", FactChecker(knowledge_base)),
            ("business", BusinessValidator(business_rules))
        ]

    def validate(self, response, context):
        results = {"passed": True, "checks": []}

        for name, validator in self.validators:
            is_valid, details = validator.validate(response, context)

            results["checks"].append({
                "name": name,
                "passed": is_valid,
                "details": details
            })

            if not is_valid:
                results["passed"] = False

        return results

    def process(self, response, context):
        validation = self.validate(response, context)

        if not validation["passed"]:
            # Option 1: Regenerate
            # Option 2: Return sanitized version
            # Option 3: Return with warnings
            return self.handle_failure(response, validation)

        return response

Best Practices

Validate Why
Output format Prevent parsing errors
Content safety Protect users
Factual accuracy Build trust
Business rules Maintain compliance

Next: Learn debugging strategies for agent systems. :::

Quiz

Module 4: Error Handling & Recovery

Take Quiz