eval-analysis
$
npx mdskill add microsoft/ACESEvals/eval-analysisSABER evaluation analysis is a structured data science framework for comparing AI agent performance across cybersecurity benchmark domains. The analysis pipeline transforms `.eval` log files into actionable insights through 12+ standardized experiments plus domain-specific analyses.
SKILL.md
.github/skills/eval-analysisView on GitHub ↗
---
name: eval-analysis
description: >
Comprehensive guide for analyzing SABER evaluation results — model comparison, agent architecture
comparison, domain-specific analysis, and cross-domain aggregate analysis. Use this when asked to
analyze eval results, compare models, generate visualizations, interpret scores, investigate
cost-efficiency trade-offs, or extend analysis to new domains.
---
# Eval Analysis Skill
## Overview
SABER evaluation analysis is a structured data science framework for comparing AI agent performance across cybersecurity benchmark domains. The analysis pipeline transforms `.eval` log files into actionable insights through 12+ standardized experiments plus domain-specific analyses.
## Getting Started: Sample Eval Data
**No evals to analyze yet?** The repo ships with pre-run sample `.eval` files and can auto-download more from HuggingFace.
### Option 1: Use bundled sample evals (fastest)
The `eval_samples/` directory contains pre-run evaluation results for all 3 domains across 5 models and 3 agent architectures:
```
eval_samples/
├── excytin/ # 20 eval files (5 models × default + reasoning baselines + 3 agents)
├── cybench/ # 17 eval files
└── cti_realm/ # 11 eval files
```
**Models included**: Claude Haiku 4.5, Claude Sonnet 4.6, Claude Opus 4.6, GPT-5.4, GPT-5.4-mini
**Agent architectures included**: React, GH Copilot, Claude Code (for Sonnet 4.6)
**Baselines**: No-reasoning/no-thinking variants for extended thinking comparison
The notebooks are pre-configured to load from `eval_samples/`. Just run any notebook — no configuration needed.
### Option 2: Auto-download from HuggingFace
If `.eval` files are missing locally, `ensure_eval_files()` automatically downloads them from the **[AcesEvals HuggingFace dataset](https://huggingface.co/datasets/anandmudgerikar/AcesEvals)**:
```python
from saber_analysis import ensure_eval_files
ensure_eval_files(
eval_logs=EVAL_LOGS, # {display_name: filename.eval}
log_dir=LOG_DIR, # local directory
domain='excytin', # HuggingFace subfolder
fallback_dirs=[ # check these before downloading
str(REPO_ROOT / 'eval_samples' / 'excytin'),
str(REPO_ROOT / 'latest_experiments' / 'excytin'),
],
)
```
**Resolution order**: local `log_dir` → `fallback_dirs` → HuggingFace download
**HuggingFace repo**: `anandmudgerikar/AcesEvals` (dataset type)
**HuggingFace path**: `latest_experiment_samples/{domain}/{filename}.eval`
### Option 3: Run your own evaluations
If you need results for a model or agent architecture not in the sample evals:
```bash
# Run for a new model
uv run inspect eval domains/<domain> --model <api/model-id> --display plain
# Run for a specific agent architecture
uv run inspect eval domains/<domain> --model <api/model-id> -T agent=react --display plain
uv run inspect eval domains/<domain> --model <api/model-id> -T agent=copilot --display plain
# Run with extended reasoning disabled (for baseline comparison)
# Set INSPECT_EVAL_MODEL_ARGS=reasoning_effort=none or configure in .env
```
Then copy the `.eval` file to `eval_samples/<domain>/` or `latest_experiments/<domain>/` and add it to the notebook's `EVAL_LOGS` configuration.
### What's available vs. what you need to run
| Domain | Models (sample evals) | Agent Architectures (sample evals) | What to run yourself |
|--------|-----------------------|-------------------------------------|----------------------|
| Excytin | Haiku 4.5, Sonnet 4.6, Opus 4.6, GPT-5.4, GPT-5.4-mini | React, Copilot, Claude Code (Sonnet) | New models, new agents on non-Sonnet models |
| CyBench | Haiku 4.5, Sonnet 4.6, Opus 4.6, GPT-5.4, GPT-5.4-mini | React, Copilot, Claude Code (Sonnet) | New models, new agents, more challenges |
| CTI Realm | Haiku 4.5, Sonnet 4.6, Opus 4.6, GPT-5.4, GPT-5.4-mini | Copilot, Claude Code (Sonnet) | React for non-Sonnet models, new models |
### Notebook Inventory
| Notebook | Scope | When to use |
|----------|-------|-------------|
| `notebooks/eval_analysis.ipynb` | Domain-agnostic template | Analyzing any single domain's model results; starting point for new domains |
| `notebooks/excytin_analysis.ipynb` | Excytin-specific (self-contained) | Full analysis of Excytin incident response domain |
| `notebooks/cybench_analysis.ipynb` | CyBench-specific (self-contained) | Full analysis of CyBench CTF challenges |
| `notebooks/cti_realm_analysis.ipynb` | CTI Realm-specific (self-contained) | Full analysis of CTI Realm threat intel domain |
| `notebooks/agent_architecture_analysis.ipynb` | Agent comparison template | Comparing agent architectures (React, Copilot, Claude Code) on any domain |
| `notebooks/excytin_agent_architecture_analysis.ipynb` | Excytin agent comparison | Agent architecture analysis specific to Excytin |
| `notebooks/cybench_agent_architecture_analysis.ipynb` | CyBench agent comparison | Agent architecture analysis specific to CyBench |
| `notebooks/cti_realm_agent_architecture_analysis.ipynb` | CTI Realm agent comparison | Agent architecture analysis specific to CTI Realm |
| `notebooks/aggregate_model_analysis.ipynb` | Cross-domain model comparison | Aggregate model analysis across all domains (normalized) |
| `notebooks/aggregate_agent_architecture_analysis.ipynb` | Cross-domain agent comparison | Aggregate agent architecture analysis across all domains |
| `notebooks/model_safety_filters_analysis.ipynb` | Safety filter investigation | Analyzing model safety refusals and guardrail triggers |
| `notebooks/excytin_basic_analysis.ipynb` | Quick Excytin analysis | Lightweight Excytin analysis for rapid iteration |
### Shared Analysis Library
The `notebooks/saber_analysis/` package provides reusable utilities:
| Module | Functions | Purpose |
|--------|-----------|---------|
| `data_loader.py` | `ensure_eval_files()`, `load_eval_logs()`, `load_baseline_logs()`, `load_trajectory_data()` | Standardized .eval parsing; HuggingFace fallback downloads; typed inspect_ai log API |
| `cost.py` | `calc_cost()`, `extract_cost_rows()` | Token→dollar conversion using per-model pricing |
| `plots.py` | `setup_plotting()`, `make_legend_patches()`, `classify_score_type()` | Consistent matplotlib styling across all notebooks |
---
## Data Pipeline
```
.eval ZIP archives
│
├── header.json → run metadata, overall saber_overall scores
├── samples/*.json → per-sample messages, scores, timing, tool calls
└── summaries.json → aggregated score summaries
│
▼
inspect_ai.log API
├── read_eval_log(path, header_only=True) → EvalLog with results.scores
├── read_eval_log_sample_summaries(path) → per-sample scores & timing
└── read_eval_log_sample(path, id=...) → full messages & tool calls
│
▼
saber_analysis library
├── ensure_eval_files() → locates locally or downloads from HuggingFace
├── load_eval_logs() → parses into DataFrames (overall_df, samples_df, subtasks_df)
├── load_trajectory_data() → extracts tool calls, steps, timing per sample
└── extract_cost_rows() → builds cost DataFrame from token usage + pricing
│
▼
Notebook Configuration
├── EVAL_LOGS: dict[str, str] → {display_name: filename.eval}
├── COLORS: dict[str, str] → {display_name: hex_color}
├── PRICING: dict[str, dict] → {api_model_id: {input, output, cache_read, cache_write}}
├── GROUP_FN: Callable → domain-specific sample ID → group label
└── NO_THINKING_LOGS (optional) → baseline without extended reasoning
│
▼
Analysis Cells (Experiments 1–14+)
│
▼
Artifacts saved to notebooks/artifacts/{domain}/*.png
```
### Key DataFrames
| DataFrame | Columns | Source |
|-----------|---------|--------|
| `overall_df` | model, mean, stderr | `load_eval_logs()` — header-level saber_overall |
| `samples_df` | model, sample_id, group, score | `load_eval_logs()` — per-sample saber_overall |
| `subtasks_df` | model, sample_id, group, score_type, score | `load_eval_logs()` — submission/checkpoint/aggregate breakdown |
| `traj_df` | model, sample_id, n_tool_calls, n_steps, total_time, tool_counts | `load_trajectory_data()` — agent behavior data |
| `cost_df` | model, score, input_tokens, output_tokens, cache_read, cache_write, reasoning_tokens, total_cost, cost_per_sample | `extract_cost_rows()` |
---
## Domain-Agnostic Analysis Types (Experiments 1–14)
These 14 core analyses apply to **any** SABER domain. They appear in `eval_analysis.ipynb` and every domain-specific notebook.
### Experiment 1: Overall Reward Distribution
- **Metric**: Mean `saber_overall` score per model
- **Visualization**: Bar chart with error bars (stderr) + optional hatched overlay for reasoning delta
- **Interpretation**:
- Higher bars = better performance
- Overlapping error bars = statistically insignificant differences
- Reasoning delta (hatched) shows impact of extended thinking (positive = reasoning helps)
- **Code pattern**:
```python
overall_df.plot.bar(x='model', y='mean', yerr='stderr')
```
### Experiment 2: Per-Group Breakdown (Domain-Specific Grouping)
- **Metric**: Scores segmented by `GROUP_FN` dimension
- **Visualization**: Clustered bar chart (models × groups)
- **Group functions by domain**:
- **Excytin**: `'_'.join(id.split('_')[:2])` → 8 security incidents
- **CyBench**: `'_'.join(id.split('_')[:-2])` → challenge types
- **CTI Realm**: `id.split('_')[0]` → 3 platforms (Linux, AKS, Cloud)
- **Interpretation**:
- Large within-group variance = model-dependent difficulty
- Shifting model rankings across groups = no single dominant model
- Uniform clusters = inherently hard/easy groups
- **Requires**: Setting `GROUP_FN` in configuration; set `GROUP_FN = None` to skip
### Experiment 3: Cost Analysis
- **Metrics**: Total cost ($), cost per sample, token breakdown (input/output/cache/reasoning)
- **Visualizations**:
1. Total cost bar chart
2. Cost per sample bar chart
3. Pareto frontier (Score vs. Cost — upper-left corner = best value)
- **Interpretation**:
- Pareto-dominant models: no other model is both cheaper AND better
- Arrows on Pareto chart show reasoning cost delta
- Diminishing returns visible from frontier plateau
- **Code pattern**:
```python
cost_df = pd.DataFrame(extract_cost_rows(EVAL_LOGS, LOG_DIR, PRICING, N_SAMPLES))
```
### Experiment 4: Token Usage Breakdown
- **Metrics**: Input, output, cache read/write, reasoning tokens per model
- **Visualization**: Horizontal stacked bar chart
- **Interpretation**:
- **Input tokens**: Prompts, tool results, conversation history
- **Output tokens**: Generated text, tool calls, answers
- **Cache read/write**: Prompt caching (Claude > GPT generally)
- **Reasoning tokens**: Extended thinking overhead; highly variable by model family
- Large hatched sections = reasoning adds substantial overhead
### Experiment 5: Cost Efficiency (Reward per Dollar)
- **Formula**: `Cost Efficiency = Mean SABER Score / Total Cost ($)`
- **Visualization**: Bar chart (taller = more efficient)
- **Interpretation**:
- Highest-cost model is not always least efficient
- Budget-constrained deployments should prioritize high-efficiency models
- Reasoning may increase OR decrease efficiency (depends on score gain vs. cost increase)
### Experiment 6: Sub-Task Score Breakdown
- **Metrics**: Submission score, checkpoint scores, aggregate score per model
- **Visualization**: Grouped bars (submission/checkpoints/aggregate) with reasoning delta overlay
- **Interpretation**:
- **Submission >> Checkpoints**: Model reaches correct conclusion without thorough investigation (may be guessing; risky in production)
- **Checkpoints >> Submission**: Model investigates well but fails at synthesis
- **Aligned**: Investigation matches answer quality (ideal)
### Experiment 7: Checkpoint Score Distributions
- **Visualization**: Violin plots per model
- **Interpretation**:
- **Bimodal** (peaks at 0 and 1): Binary pass/fail checkpoint; few partial successes
- **Continuous**: Fine-grained scoring; various intermediate scores
- **Wide violins**: Inconsistent across samples; **narrow**: consistent
- **Long upper tails**: Some samples max out the checkpoint score
### Experiment 8: Domain-Specific Gap Analysis
- **Purpose**: Submission vs. checkpoint gap per domain grouping dimension
- **Visualization**: Heatmap (models × groups) showing gap = submission - checkpoint
- **Interpretation**:
- Red (positive gap): Model guesses correctly without thorough investigation
- Green (negative gap): Model investigates well but fails at synthesis
- Yellow (aligned): Investigation matches answer quality
### Experiment 9: Agent Trajectory Analysis
Data loading step that extracts per-sample behavioral data:
- `n_tool_calls`: Actual tool invocations per sample
- `n_steps`: Assistant message turns per sample
- `tool_counts`: Per-tool breakdown (bash, python, domain tools)
- `total_time`: Wall-clock seconds per sample
### Experiment 9a: Score vs. Tool-Call Limit (Effort Budget)
- **Calculation**: For each hypothetical limit N, mean score where samples needing >N calls score 0
- **Visualization**: Curves per model converging to actual mean at real tool-call cap
- **Interpretation**:
- **Steep early rise**: Efficient model (solves many tasks in first 5–15 calls)
- **Plateau**: Budget increase yields diminishing returns
- **Key finding**: All models typically reach 95% of final score by ~20–25 calls (Excytin) or ~33–35 (CTI Realm)
### Experiment 9b: Effort Distribution per Model
- **Visualization**: Violin plots of tool calls and steps per sample per model
- **Interpretation**:
- **Narrow, low**: Efficient (quick solutions)
- **Wide, high**: Variable effort or struggling
- **Long upper tail**: Some samples hit tool-call limit (binding constraint)
### Experiment 10: Tool Usage Analysis
- **Visualization**: Dual panel — left: mean tool calls per tool (absolute); right: tool call ratio (fraction)
- **Interpretation**:
- Domain determines tool preferences (Excytin → 95%+ bash; CTI Realm → 48% KQL tools)
- Cross-model consistency = task design funnels agents toward certain tools
- Model-specific tool preferences reveal different investigation strategies
### Experiment 11: Time Efficiency & Reward per Tool Call
- **Formulas**:
- `Reward/Call = Mean SABER Score / Mean Tool Calls`
- `Reward/Minute = Mean Score / Wall-Clock Time (min)`
- **Visualizations**: Bar charts, scatter plots (effort vs. score with trend line), time distribution violin plots
- **Interpretation**:
- High reward/call = doesn't waste invocations
- High reward/minute = fast AND accurate
- Time outliers reveal latency issues (e.g., GPT 10+ min vs. Claude 2–3 min)
### Experiment 12: Score Outcome Segmentation by Effort
- **Methodology**: Segment by tool-call quartile (Q1–Q4); show perfect/partial/zero score rates
- **Visualization**: Stacked bar chart per model per quartile
- **Interpretation**:
- Perfect-score rate drops Q1→Q4: High effort = harder task (effort doesn't help)
- Zero-score rate doubles Q1→Q4: Tool-call limit is binding constraint on hard tasks
- Partial scores rise in Q4: Upper-effort tasks succeed only partially within budget
### Experiment 13: Cross-Model Difficulty Agreement
- **Calculation**: Spearman rank correlation of per-sample scores for each model pair
- **Visualization**: Heatmap with ρ values and significance markers
- **Interpretation**:
- **High ρ (>0.7)**: Models agree — difficulty is intrinsic to task
- **Low ρ (<0.3)**: Models struggle differently — complementary strengths, good for ensembles
- **Within-family > cross-family**: Same-vendor models agree more than different vendors
---
## Domain-Specific Analyses
### Excytin (Incident Response Forensics)
**Domain context**: 8 security incidents, 599 tasks, agents investigate via SQL queries through bash tool.
| Analysis | What it reveals |
|----------|-----------------|
| **Per-Incident Breakdown** (Exp 2) | Score variation across 8 incident types |
| **Submission vs. Checkpoint Gap Heatmap** (Exp 8) | Per-incident × per-model gap showing investigation thoroughness |
| **SQL Query Analysis** (Exp 14) | Classifies all SQL queries into success/large-result/empty/error; reveals query quality differences across models |
**SQL Query Classification**:
- **Success (≤100 rows)**: Manageable, agent can parse
- **Large result (>100 rows)**: Unrefined query; should add LIMIT/WHERE
- **Empty result**: Valid syntax but no matching data (dominant failure mode)
- **Error**: Syntax error, missing table, permission denied
**Key Excytin insights**:
- All models show large positive submission-checkpoint gaps (0.459–0.573) — bypassing investigation
- Bash tool dominates (95–100% of tool calls); Haiku uniquely uses python (~5%)
- Query quality separates models: Opus 79% success rate vs. GPT-5.4-mini 61%
- Empty results are more problematic than syntax errors (agents waste effort on unproductive queries)
### CyBench (CTF Challenges)
**Domain context**: CTF exploitation challenges with multi-checkpoint progressive investigation tracking.
| Analysis | What it reveals |
|----------|-----------------|
| **Per-Challenge Breakdown** (Exp 2) | Score per challenge type |
| **Submission vs. Checkpoint Gap** (Exp 8) | Per-challenge gap showing investigation process |
**CyBench checkpoint progression** (e.g., Labyrinth Linguist):
1. Contextualizing — understanding challenge environment
2. Understanding templating — recognizing SSTI/language context
3. Identifying attack vectors — finding injection points
4. Exploring templates — testing template engines
5. Locating the flag — finding flag file on disk
6. Crafting exploit — building final payload
**Key CyBench insights**:
- All models achieve 1.0 on submission but 0.167–0.500 on checkpoints — solving without demonstrating full process
- Currently 1 sample per model; distribution analyses produce summary tables instead of charts
### CTI Realm (Threat Intelligence & Detection)
**Domain context**: 25 tasks across 3 platforms (Linux, AKS, Cloud), 5 scored checkpoints totaling 10.0 points.
| Analysis | What it reveals |
|----------|-----------------|
| **Per-Platform Breakdown** (Exp 2) | Score variation across Linux/AKS/Cloud |
| **Per-Checkpoint Score Heatmap** (Exp 8) | C0–C4 × Model heatmap showing checkpoint-level performance |
| **CTI Tool Usage & Report Analysis** (Exp 14) | Tool call patterns across 4 categories (KQL/Data, CTI/MITRE, Sigma, General) |
| **KQL Query Quality Analysis** (Exp 15) | Query success/error rates, correlation with F1 scores |
| **Checkpoint Correlation Analysis** (Exp 16) | Independence of C0–C4 checkpoints (validates scoring design) |
**CTI Realm 5-checkpoint scoring**:
- **C0 CTI Alignment** (max 1.25): Did agent use CTI reports? Relevance to objective?
- **C1 MITRE Techniques** (max 0.75): Jaccard similarity of found vs. expected ATT&CK techniques
- **C2 Data Exploration** (max 1.0): Coverage of expected data sources queried
- **C3 Query Iteration** (max 0.5): Binary — did agent run ≥2 unique successful KQL queries?
- **C4 Detection Quality** (max 6.5): KQL F1 (weight 5.0) + Sigma rule quality (weight 1.5)
**CTI Realm tool categories**:
- **KQL/Data** (48%): execute_kql_query, list_kusto_tables, get_table_schema, sample_table_data
- **CTI/MITRE** (29%): list_cti_report_tags, get_cti_reports_by_tag, search_mitre_techniques
- **Sigma/Detection** (13%): search_sigma_rules, validate_output_json
- **General** (10%): bash, python
**Key CTI Realm insights**:
- C4 (Detection Quality) dominates total score (65% of max) but max achieved is ~59%
- Checkpoints are remarkably independent (all pairwise |ρ| < 0.4) — validates scoring design
- C0 (CTI reading) does NOT predict C4 (detection quality) — distinct skills
- More KQL queries don't linearly improve detection: Opus achieves 58% C4 with only 4.6 queries/sample vs. GPT-5.4's 9.7
---
## Agent Architecture Analysis
### Comparison Framework
Agent architecture analysis uses the **same 12 experiments** as model comparison but slices by agent architecture instead of model identity. Primary comparison axis: **React vs. GH Copilot vs. Claude Code** for a fixed model (currently Claude Sonnet 4.6).
### Agent Architecture Findings (Aggregate)
| Metric | React | GH Copilot | Claude Code |
|--------|-------|------------|-------------|
| Aggregate Score | 0.801 ± 0.134 | 0.648 | 0.441 |
| Cost/Sample | $0.251 (cheapest) | $0.350 | $0.420 |
| Reward/$ | 3.19 (highest) | 1.85 | 1.05 |
| Tool Calls/Sample | ~18.8 | ~18.7 | ~18.9 |
| Time/Sample | 3.3 min (fastest) | 5.0 min | 4.2 min |
| Ranking Consistency | 1.0 ± 0.0 (perfect) | 2.33 ± 1.15 (volatile) | 2.67 ± 0.58 |
**Key agent architecture insights**:
- React dominates on score, cost, and consistency across all domains
- All architectures use nearly identical tool-call budgets (~18.8 calls); speed difference is thinking/planning overhead
- Copilot matches React on submission scores (0.846 vs 0.852) but lags on checkpoints (0.380 vs 0.548)
- Claude Code's primary failure mode is invalid final answers (lowest submission score: 0.348)
---
## Cross-Domain Aggregate Analysis
### Normalization Method
Domains have vastly different sample counts (Excytin: 599, CTI Realm: 25, CyBench: 1). To prevent sample-rich domains from dominating:
1. Compute **per-domain mean** for each model/agent on each metric
2. Average per-domain means with **equal weight per domain**
$$\text{Aggregate Score}(m) = \frac{1}{|\mathcal{D}|} \sum_{d \in \mathcal{D}} \overline{\text{score}}(m, d)$$
### Aggregate Experiment Types
| # | Analysis | What it shows |
|---|----------|---------------|
| 1 | Normalized Overall Reward | Domain-weighted mean saber_overall per model |
| 2 | Per-Domain Score Breakdown | Heatmap + grouped bar showing model × domain scores |
| 3 | Normalized Cost Analysis | Per-sample cost averaged across domains; Pareto chart |
| 4 | Normalized Cost Efficiency | Reward per dollar, domain-weighted |
| 5 | Sub-Task Breakdown | Submission/Checkpoints/Aggregate, domain-averaged |
| 6 | Agent Trajectory Efficiency | Tool calls, steps, time (domain-averaged) |
| 7 | Cross-Domain Ranking Consistency | Bump chart: do models rank the same across domains? |
| 8 | Cross-Domain Radar Chart | Spider chart showing per-domain strength per model |
| 9 | Reasoning Impact (models only) | Per-domain reasoning delta aggregated with equal weight |
**Partial coverage**: If a model doesn't appear in all domains, its aggregate is computed over only contributing domains (annotated in charts).
---
## Key Insights Patterns
These are recurring patterns to look for and communicate when interpreting results:
### 1. Cost-Efficiency Trade-offs
- Higher-cost models often provide diminishing returns
- Typical pattern: Haiku (cheapest) → Sonnet (mid) → Opus (high) → GPT (highest)
- Haiku gets 12–18× more reward per dollar than GPT-5.4
- Marginal score gains diminish as cost increases
### 2. Effort vs. Outcome (Non-linear)
- More tool calls do NOT guarantee better scores
- High-effort (Q4) tasks have 15–20% more zero scores than low-effort (Q1) tasks
- Tool-call budget is a binding constraint on hard tasks, not a surplus resource
### 3. Investigation vs. Final Answer Gap
- Submission scores >> Checkpoint scores across ALL domains
- Typical gaps: Excytin 0.459–0.573, CyBench 0.500–0.833
- Models reach correct conclusions without thorough investigation — risky in production
- Models may be using prior knowledge or pattern-matching instead of genuine analysis
### 4. Speed vs. Capability (Model-Family Patterns)
- Claude models: 1.4–2.7 min/sample, lower token budgets
- GPT models: 10+ min/sample (4–6× slower), higher token budgets
- GPT's extended reasoning overhead adds latency without proportional score gains in some domains
### 5. Task Difficulty Varies by Domain Structure
- Easy domains (all models score high): Excytin (0.77–0.88)
- Hard domains (massive variance): CTI Realm (0.26–0.63)
- Domain complexity (tool count, checkpoint interdependencies) drives difficulty ceiling
### 6. Model Agreement on Difficulty
- Spearman correlations moderate (0.4–0.8) — difficulty is partly intrinsic, partly model-dependent
- Same-family models agree more than cross-family (Sonnet↔Opus: ρ=0.64 vs. Claude↔GPT: ρ=0.47–0.53)
- Implication: Ensemble strategies should pair different model families
### 7. Reasoning Impact is Model & Domain-Dependent
- Excytin: Minimal reasoning boost for most models
- CTI Realm: Dramatic reasoning boost for GPT (+0.16–0.18), none for Claude
- Multi-step collaborative workflows benefit more from explicit chain-of-thought
### 8. Architecture Consistency
- React agent: Rank 1 across all domains (perfect consistency)
- GH Copilot: Rank 1–3 depending on domain (volatile)
- Agent architecture choice has domain-dependent trade-offs
---
## Configuration Guide
### Adding a New Model to Analysis
1. **Check if sample evals already exist** — look in `eval_samples/<domain>/` or try running the notebook (it auto-downloads from HuggingFace)
2. **If not available**, run the evaluation:
```bash
uv run inspect eval domains/<domain> --model <api/model-id> --display plain
```
3. Copy the `.eval` file to `eval_samples/<domain>/` or `latest_experiments/<domain>/` with a descriptive name
4. Edit the notebook's Configuration cell:
```python
EVAL_LOGS['Display Name'] = 'filename.eval'
COLORS['Display Name'] = '#HexColor'
PRICING['api/model-id'] = {'input': X, 'output': Y, 'cache_read': Z, 'cache_write': W}
```
5. (Optional) Add a no-reasoning baseline to `NO_THINKING_LOGS`
### Adding a New Domain
1. Create a domain-specific notebook by copying `eval_analysis.ipynb`
2. Define `GROUP_FN` for your domain's sample ID grouping:
```python
# Inspect sample IDs from your .eval files
# Write a function: sample_id → group_label
GROUP_FN = lambda id: id.split('_')[0] # Example
```
3. Add domain-specific experiments after the generic 14
4. Register the notebook in the inventory table above
### Adding a New Agent Architecture
1. **Check if sample evals already exist** — look in `eval_samples/<domain>/` for `*_react.eval`, `*_copilot.eval`, `*_claude_code.eval`
2. **If not available**, run evals for each agent:
```bash
uv run inspect eval domains/<domain> --model <model> -T agent=react --display plain
uv run inspect eval domains/<domain> --model <model> -T agent=copilot --display plain
```
3. Add entries to the agent architecture notebook's `AGENT_EVAL_LOGS`
4. Add a color in `AGENT_COLORS`
---
## Artifact Output Structure
Analysis notebooks save visualizations to `notebooks/artifacts/`:
```
notebooks/artifacts/
├── {domain}/ # Per-domain model comparison
│ ├── overall_reward.png
│ ├── per_{group}_scores.png
│ ├── cost_analysis.png
│ ├── token_usage_breakdown.png
│ ├── cost_efficiency.png
│ ├── subtask_breakdown.png
│ ├── checkpoint_distributions.png
│ ├── submission_checkpoint_gap.png
│ ├── score_vs_tool_call_limit.png
│ ├── effort_distributions.png
│ ├── tool_usage.png
│ ├── steps_vs_reward.png
│ ├── effort_outcome_segmentation.png
│ └── cross_model_correlation.png
│
├── {domain}_agent/ # Per-domain agent architecture comparison
│ ├── overall_reward_by_agent.png
│ ├── cost_analysis_by_agent.png
│ └── ... (same experiment set, sliced by agent)
│
├── aggregate_model/ # Cross-domain model comparison (normalized)
│ ├── aggregate_overall_reward.png
│ ├── per_domain_breakdown.png
│ ├── aggregate_cost_analysis.png
│ ├── aggregate_cost_efficiency.png
│ ├── cross_domain_radar.png
│ ├── ranking_consistency.png
│ └── reasoning_impact.png
│
└── aggregate_agent/ # Cross-domain agent comparison (normalized)
├── aggregate_overall_reward.png
├── per_domain_breakdown.png
├── cross_domain_radar.png
└── ranking_consistency.png
```
---
## Quick Analysis Recipes
### "Which model should I use for domain X?"
1. Open `notebooks/{domain}_analysis.ipynb`
2. Look at Experiment 1 (overall reward) for raw scores
3. Look at Experiment 5 (cost efficiency) for value
4. Check Experiment 3 (Pareto frontier) for dominated vs. non-dominated models
5. Consider Experiment 11 (time efficiency) if latency matters
### "How do agent architectures compare?"
1. Open `notebooks/aggregate_agent_architecture_analysis.ipynb`
2. Look at overall reward per agent (Exp 1) — React typically dominates
3. Check per-domain breakdown (Exp 2) — some agents may excel in specific domains
4. Review cost analysis (Exp 3) — architecture affects cost
5. Check trajectory efficiency (Exp 6) — same tool calls, different speed
### "Is extended reasoning worth it?"
1. Open the domain-specific notebook with `NO_THINKING_LOGS` configured
2. Look at reasoning delta (hatched bars) in Exp 1
3. Check cost-efficiency impact in Exp 5
4. CTI Realm benefits most from reasoning; Excytin shows minimal impact
### "Why is my model scoring low?"
1. Check Experiment 6 (sub-task breakdown): Submission vs. checkpoint gap reveals whether the model investigates or guesses
2. Check domain-specific analysis: SQL errors (Excytin), KQL quality (CTI Realm), checkpoint progression (CyBench)
3. Check Experiment 9a (effort budget): Is the model running out of tool calls?
4. Check Experiment 12 (effort segmentation): Does the model fail on high-effort tasks?
### "Programmatic analysis without notebooks"
Use the `saber_analysis` library directly:
```python
import sys
sys.path.insert(0, "notebooks")
from saber_analysis import load_eval_logs, extract_cost_rows, setup_plotting
# Load data
overall_df, samples_df, subtasks_df = load_eval_logs(
eval_logs={'Model A': 'model_a.eval'},
log_dir='latest_experiments/excytin',
group_fn=lambda id: '_'.join(id.split('_')[:2]),
)
# Cost analysis
cost_rows = extract_cost_rows(
log_dict={'Model A': 'model_a.eval'},
log_dir='latest_experiments/excytin',
pricing={'anthropic/claude-sonnet-4-6': {'input': 3, 'output': 15, 'cache_read': 0.3, 'cache_write': 3.75}},
n_samples=599,
)
```