| name | inspect-eval-execution |
| description | Guide for running SABER inspect_ai evaluations locally. Use this when asked to run, re-run, or configure an inspect eval for any SABER domain. |
To run SABER inspect_ai evaluations locally, follow this process:
1. Understand the Domain Structure
Each domain lives in domains/<domain>/ and follows this layout:
domains/<domain>/
<domain>.py # @task entry point โ calls create_task()
eval.yaml # Domain metadata, Docker image definitions
tasks/
global.yaml # Default prompts, tools, max_steps, aggregation
<task_group>/
<task>.yaml # Individual task definitions
compose/
sandbox.compose.yml # Docker Compose for sandbox containers
scoring/ # Domain-specific scoring strategies
tools/ # Domain-specific MCP tools
prompts/ # Jinja2 prompt templates (instructions/, assistants/, etc.)
docker/ # Dockerfiles for sandbox images
The <domain>.py file is minimal โ it just calls saber.task.create_task(**kwargs) which wires up config loading, prompt rendering, scoring, tools, and the agent.
2. Build Docker Images First
Before running evals, ensure Docker images are built:
uv run saber build <domain>
uv run inspect eval domains/<domain> --model <model> --display plain -T build=true
uv run inspect eval domains/<domain> --model <model> --display plain -T rebuild_all=true
uv run inspect eval domains/<domain> --model <model> --display plain -T rebuild=sandbox
3. Run an Evaluation
Always use --display plain to disable the Rich interactive progress display.
This outputs clean, parseable text instead of ANSI escape sequences and cursor
manipulation that pollute AI agent context windows.
uv run inspect eval domains/<domain> --model <model> --display plain
uv run inspect eval domains/<domain> --model <model> --display plain --limit 1
uv run inspect eval domains/<domain> --model <model> --display plain -T task_filter="sanity_*"
uv run inspect eval domains/<domain> --model <model> --display plain -T task_filter="incident_response_01"
uv run inspect eval domains/<domain> --model <model> --display plain -T task_filter="sanity_*,advanced_*"
3.1 Permanent Environments
It is generally recommended if you expect to do multiround eval analysis to use "-T keep_permanent=true" in
your uv run inspect command. This keeps long to spin up resources like databases around for ease of use
on following evals.
If you do this, use
uv run saber teardown -y
To teardown any assets when you are finished
4. Key -T Parameters
| Parameter | Purpose | Example |
|---|
task_filter | Glob pattern to select tasks | -T task_filter="sanity_*" |
build=true | Build missing Docker images | -T build=true |
rebuild_all=true | Rebuild ALL Docker images | -T rebuild_all=true |
rebuild=<prefix> | Rebuild images matching prefix | -T rebuild=sandbox |
agent=<name> | Agent implementation | -T agent=react |
run_preflight=true | Health check before eval | -T run_preflight=true |
keep_permanent=true | Keep permanent services alive | -T keep_permanent=true |
5. Control Concurrency and Limits
uv run inspect eval domains/<domain> --model <model> --display plain --max-samples 4
uv run inspect eval domains/<domain> --model <model> --display plain --max-connections 20
uv run inspect eval domains/<domain> --model <model> --display plain --limit 1 --max-samples 1
5b. Reasoning models โ set --reasoning-effort explicitly (IMPORTANT)
Reasoning models (gpt-5.x, o1/o3, Claude Opus) do NO reasoning by default in these
runs: reasoning_effort is unset, the model emits 0 reasoning tokens, and scores land
noticeably lower. Always pass it explicitly โ especially for cross-run / cross-model
comparisons:
uv run inspect eval domains/<domain> --model <model> --reasoning-effort high --display plain
Verified impact (excytin, gpt-5.4, latest_test_set, 599 samples): reasoning=high
scored 0.886 vs 0.813 with reasoning unset โ a +0.073 aggregate swing, entirely
from reasoning, with the largest lift on intermediate checkpoints (the multi-step
investigation). The effort used is recorded in the eval-log header
(model_generate_config.reasoning_effort); stats.model_usage.*.reasoning_tokens == 0
means no reasoning happened. --max-tokens must be large enough to cover reasoning +
answer or completions get truncated.
6. Enable Verbose Logging
INSPECT_LOG_LEVEL=info uv run inspect eval domains/<domain> --model <model> --display plain
INSPECT_LOG_LEVEL=debug uv run inspect eval domains/<domain> --model <model> --display plain
INSPECT_PY_LOGGER_FILE=/tmp/eval_debug.log uv run inspect eval domains/<domain> --model <model> --display plain
7. Iterative Development Workflow
When developing or debugging a domain:
- Start with
--limit 1 to run a single sample quickly
- Use
task_filter to isolate the specific task you're working on
- Pipe output to capture both stdout and stderr:
2>&1 | tee /tmp/eval_output.log
- Check the eval log โ the path is printed at the end:
Log: logs/<timestamp>_<domain>_<id>.eval
- Iterate on scoring/tools โ changes to domain code in
domains/<domain>/ take effect immediately (no reinstall needed)
- Changes to
saber or inspect_ai โ if installed as editable, take effect immediately; if installed from git, you must edit the files in .venv/lib/python3.11/site-packages/ directly and clear __pycache__ dirs
8. Understand the Score Output
The eval summary shows scores organized by task and scorer:
saber_overall task_name_1 task_name_2
mean 0.500 accuracy 1.000 accuracy 0.000
stderr 0.000 stderr 0.000 stderr 0.000
saber_overall is the aggregate across all tasks
- Each task shows its
accuracy (the final aggregated score)
nan means the scorer did not produce a result (e.g., the agent didn't reach that evaluation point)
- Multiple scorers per task (e.g.,
submission, crash_analysis, root_cause) are aggregated per the task's scoring_aggregation config
9. Common Issues