Skip to content

Pattern Matching Evaluators

Pattern matching evaluators provide rule-based validation for structured outputs, required content, and format compliance. This includes the Contains Evaluator and Regex Evaluator for different types of pattern matching needs.

🎯 Overview

Pattern matching evaluators validate agent outputs against specific patterns, keywords, or structures. They're ideal for:

  • Structured data validation
  • Required content verification
  • Format compliance checking
  • Keyword presence testing
  • Rule-based quality assurance

📝 Contains Evaluator

The Contains Evaluator checks if the output contains required words, phrases, or concepts.

Configuration

evaluators:
  - name: 'contains'
    type: 'contains_check'
    config:
      case_sensitive: false
      match_all: true # All items must be present
      partial_match: true # Allow substring matching
    weight: 1.0
    enabled: true

Basic Usage

@agent_test(criteria=['contains'])
def test_required_keywords():
    """Test if response contains required keywords."""
    prompt = "List the benefits of renewable energy"
    response = agent(prompt)

    return {
        "input": prompt,
        "actual": response,
        "contains": ["solar", "wind", "sustainable", "environment"]
    }

Advanced Contains Patterns

Exact Phrases

@agent_test(criteria=['contains'])
def test_exact_phrases():
    """Test for exact phrase matches."""
    return {
        "input": "Explain machine learning",
        "actual": agent_response,
        "contains": [
            "machine learning",
            "artificial intelligence",
            "data patterns",
            "predictive models"
        ]
    }

Case-Sensitive Matching

@agent_test(criteria=['contains'])
def test_case_sensitive():
    """Test with case-sensitive requirements."""
    return {
        "input": "List Python libraries",
        "actual": agent_response,
        "contains": ["NumPy", "TensorFlow", "PyTorch"],  # Exact capitalization
        "contains_config": {
            "case_sensitive": True
        }
    }

Flexible Matching

@agent_test(criteria=['contains'])
def test_flexible_matching():
    """Test with flexible matching requirements."""
    return {
        "input": "Describe data science workflow",
        "actual": agent_response,
        "contains": ["collect", "clean", "analyze", "visualize"],
        "contains_config": {
            "match_all": False,    # At least one must be present
            "min_matches": 2       # Minimum number of matches required
        }
    }

Domain-Specific Keywords

@agent_test(criteria=['contains'])
def test_medical_terminology():
    """Test for domain-specific terminology."""
    medical_terms = [
        "diagnosis", "treatment", "symptoms",
        "patient", "medication", "therapy"
    ]

    return {
        "input": "Explain diabetes management",
        "actual": medical_agent_response,
        "contains": medical_terms,
        "contains_config": {
            "min_matches": 4,      # At least 4 medical terms
            "case_sensitive": False
        }
    }

🔍 Regex Evaluator

The Regex Evaluator validates outputs against regular expression patterns for structured data and format compliance.

Configuration

evaluators:
  - name: 'regex'
    type: 'regex_pattern'
    config:
      flags: ['IGNORECASE', 'MULTILINE']
      match_all: true # All patterns must match
      extract_groups: true # Extract matched groups
    weight: 1.0
    enabled: true

Basic Usage

@agent_test(criteria=['regex'])
def test_email_extraction():
    """Test if response contains valid email format."""
    return {
        "input": "Extract contact information",
        "actual": agent_response,
        "patterns": [r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"]
    }

Common Regex Patterns

Contact Information

@agent_test(criteria=['regex'])
def test_contact_patterns():
    """Test for various contact information patterns."""
    patterns = {
        "email": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
        "phone": r"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b",
        "url": r"https?://(?:[-\w.])+(?:\:[0-9]+)?(?:/(?:[\w/_.])*(?:\?(?:[\w&=%.])*)?(?:\#(?:[\w.])*)?)?",
        "zip_code": r"\b\d{5}(?:-\d{4})?\b"
    }

    return {
        "input": "Format contact details",
        "actual": agent_response,
        "patterns": list(patterns.values()),
        "pattern_names": list(patterns.keys())
    }

Structured Data

@agent_test(criteria=['regex'])
def test_json_structure():
    """Test for valid JSON structure."""
    return {
        "input": "Return data as JSON",
        "actual": agent_response,
        "patterns": [
            r'^\s*\{.*\}\s*$',                    # Valid JSON object
            r'"[^"]+"\s*:\s*"[^"]*"',             # Key-value pairs
            r'"name"\s*:\s*"[^"]*"',              # Required name field
            r'"id"\s*:\s*\d+'                     # Required numeric ID
        ]
    }

Date and Time

@agent_test(criteria=['regex'])
def test_datetime_formats():
    """Test for various date/time formats."""
    datetime_patterns = [
        r'\b\d{4}-\d{2}-\d{2}\b',                      # YYYY-MM-DD
        r'\b\d{2}/\d{2}/\d{4}\b',                      # MM/DD/YYYY
        r'\b\d{1,2}:\d{2}(?::\d{2})?\s*(?:AM|PM)\b',   # Time with AM/PM
        r'\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2},?\s+\d{4}\b'  # Month DD, YYYY
    ]

    return {
        "input": "Include current date and time",
        "actual": agent_response,
        "patterns": datetime_patterns,
        "regex_config": {
            "match_all": False,    # Any date format is acceptable
            "min_matches": 1
        }
    }

Code Validation

@agent_test(criteria=['regex'])
def test_python_code():
    """Test for valid Python code patterns."""
    python_patterns = [
        r'def\s+\w+\s*\([^)]*\)\s*:',          # Function definition
        r'import\s+\w+|from\s+\w+\s+import',   # Import statements
        r'if\s+.*:\s*$',                       # If statements
        r'for\s+\w+\s+in\s+.*:\s*$',          # For loops
        r'return\s+.*$'                        # Return statements
    ]

    return {
        "input": "Write a Python function",
        "actual": agent_code,
        "patterns": python_patterns,
        "regex_config": {
            "flags": ["MULTILINE"],
            "min_matches": 2  # At least function def + one other construct
        }
    }

💡 Advanced Usage

Combined Pattern Validation

@agent_test(criteria=['contains', 'regex'])
def test_comprehensive_validation():
    """Combine contains and regex for thorough validation."""
    return {
        "input": "Create a user profile with contact info",
        "actual": agent_response,

        # Contains validation
        "contains": ["name", "email", "phone", "address"],

        # Regex validation
        "patterns": [
            r'"name"\s*:\s*"[^"]+\"',              # Name field
            r'"email"\s*:\s*"[^@]+@[^"]+\"',       # Email field
            r'"phone"\s*:\s*"\d{3}-\d{3}-\d{4}"'   # Phone field
        ]
    }

Dynamic Pattern Generation

@agent_test(criteria=['regex'])
def test_dynamic_patterns():
    """Generate patterns based on context."""

    def generate_patterns(domain):
        if domain == "finance":
            return [
                r'\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?',  # Currency
                r'\b\d{4}-\d{4}-\d{4}-\d{4}\b',       # Credit card
                r'\b\d{3}-\d{2}-\d{4}\b'              # SSN
            ]
        elif domain == "medical":
            return [
                r'\b\d{3}-\d{2}-\d{4}\b',             # Patient ID
                r'\b(?:mg|ml|g|kg|lb)\b',             # Medical units
                r'\b\d+/\d+\s*mmHg\b'                 # Blood pressure
            ]
        return []

    domain = "finance"
    patterns = generate_patterns(domain)

    return {
        "input": f"Generate {domain} report",
        "actual": agent_response,
        "patterns": patterns
    }

Multi-Level Validation

@agent_test(criteria=['regex'])
def test_multi_level_validation():
    """Validate at multiple structural levels."""

    validation_levels = {
        "document_structure": [
            r'^# .+$',                    # Title (H1)
            r'^## .+$',                   # Section headers (H2)
            r'^\d+\. .+$'                 # Numbered lists
        ],
        "content_quality": [
            r'\b\w{10,}\b',              # Complex words (10+ chars)
            r'[.!?]\s+[A-Z]',            # Proper sentence structure
            r'\b(?:however|therefore|furthermore|moreover)\b'  # Transition words
        ],
        "formatting": [
            r'\*\*[^*]+\*\*',            # Bold text
            r'`[^`]+`',                  # Code snippets
            r'\[[^\]]+\]\([^)]+\)'       # Markdown links
        ]
    }

    return {
        "input": "Write a technical blog post",
        "actual": agent_response,
        "patterns": [pattern for patterns in validation_levels.values() for pattern in patterns],
        "pattern_groups": validation_levels
    }

📊 Configuration Options

Contains Evaluator Options

Option Type Default Description
case_sensitive Boolean false Case-sensitive matching
match_all Boolean true All items must be present
partial_match Boolean true Allow substring matching
min_matches Integer null Minimum number of matches
exact_match Boolean false Require exact word boundaries

Regex Evaluator Options

Option Type Default Description
flags List [] Regex flags (IGNORECASE, etc.)
match_all Boolean true All patterns must match
min_matches Integer null Minimum pattern matches
extract_groups Boolean false Extract matched groups
multiline Boolean false Enable multiline mode

Regex Flags

# Available regex flags
regex_flags = [
    "IGNORECASE",    # Case-insensitive matching
    "MULTILINE",     # ^ and $ match line boundaries
    "DOTALL",        # . matches newlines
    "VERBOSE",       # Allow comments in patterns
    "ASCII",         # ASCII-only matching
    "LOCALE"         # Locale-dependent matching
]

🔍 Result Format

Contains Evaluator Results

{
    "passed": True,
    "score": 0.75,  # Percentage of required items found
    "threshold": 1.0,
    "details": {
        "required_items": ["solar", "wind", "sustainable", "environment"],
        "found_items": ["solar", "wind", "sustainable"],
        "missing_items": ["environment"],
        "match_count": 3,
        "total_required": 4,
        "case_sensitive": False
    }
}

Regex Evaluator Results

{
    "passed": True,
    "score": 1.0,
    "threshold": 1.0,
    "details": {
        "patterns": [
            r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
        ],
        "matches": [
            {
                "pattern": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
                "found": True,
                "match_text": "user@example.com",
                "position": [45, 61],
                "groups": []
            }
        ],
        "total_patterns": 1,
        "successful_matches": 1
    }
}

📋 Best Practices

Pattern Design

Contains Patterns

# Good: Specific, relevant keywords
good_contains = ["machine learning", "neural networks", "deep learning"]

# Avoid: Too generic or common words
avoid_contains = ["the", "and", "is", "very"]

# Better: Domain-specific terms
domain_contains = ["convolutional", "backpropagation", "gradient descent"]

Regex Patterns

# Good: Specific, well-escaped patterns
good_regex = r'\b\d{3}-\d{2}-\d{4}\b'  # SSN format

# Avoid: Overly broad patterns
avoid_regex = r'.*'  # Matches everything

# Better: Structured with groups
better_regex = r'\b(\d{3})-(\d{2})-(\d{4})\b'  # Capture groups

Performance Optimization

@agent_test(criteria=['regex'])
def test_optimized_patterns():
    """Optimize patterns for performance."""

    # Compile patterns once (done automatically by evaluator)
    optimized_patterns = [
        r'^[A-Z][a-z]+$',        # Simple character class
        r'\b\d+\b',              # Word boundaries for precision
        r'(?:cat|dog|bird)'      # Non-capturing groups
    ]

    return {
        "input": prompt,
        "actual": response,
        "patterns": optimized_patterns,
        "regex_config": {
            "flags": ["IGNORECASE"],  # Use flags efficiently
            "match_all": False        # Early exit when possible
        }
    }

Error Handling

@agent_test(criteria=['regex'])
def test_with_fallback():
    """Handle pattern matching errors gracefully."""

    primary_patterns = [r'\b\d{4}-\d{2}-\d{2}\b']  # ISO date
    fallback_patterns = [r'\b\d{1,2}/\d{1,2}/\d{4}\b']  # US date

    return {
        "input": "Include today's date",
        "actual": agent_response,
        "patterns": primary_patterns,
        "fallback_patterns": fallback_patterns,
        "regex_config": {
            "use_fallback": True
        }
    }

🚨 Troubleshooting

Common Issues

No Matches Found

# Debug with verbose output
@agent_test(criteria=['contains'])
def debug_contains():
    return {
        "input": prompt,
        "actual": response,
        "contains": ["keyword"],
        "contains_config": {
            "debug": True,           # Show detailed matching info
            "case_sensitive": False  # Check case sensitivity
        }
    }

Regex Pattern Errors

# Test patterns separately
import re

def test_pattern(pattern, text):
    try:
        matches = re.findall(pattern, text)
        print(f"Pattern: {pattern}")
        print(f"Matches: {matches}")
        return len(matches) > 0
    except re.error as e:
        print(f"Regex error: {e}")
        return False

Performance Issues

# Optimize for large texts
@agent_test(criteria=['regex'])
def test_performance_optimized():
    return {
        "input": prompt,
        "actual": large_response,
        "patterns": [r'\b\w+@\w+\.\w+\b'],  # Simple email pattern
        "regex_config": {
            "max_length": 10000,    # Limit text length
            "timeout": 5.0          # Pattern matching timeout
        }
    }

📚 Examples

See Basic Examples for more pattern matching usage patterns.