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