02 Model Landscape 9 min read 1,978 words

Capability Assessment

This chapter covers how to evaluate and compare model capabilities for your specific use case. Generic benchmarks rarely tell the full story; this guide helps you conduct meaningful assessments.

model-selectionevaluationproductionapplied
01not enough

Why Benchmarks Are Not Enough

1.1

The Benchmark Problem

Public benchmarks (MMLU, HumanEval, GSM8K) have limitations:

IssueImpact
Training data contaminationModels may have seen test questions
Task mismatchBenchmarks may not reflect your use case
Aggregate scores hide varianceModel A may beat B overall but lose on your domain
GamingModels optimized for benchmarks over real tasks
OutdatedBenchmarks lag behind model capabilities
1.2

What Benchmarks Tell You

Texttext · 4 lines
1234
Benchmark results tell you: "Model X scored 88% on MMLU"

What you need to know: "Will Model X correctly answer my 
customers' questions about our product documentation?"

Rule of thumb: Use benchmarks for initial filtering, then conduct your own evaluation.

02dimensions

Evaluation Dimensions

2.1

Dimension 1: Task Performance (Dec 2025)

Task TypeEvaluation ApproachKey Metric
Autonomous CodingCWE/SWE-bench (Verified)% issues resolved autonomously
Long-Horizon PlanningAgentic Loop testingSuccess rate on 10+ step plans
Reasoning DepthThinking mode analysisLogic consistency across CoT steps
Long Context RAGNeedle-in-a-Haystack (2M+)Recall efficiency at scale
Native MultimodalInterleaved Vision/Voice/TextSync accuracy across modalities
2.2

Dimension 2: Agentic Mastery

How well does the model use tools and follow multi-step instructions?

Pythonpython · 17 lines
1234567891011121314151617
def evaluate_agentic_flow(agent, task_environment):
    """
    Measure success on 'Autonomous Agent' tasks:
    1. Plan generation
    2. Tool selection accuracy
    3. Error recovery
    4. Feedback loop utilization
    """
    results = []
    for scenario in task_environment.scenarios:
        traj = agent.run(scenario.goal)
        results.append({
            "success": traj.reached_goal,
            "steps": len(traj.steps),
            "tool_errors": traj.count_invalid_tool_calls()
        })
    return aggregate(results)
2.3

Dimension 3: Reasoning Reliability

Does the "Thinking" mode improve output accuracy vs standard generation?

ModeAccuracy (Math)Accuracy (Code)Avg LatencyTokens / Output
Standard72%68%1.2s400
Thinking94%89%12.5s2400
HybridVariableVariableUser-definedConfigurable
2.4

Reasoning Calibration

The "Over-Thinking" Problem: Models often spend 2000+ "thinking" tokens on a question that could be answered with 10 tokens (e.g., "What is 2+2?").

Principal-level Nuance: Evaluate models based on Logic Efficiency: Accuracy / (Inference Tokens). Production systems in 2025 use Model Arbitration: A small model (Gemini 3 Flash) detects if a query needs "Thinking" mode. This avoids the 10x latency/cost penalty for simple queries.

03elo-based evaluation

Internal Elo-based Evaluation

Moving beyond static rubrics. Rubrics (1-5 scales) are prone to "judge fatigue" and "score drifting." Late 2025 systems use Pairwise Elo for internal golden sets.

The Workflow:

  1. Blind Side-by-Side: Model A and Model B generate answers for the same query.
  2. The Judge: An "Ultra" model (GPT-5.2 or Human) selects the winner.
  3. Elo Update: Update the internal leaderboard.
Pythonpython · 5 lines
12345
def update_elo(winner_elo, loser_elo, k=32):
    expected_winner = 1 / (1 + 10 ** ((loser_elo - winner_elo) / 400))
    new_winner_elo = winner_elo + k * (1 - expected_winner)
    new_loser_elo = loser_elo + k * (0 - (1 - expected_winner))
    return new_winner_elo, new_loser_elo

Why it wins: It provides a relative ranking that is much more robust to changes in judge personality or model versioning.

3.1

Dimension 4: Context Recall (Dec 2025)

With 2M+ context windows, simple "needle-in-a-haystack" is no longer enough. We now measure Contextual Reasoning across the window.

MetricMeasurementTarget
Window RecallFactual recall at 90% window depth> 98%
Cross-Doc ReasoningLogic linking Doc A (pos 10k) to Doc B (pos 1M)> 90%
Contextual Noise ResistanceAccuracy when 90% of window is irrelevant "filler"> 95%
04custom evaluations

Building Custom Evaluations

4.1

Step 1: Define Evaluation Criteria

Pythonpython · 29 lines
1234567891011121314151617181920212223242526272829
evaluation_criteria = {
    "correctness": {
        "weight": 0.4,
        "description": "Is the answer factually correct?",
        "scale": [1, 2, 3, 4, 5],
        "rubric": {
            5: "Completely correct, no errors",
            4: "Mostly correct, minor issues",
            3: "Partially correct, some errors",
            2: "Mostly incorrect",
            1: "Completely wrong or nonsensical"
        }
    },
    "relevance": {
        "weight": 0.3,
        "description": "Does the answer address the question?",
        "scale": [1, 2, 3, 4, 5]
    },
    "completeness": {
        "weight": 0.2,
        "description": "Are all parts of the question addressed?",
        "scale": [1, 2, 3, 4, 5]
    },
    "conciseness": {
        "weight": 0.1,
        "description": "Is the answer appropriately concise?",
        "scale": [1, 2, 3, 4, 5]
    }
}
4.2

Step 2: Create Test Set

Pythonpython · 19 lines
12345678910111213141516171819
test_set = [
    {
        "id": "q001",
        "query": "What is the refund policy for subscription cancellation?",
        "context": "[relevant documentation]",
        "ground_truth": "Full refund within 30 days, prorated after",
        "difficulty": "easy",
        "category": "policy"
    },
    {
        "id": "q002",
        "query": "How do I integrate the API with a Python async application?",
        "context": "[API documentation]",
        "ground_truth": "[expected code pattern]",
        "difficulty": "medium",
        "category": "technical"
    },
    # ... 50-100+ test cases
]

Test set guidelines:

  • Cover all major use cases
  • Include easy, medium, hard examples
  • Balance across categories
  • Include edge cases
  • Have clear ground truth answers
4.3

Step 3: Implement Evaluation

Pythonpython · 41 lines
1234567891011121314151617181920212223242526272829303132333435363738394041
class ModelEvaluator:
    def __init__(self, models: list[str], test_set: list[dict]):
        self.models = models
        self.test_set = test_set
        self.results = {}
    
    def evaluate_all(self):
        for model in self.models:
            self.results[model] = self.evaluate_model(model)
        return self.results
    
    def evaluate_model(self, model: str) -> dict:
        scores = []
        latencies = []
        
        for case in self.test_set:
            start = time.time()
            response = self.generate(model, case)
            latency = time.time() - start
            latencies.append(latency)
            
            # Score using LLM judge or human
            score = self.score_response(case, response)
            scores.append(score)
        
        return {
            "mean_score": mean(scores),
            "score_by_category": self.group_by_category(scores),
            "p50_latency": percentile(latencies, 50),
            "p99_latency": percentile(latencies, 99)
        }
    
    def score_response(self, case: dict, response: str) -> float:
        # Option 1: LLM-as-judge
        return self.llm_judge(case, response)
        
        # Option 2: Exact match
        # return exact_match(response, case["ground_truth"])
        
        # Option 3: Semantic similarity
        # return cosine_sim(embed(response), embed(case["ground_truth"]))
4.4

Step 4: LLM-as-Judge

Pythonpython · 19 lines
12345678910111213141516171819
def llm_judge(case: dict, response: str) -> dict:
    prompt = f"""Evaluate this response to a customer query.

Query: {case['query']}
Expected Answer: {case['ground_truth']}
Model Response: {response}

Rate the response on these criteria (1-5 scale):
1. Correctness: Is it factually accurate?
2. Relevance: Does it answer the question?
3. Completeness: Are all aspects covered?
4. Conciseness: Is it appropriately brief?

Output JSON:
{{"correctness": X, "relevance": X, "completeness": X, "conciseness": X, "reasoning": "..."}}
"""
    
    result = judge_model.generate(prompt)
    return parse_json(result)
05evaluation pitfalls

Common Evaluation Pitfalls

5.1

Pitfall 1: Small Test Set

Problem: 20 test cases is not enough for reliable comparison.

Solution: Aim for 100+ cases, stratified by difficulty and category.

5.2

Pitfall 2: Ambiguous Ground Truth

Problem: "Reasonable" answers get marked wrong.

Texttext · 4 lines
1234
Query: "What is the capital of Australia?"
Ground truth: "Canberra"
Model answer: "The capital of Australia is Canberra."
Exact match: FAIL (but clearly correct)

Solution: Use semantic matching or LLM judge, not exact match.

5.3

Pitfall 3: Evaluation Set Leakage

Problem: Using same cases for development and evaluation.

Solution: Keep a held-out test set that you never use for prompt tuning.

5.4

Pitfall 4: Ignoring Variance

Problem: Running each test once ignores model randomness.

Solution: Run multiple times with temperature > 0, report confidence intervals.

5.5

Pitfall 5: Cost Blindness

Problem: Best model is 10x more expensive.

Solution: Always report quality-adjusted cost.

Pythonpython · 9 lines
123456789
def quality_adjusted_cost(model_results):
    return {
        model: {
            "quality": results["mean_score"],
            "cost_per_1k": results["cost_per_1k_queries"],
            "quality_per_dollar": results["mean_score"] / results["cost_per_1k"]
        }
        for model, results in model_results.items()
    }
06assessment process

Practical Assessment Process

6.1

Week 1: Setup and Initial Filtering

Texttext · 3 lines
123
Day 1-2: Define evaluation criteria and create test set
Day 3-4: Benchmark 4-6 candidate models
Day 5: Analyze results, filter to top 2-3
6.2

Week 2: Deep Evaluation

Texttext · 4 lines
1234
Day 1-2: Expand test set for top candidates
Day 3: Test edge cases and robustness
Day 4: Measure latency and throughput
Day 5: Calculate total cost of ownership
6.3

Week 3: Production Validation

Texttext · 3 lines
123
Day 1-2: Shadow mode deployment
Day 3-4: A/B test if traffic allows
Day 5: Final decision and documentation
6.4

Decision Template

Markdownmarkdown · 22 lines
12345678910111213141516171819202122
## Model Evaluation Report

### Candidates Evaluated
- Model A: GPT-4o
- Model B: Claude 3.5 Sonnet
- Model C: Llama 3.1 70B

### Evaluation Results

| Metric | Model A | Model B | Model C |
|--------|---------|---------|---------|
| Overall Score | 4.2/5 | 4.3/5 | 3.9/5 |
| Category 1 | ... | ... | ... |
| P50 Latency | 450ms | 520ms | 180ms |
| Cost/1K queries | $0.85 | $1.10 | $0.25 |

### Recommendation
Model B (Claude 3.5 Sonnet) for quality-critical paths
Model C (Llama 3.1 70B) for high-volume, cost-sensitive paths

### Rationale
[Detailed reasoning]
07testing models

A/B Testing Models

7.1

When to A/B Test

  • High traffic (1000+ queries/day)
  • Clear success metrics
  • Acceptable risk of quality variation
  • Need production validation
7.2

A/B Test Design

Pythonpython · 33 lines
123456789101112131415161718192021222324252627282930313233
class ModelABTest:
    def __init__(self, model_a: str, model_b: str, traffic_split: float = 0.5):
        self.model_a = model_a
        self.model_b = model_b
        self.traffic_split = traffic_split
        self.results = {"a": [], "b": []}
    
    def route_request(self, request_id: str) -> str:
        # Deterministic routing for consistency
        hash_val = hash(request_id) % 100
        if hash_val < self.traffic_split * 100:
            return self.model_a
        return self.model_b
    
    def record_outcome(self, request_id: str, metrics: dict):
        model = self.route_request(request_id)
        bucket = "a" if model == self.model_a else "b"
        self.results[bucket].append(metrics)
    
    def analyze(self):
        return {
            "model_a": {
                "name": self.model_a,
                "mean_score": mean([r["score"] for r in self.results["a"]]),
                "sample_size": len(self.results["a"])
            },
            "model_b": {
                "name": self.model_b,
                "mean_score": mean([r["score"] for r in self.results["b"]]),
                "sample_size": len(self.results["b"])
            },
            "p_value": self.calculate_significance()
        }
7.3

Metrics to Track

Metric TypeExamples
QualityUser ratings, expert review, LLM judge
EngagementClick-through, time on page, follow-up queries
BusinessConversion, support escalation, resolution rate
OperationalLatency, errors, cost
08questions

Interview Questions

Q: How would you evaluate models for a customer support chatbot?

Strong answer: I would structure evaluation in layers:

1. Offline evaluation (80% of effort):

  • Create test set from real support tickets (200+ cases)
  • Cover all categories: billing, technical, returns, general
  • Include easy, medium, hard difficulty
  • Measure: accuracy, helpfulness, safety

2. Evaluation method:

  • Use LLM-as-judge for subjective metrics
  • Human review for sample (20%)
  • Track instruction following (format, length)

3. Metrics:

Pythonpython · 6 lines
123456
metrics = {
    "resolution_accuracy": "Does answer solve the problem?",
    "safety": "No harmful/wrong advice?", 
    "tone": "Professional and empathetic?",
    "escalation_appropriate": "Knows when to involve human?"
}

4. Production validation:

  • Shadow mode: run new model, compare outputs
  • A/B test: 10% traffic to new model
  • Monitor: CSAT, escalation rate, resolution time

Q: What is wrong with using MMLU to compare models for your use case?

Strong answer: MMLU has several problems for specific use cases:

1. Domain mismatch

MMLU tests academic knowledge. My customer support bot needs product knowledge.

2. Format mismatch

MMLU is multiple choice. My use case is free-form generation.

3. Contamination

Models may have trained on MMLU questions.

4. Aggregation hides variance

Model A might beat B on MMLU but lose on the specific categories I care about.

5. No context testing

MMLU does not test RAG or long-context abilities.

Better approach:

  • Use MMLU for initial filtering (saves time)
  • Build custom evaluation for final decision
  • Test on actual use case data
  • Include operational metrics (latency, cost)
09references

References


Previous: Model Taxonomy | Next: Pricing and Costs

summary · added by this rebuild

Key takeaways

01

Thinking mode costs ten times the latency

The mode comparison puts standard generation at 72 percent math accuracy in 1.2s and 400 tokens, against thinking mode's 94 percent in 12.5s and 2400 tokens.

02

Benchmarks filter, custom evals decide

Contamination, format mismatch and aggregate scores that hide per-category variance mean MMLU narrows the candidate list but never settles which model answers your users' questions.

03

Pairwise Elo beats a 1-5 rubric

Rubric scores drift as judges fatigue; blind side-by-side comparisons fed into an Elo table with k=32 give a relative ranking robust to judge and version changes.

04

Twenty test cases prove nothing

The pitfalls list asks for 100+ examples stratified by difficulty and category, each run several times at non-zero temperature so variance is reported rather than hidden.

05

Always report quality per dollar

Cost blindness is its own pitfall: pair mean score with cost per thousand queries so a ten-times-more-expensive model has to justify the margin, not just win.