Skip to navigation

Eval Datasets

View as Markdown

Everything a live evaluation runs on lives in one directory: evals/ inside your skill. This page is the contract for that directory — what goes in it, the dataset format tier3 evaluate reads, and how to generate and refine cases. You can generate a starter dataset with no API key; LLM-backed generation and refinement need a configured provider (see Providers & Credentials).

Tier 3 executes these cases through Harbor, the open-source agent evaluation framework. Native Harbor task bundles are the bring-your-own-task alternative to the evals.json dataset described below.

The evals/ directory

SkillEvaluator recognizes this tree inside your skill:

my-skill
SKILL.md
scripts
requirements.txt
apt-packages.txt
evals
evals.json
config.yml
EVAL.md
files
environment
Dockerfile
docker-compose.yaml
mcp_servers.toml
harbor
grader.py
results

Only the dataset is required. Everything else is optional and additive:

PathPurposeCommit it?
evals/evals.jsonThe eval dataset. Without it, live evaluation cannot run.Yes — required
evals/config.ymlRun policy: attempts, thresholds, runtime env, agent models, grading mode.Yes
evals/EVAL.mdDeveloper guidance that steers dataset generation.Yes
evals/files/Input fixtures, staged into /workspace/input/ in the container.Yes
evals/environment/Custom container environment: Dockerfile, sidecars, MCP servers.Yes
evals/harbor/Bring-your-own-task native Harbor tasks — see Custom Graders & Tasks.Yes
evals/grader.py or evals/grader.shBring-your-own-grader script — see Custom Graders & Tasks.Yes
evals/results/Run output, written by tier3 evaluate. Never author it by hand.No — output only

Unrecognized files in evals/ are ignored, and the validator warns about them so typos don’t silently drop a fixture.

Dataset format

New datasets use the agentskills.io shape: a top-level object with skill_name and an evals array.

evals/evals.json
{
"skill_name": "my-skill",
"evals": [
{
"id": "my-skill-001",
"prompt": "Use my-skill to do the thing",
"expected_output": "Expected outcome description",
"assertions": [
"Read my-skill/SKILL.md",
"Returned correct result"
]
}
]
}
FieldRequiredMeaning
skill_nameYesName of the skill under evaluation.
evalsYesArray of eval cases.
idYesUnique case ID. Start with a letter or digit; ASCII letters, digits, ., _, - only; at most 128 characters. Integer IDs are accepted and canonicalized to strings.
promptYesThe user request the agent receives.
expected_outputYesOutcome-oriented description of what a successful agent produced.
assertionsNoObservable agent behaviors to verify, one per entry.
expected_skillNoSkill the agent should route to; null for negative cases.
expected_scriptNoScript the agent should run; null if none applies.
acceptable_skillsNoClosely related skills that earn partial routing credit (legacy alias: acceptable_alternates).

The dataset file can be named evals.json, evals.jsonl, evals.yaml, or evals.yml — tier3 evaluate accepts all four.

Older datasets are a flat array of entries with question instead of prompt. They still run, but the validator emits a deprecation warning — migrate to the agentskills.io shape for new work.

Legacy fieldagentskills.io equivalent
question (required, with id)prompt
ground_truth (recommended)expected_output
expected_behavior (recommended)assertions
expected_skill (recommended)expected_skill (unchanged)
acceptable_alternatesacceptable_skills

Generate a dataset

create-eval-dataset writes evals/evals.json for you, grounded in your SKILL.md name, description, body, and script filenames:

Generate the full bucket dataset
skillevaluator create-eval-dataset ./my-skill --full
FlagEffect
--fullGenerate the full bucket set instead of one. LLM mode targets four cases; --no-llm template mode emits three cases unless EVAL.md includes a negative prompt.
--no-llmUse local templates only — no API key required.
--dry-runPreview without writing.
--forceOverwrite existing evals/evals.json.
--prompt PATHUse a developer guidance file instead of auto-detected evals/EVAL.md.
--refineRefine cases using existing or collected trajectories.
--from-results PATHWith --refine: read trajectories from a specific results directory.
--results-dir DIRECTORYWith --refine: external results root to search first.

Without an LLM provider configured, generation automatically falls back to the same templates --no-llm uses, so the command always succeeds. Exact defaults for every flag are in the CLI Reference.

You don’t have to generate a dataset up front at all: validate PATH, tier3 PATH, or tier3 evaluate PATH --autopilot creates exactly one eval case when no evaluation source exists — LLM-generated with your configured provider, falling back to a deterministic template case when no key is available. Autopilot never overwrites an existing source; if evals/evals.json (or evals/harbor/) is already there, it runs with what you wrote. Dataset generation does not replace the provider and runtime requirements for the live evaluation. Use validate PATH --no-autopilot to require an existing source instead of generating one.

--force overwrites evals/evals.json without a backup. --refine implies overwrite too — it produces the final version of the dataset.

The four case buckets

--full with an LLM provider targets one case per bucket, including a negative case that should not trigger the skill. In --no-llm template mode, the negative bucket is omitted unless eval guidance includes an off-skill prompt under a supported negative heading (see below).

BucketIntent
ExplicitThe user names the skill directly.
ImplicitThe user describes the task without naming the skill.
ContextualThe task appears inside a realistic project scenario.
NegativeA request that should not activate the skill.

Guide generation with EVAL.md

Place an EVAL.md in evals/ and the generator picks it up automatically. Four headings are parsed as structured hints; everything else is general context:

evals/EVAL.md
## Questions
- Sample user prompts to use as eval questions
## Behaviors
- Expected agent behaviors to verify
## Negative Cases
- Off-skill prompts for the negative bucket in template `--full` mode
## Notes
- General context and constraints

Author guidance is the highest-priority input: behaviors you list here are preserved in the generated cases even when a trial agent skipped them — that is exactly the regression the eval should catch.

Refine from real runs

--refine grounds the dataset in real agent behavior. It looks for existing trajectories from a previous evaluation (checking --from-results or the latest resolved results first); if none exist, it runs the claude-code agent via Harbor to collect one, then rewrites expected_output and assertions from what the agent actually did:

Agent-refined generation
skillevaluator create-eval-dataset ./my-skill --full --refine

Collecting a fresh trajectory needs Docker, the tier3 extra, and a configured provider. Refining from existing results does not need the tier3 extra: case IDs are read from each trial’s reward.json or Harbor result.json, and multimodal trajectory messages are read as text, with image and audio parts shown as [image] and [audio] markers. With --no-llm, refinement still works — it applies template-based updates from the trajectory instead of an LLM rewrite.

Run policy: evals/config.yml

The dataset says what to evaluate; evals/config.yml (or config.yaml) says how to run it. CLI flags always override config values.

evals/config.yml
schema_version: 1
harbor:
task_source: auto # auto | evals_json | native_harbor
custom_dockerfile_mode: preserve # preserve | rebase
n_attempts: 3
pass_threshold: 0.50
n_concurrent: 2
timeout_multiplier: 1.5
resources:
cpus: 2
memory_mb: 4096
runtime_env:
- SERVICE_API_TOKEN # expands to ${SERVICE_API_TOKEN} at run time
pre_agent_setup: # with-skill-only runs (--skip-baseline)
- service-cli auth login --token "$SERVICE_API_TOKEN"
- service-cli whoami >/dev/null
agents:
claude-code:
model: your-model-id # any model your agent CLI accepts
skill_workspace:
mode: isolated # isolated | group
include: []
grading:
mode: default # default | default_plus_custom | custom_only
KeyAcceptsEffect
schema_version1Required. Only version 1 is supported.
harbor.task_sourceauto, evals_json, native_harborWhere cases come from: auto-detect, the eval dataset, or native Harbor tasks.
harbor.custom_dockerfile_modepreserve, rebaseHow your custom Dockerfile is handled.
harbor.base_image_modereuse, rebuild, disabledShared pre-built eval base image (Docker env mode only): reuse it across tasks, force a rebuild, or ship self-contained per-task Dockerfiles. Default: disabled.
harbor.n_attemptsinteger ≥ 1Attempts per case (drives pass@k).
harbor.pass_threshold0.0–1.0Minimum score for an attempt to count as a pass.
harbor.stop_on_passbooleanStop a case’s remaining attempts after the first pass. Requires n_attempts > 1.
harbor.n_concurrentinteger ≥ 1Concurrent trials.
harbor.max_agentsinteger ≥ 1Cap on agents evaluated in one run.
harbor.max_casesinteger ≥ 1Run only the first N cases in staged order (eval dataset order, or task-directory name order for native Harbor tasks). Unset runs every case. A run that skips cases is not publication evidence. The --max-cases CLI flag overrides this value.
harbor.timeout_multiplierfinite number > 0Scales task timeouts. Values that would overflow Harbor’s mandatory lifecycle timeouts are rejected before execution.
harbor.agent_runtime_preflightbooleanOpt-in extra agent-only execution of the first staged task before the measured evaluation. Default: false; the CLI flag overrides this value.
harbor.agent_workdirstringWorking directory for the agent inside the container.
harbor.resourcescpus, memory_mb, storage_mbPer-container resource requests.
harbor.runtime_envlist or mappingNon-credential task values passed into the container. Prefer a list of plain names — each expands to ${NAME} from your shell; a mapping sets explicit templates. Entries that name or reference operator-owned credentials (OPENAI_API_KEY, ANTHROPIC_API_KEY, NVIDIA_API_KEY, base-URL variables, AWS credential variables) fail with a hard error. Alias: passthrough_env.
harbor.pre_agent_setupstring or listShell commands run in the container before the agent starts. This skill-owned code is allowed only for with-skill-only runs (--skip-baseline); paired runs reject it so setup cannot distinguish or alter the baseline arm. Alias: setup_commands. Both spellings normalize to the same policy.
harbor.agents.<name>.modelstringPer-agent model override; whitespace is trimmed. claude is accepted as an alias of claude-code, but configuring both spellings in one config is an error.
skill_workspace.modeisolated, groupWhether sibling skills are visible — see Agents & Sandboxes.
skill_workspace.includelist of pathsExtra skills staged into a group workspace.
skill_workspace.decoy_manifestpathOptional frozen selection from tier3 select-decoys; requires group mode. Relative paths resolve against the target skill’s parent directory, as with include. Selected runtime-content hashes are checked before evaluation; evaluator-only files do not affect them. See Workspace modes.
grading.modedefault, default_plus_custom, custom_onlyGrading pipeline — see Custom Graders & Tasks. Legacy spellings aces_default and aces_plus_custom are accepted and normalized to default and default_plus_custom.

Environment constructor kwargs are deliberately not accepted in this skill-owned file. Operators can pass non-secret kwargs for Harbor-native non-Docker backends with repeatable --environment-kwarg KEY=VALUE / --ek KEY=VALUE CLI flags. Backend credentials stay in the host environment; runtime-policy, mount, network, lifecycle, and pod-security fields are reserved.

Unknown keys are rejected, not ignored — a typo like n_attemps fails the config load instead of silently running with defaults. Use only one of each alias pair. Agent and provider credentials never go through runtime_env — they come only from your host environment; see the two-credential-roles section in Providers & Credentials.

Fixtures and custom environments

Two optional directories shape the world the agent works in.

evals/files/ holds input fixtures — test data, configs, sample documents. An eval entry’s files string or list selects only those paths (relative to evals/) for its /workspace/input/. An explicit empty list or null stages no fixture. For backward compatibility, an entry that omits files receives the entire shared evals/files/ directory.

evals/environment/ customizes the container itself:

FileRules
DockerfileAt most 20 KB; the first non-comment line must be FROM. The build context is environment/ itself, so COPY paths are relative to it (COPY skills/, not COPY environment/skills/). If absent, an image is auto-generated from scripts/requirements.txt and scripts/apt-packages.txt on a python:3.12-slim base.
docker-compose.yamlSidecar services only (databases, APIs, mock servers) launched alongside the agent container. Not supported with --env-mode kata, where services cannot resolve each other by name, or with --env-mode mosaic, which runs one VM per trial. A strict allowlist applies: host port mappings are removed, and host mounts, privileged access, and unrecognized keys are rejected. Also accepted as docker-compose.yml.
mcp_servers.tomlMCP servers injected into the container. TOML array-of-tables [[mcp_servers]]; each entry needs name plus url or command. Command-based servers use transport = "stdio" (auto-inferred); URL-based servers default to sse.
SubdirectoriesCopied into the container environment (config files, seed data for sidecars). Any name except __pycache__ and .git.
evals/environment/mcp_servers.toml
[[mcp_servers]]
name = "my-api"
url = "http://localhost:8080"
[[mcp_servers]]
name = "my-tool"
command = "python /workspace/tools/server.py"
transport = "stdio"

In Docker mode, Claude Code and Codex setup no longer installs the distribution nodejs and npm packages, except on Alpine images. On Debian-based images, including the default python:3.12-slim image, those packages also brought npx and a C/C++ toolchain (gcc, g++, make). Claude Code containers now have no Node.js unless the image provides it. Codex installs its own Node.js, which is on PATH only for the Codex process and the commands it runs. If your skill’s scripts, its tasks, or a stdio MCP server (for example command = "npx ...") need these tools, install them in the image: list the packages in scripts/apt-packages.txt, one per line, for the auto-generated image, or install them in evals/environment/Dockerfile.

my-skill/scripts/apt-packages.txt
nodejs
npm
build-essential

Validate before you run

Check the whole contract — dataset fields, case IDs, config.yml, Dockerfile rules, MCP declarations — without launching anything:

Validate the evals/ contract
skillevaluator tier3 validate ./my-skill
FlagEffect
--jsonEmit JSON output.
--strictTreat warnings as failures.
--harbor-contractValidate the Harbor task and reward contract for BYOT/BYOG setups — see Custom Graders & Tasks.

Run it in CI or before a long evaluation: it catches an empty dataset, missing required fields, duplicate case IDs, and invalid config in seconds.

validate PATH also checks the selected Tier 3 source (dataset entries and case IDs, config.yml, or the native Harbor task layout) before any live run. When that check fails, each error names the entry or file and what is wrong, for example entry case-3: Missing required fields: question:

  • The terminal lists the errors under the Tier 3 finding when the full CLI report prints (-r cli or --verbose), otherwise in the compact Tier 3 panel. When the run also writes an HTML or JSON report, the terminal shows the first five and names that report for the rest. When it writes neither (for example -r cli alone, or --verbose without -r), the terminal lists every error.
  • The HTML report shows the exact error on the Tier 3 dashboard when there is one, or the error count when there are several. Full Insights → Source Validation Errors lists every error in order.
  • The JSON report keeps every error under tier3.provenance.source_validation.

Passing checks and warnings are not listed as source errors.

Next steps