Skip to content

Custom Evaluation Frameworks: Domain-Specific Assessment

Overview

Generic benchmarks don't capture what matters for YOUR use case. Custom evaluation frameworks measure what actually counts.


Building Domain-Specific Evaluation

Step 1: Define Success Criteria

class DomainEvaluationFramework:
    """Template for custom evaluation"""

    def __init__(self, domain: str):
        self.domain = domain

        # Define success for THIS domain
        self.success_criteria = self.define_success()

    def define_success(self) -> dict:
        """Domain-specific definition of 'correct'"""

        if self.domain == 'customer_support':
            return {
                'customer_satisfied': True,
                'issue_resolved': True,
                'response_time': '< 2 minutes',
                'tone_appropriate': True,
                'no_escalation_needed': True
            }

        elif self.domain == 'code_generation':
            return {
                'code_runs': True,
                'passes_tests': True,
                'follows_style': True,
                'well_documented': True,
                'no_security_issues': True
            }

        elif self.domain == 'medical_diagnosis':
            return {
                'diagnosis_accurate': True,
                'reasoning_sound': True,
                'no_harmful_advice': True,
                'appropriate_escalation': True,
                'follows_guidelines': True
            }

Step 2: Construct Test Sets

class TestSetConstruction:
    """Build evaluation data"""

    def build_comprehensive_set(self, domain: str):
        """Balanced mix of cases"""

        test_cases = {
            # Easy cases (should always work)
            'easy': self.generate_easy_cases(domain, count=100),

            # Medium cases (require some reasoning)
            'medium': self.generate_medium_cases(domain, count=100),

            # Hard cases (rare, important)
            'hard': self.generate_hard_cases(domain, count=50),

            # Edge cases (corner scenarios)
            'edge': self.generate_edge_cases(domain, count=50),

            # Adversarial (users trying to break it)
            'adversarial': self.generate_adversarial(domain, count=50)
        }

        return test_cases

    def generate_hard_cases(self, domain: str, count: int):
        """Cases with multiple correct paths"""

        if domain == 'customer_support':
            return [
                {
                    'input': 'Billing issue with conflicting charges',
                    'correct_paths': [
                        'Refund + apology',
                        'Credit for future use',
                        'Partial refund + discount'
                    ],
                    'wrong_paths': [
                        'Deny issue exists',
                        'Blame customer',
                        'Demand payment'
                    ]
                }
            ]

    def generate_edge_cases(self, domain: str, count: int):
        """Rare but important scenarios"""

        if domain == 'code_generation':
            return [
                {
                    'input': 'Generate thread-safe counter',
                    'requirements': ['Thread-safe', 'Lock-free if possible'],
                    'common_mistakes': [
                        'Race condition',
                        'Deadlock',
                        'Performance issue'
                    ]
                }
            ]

Step 3: Evaluation Harness

class EvaluationHarness:
    """Run tests and collect metrics"""

    def evaluate(self, agent, test_set, domain: str):
        """Comprehensive evaluation"""

        results = {
            'by_difficulty': {},
            'by_category': {},
            'failures': [],
            'metrics': {}
        }

        for difficulty, test_cases in test_set.items():
            difficulty_results = []

            for test_case in test_cases:
                try:
                    # Run the test
                    output = agent.run(test_case['input'])

                    # Evaluate output
                    score = self.grade_output(
                        output,
                        test_case,
                        domain
                    )

                    difficulty_results.append(score)

                except Exception as e:
                    results['failures'].append({
                        'test': test_case['input'],
                        'error': str(e),
                        'difficulty': difficulty
                    })

            results['by_difficulty'][difficulty] = {
                'accuracy': sum(difficulty_results) / len(difficulty_results),
                'count': len(difficulty_results)
            }

        return results

    def grade_output(self, output, test_case, domain: str) -> float:
        """Domain-specific grading"""

        if domain == 'customer_support':
            score = 0

            # Check resolution quality
            if self.resolves_issue(output, test_case):
                score += 0.4

            # Check tone
            if self.appropriate_tone(output):
                score += 0.3

            # Check no escalation needed
            if not self.requires_escalation(output):
                score += 0.3

            return score

        elif domain == 'code_generation':
            score = 0

            # Test runs
            if self.code_runs(output):
                score += 0.3

            # Tests pass
            if self.passes_tests(output, test_case):
                score += 0.4

            # No security issues
            if not self.has_security_issues(output):
                score += 0.3

            return score

Step 4: Analyze Failure Modes

class FailureModeAnalysis:
    """Understand why agent fails"""

    def analyze(self, agent, test_set, results):
        """Categorize failures"""

        failure_modes = {
            'reasoning_error': 0,
            'tool_misuse': 0,
            'safety_violation': 0,
            'incomplete_solution': 0,
            'hallucination': 0,
            'timeout': 0
        }

        for failure in results['failures']:
            test_case = self.find_test_case(failure['test'], test_set)

            # Categorize failure
            category = self.classify_failure(
                failure,
                test_case,
                agent
            )

            failure_modes[category] += 1

        # Print analysis
        for mode, count in failure_modes.items():
            percentage = (count / len(results['failures'])) * 100
            print(f"{mode}: {count} ({percentage:.1f}%)")

        # Identify top failure mode
        top_mode = max(failure_modes, key=failure_modes.get)
        print(f"\nTop failure: {top_mode}")
        print("Action: Fix this and retest")

3 Warnings ⚠️

Warning 1: Evaluation Bias

# ❌ WRONG
# Build test set with only "happy path"
test_set = [
    'What is 2+2?',      # Easy
    'Sum 1-100?',        # Medium
    'Optimize algorithm?' # Hard
]
# Accuracy: 98%, but...

# ✅ RIGHT
# Balanced test set
test_set = {
    'easy': [...],           # 30%
    'medium': [...],         # 40%
    'hard': [...],           # 20%
    'edge_cases': [...],     # 5%
    'adversarial': [...]     # 5%
}
# More realistic accuracy: 75%

Warning 2: Overfitting to Custom Eval

# ❌ WRONG
# Agent is optimized for your specific test set
agent = optimize_for_eval(agent, your_test_set)
accuracy_on_your_eval = 95%
accuracy_on_user_data = 42%  # Oops!

# ✅ RIGHT
# Keep eval set separate
eval_set_v1 = build_eval_set()  # Use for development
eval_set_v2 = build_eval_set()  # Hold out for testing

agent = train_on_v1(agent)
final_accuracy = evaluate_on_v2(agent)

Warning 3: Expensive Evaluation

# ❌ WRONG
class SlowEvaluation:
    def evaluate(self, agent):
        # Run agent 10,000 times
        for i in range(10000):
            agent.run(test_case)
        # Takes 5 hours!
        # Runs only once per release

# ✅ RIGHT
class FastEvaluation:
    def evaluate(self, agent):
        # Stratified sample: 500 cases
        for test_case in stratified_sample(500):
            agent.run(test_case)
        # Takes 10 minutes!
        # Can run on every commit

Last Updated: August 9, 2026