Skip to content

Running evaluations

Three ways to run a target: the admin UI, the ai-eval:run Drush command, and a scheduled CI job. All three execute the same run engine and write the same result row. This page is for whoever operates the evals.

From the UI

The Targets page at /admin/config/ai/ai-eval has a Run button on every enabled target. Pressing it starts a run and swaps the button for a progress bar.

The run advances one question per HTTP request, so a hundred-question dataset is a hundred small requests rather than one that times out. Progress and the error count update on each poll, and a Cancel button stops the run cleanly. A cancelled run is marked cancelled and writes no result row, so its partial scores are not recorded.

Two locks keep concurrent operators honest: a per-target lock at run start, and a per-run lock on each step. The same target cannot be started twice at once, and no question is graded twice.

stateDiagram-v2
    [*] --> pending: Run pressed
    pending --> running: first step
    running --> running: one question per poll
    running --> complete: last question graded
    running --> cancelled: Cancel pressed
    running --> error: run failed
    complete --> [*]: result row written
    cancelled --> [*]: no result row

Running from the UI needs the operate ai eval or administer ai eval permission.

From Drush

The command is ai-eval:run, alias aer. With no options it runs every enabled target.

# Every enabled target
drush ai-eval:run

# One target, or one question of one target
drush ai-eval:run --target=my_target
drush ai-eval:run --target=my_target --question=Q01

# List what would run, calling no provider
drush ai-eval:run --dry-run

# Also write a timestamped JSON results file
drush ai-eval:run --json

# Tag the run so cron runs are distinguishable from manual ones
drush ai-eval:run --source=cron

# Run every question as Drupal user 2
drush ai-eval:run --as-user=2

Options

Option Default What it does
--target all enabled targets Run only this target id. An unknown id is an error that lists the available ids.
--question all questions Run only this question id. Skips the sample-size advisory, since the run is a single question.
--split all Run only the train, validation, or test partition of the dataset. Any other value is an error. See datasets.
--source manual The run source tag stored on the result row.
--as-user none The Drupal uid used as caller for every question.
--dry-run off List targets and question counts, then exit. Ignores --split, see below.
--json off Also write a JSON results file.

Dry run

--dry-run resolves each target's dataset and prints the target id, the question count, and the dataset reference. It calls no AI provider, grades nothing, and writes no result row, so it costs nothing and is the cheap way to confirm that a target points at the dataset you think it does.

$ drush ai-eval:run --dry-run
DRY RUN: would evaluate these targets:
  my_target: 24 questions from my_dataset.yml

Warning

--dry-run ignores --split. The dry-run branch loads and counts the dataset before the split reaches the runner configuration, so --dry-run --split=test reports the whole dataset, not the test partition. The count it prints is the full question count in that case.

The JSON results file

--json writes one file per invocation, covering every target the run touched. It goes to the results_path set in configuration, or to the system temp directory plus /ai_eval_results when that setting is empty. The filename is results-YYYYmmdd-HHMMSS.json and the payload is the run timestamp plus one entry per target id. The path is printed at the end of the run.

This file is a raw dump of the run, not a portable artifact. For an artifact meant to travel between sites, see sharing results.

Tagging the run source

--source sets the source column on the result row, and nothing else. It defaults to manual, the UI writes ui, and a scheduled job should pass --source=cron. The tag is carried into exported envelopes as run_source, so "did this number come from a nightly job or from someone pressing Run" stays answerable after the fact.

Running as a specific user

--as-user=N sets the run's default caller uid. A question that carries its own caller_uid keeps it, so the option is a default rather than an override.

Warning

The option only has an effect if a host module registers a ai_eval.caller_context service implementing Drupal\ai_eval\Service\CallerContextInterface. AI Eval declares that dependency as optional and ships no implementation of its own. Without a host implementation, the uid is resolved and then handed to nothing: --as-user changes nothing about the run. It exists for sites whose agents resolve user-scoped state (per-user memory, per-user access) and that wire up the service themselves.

Advisories printed during a run

A run prints warnings that never change the verdict or the exit code:

  • The dataset is below the Cochran sample-size floor. See scoring.
  • The gate verdict rests on a judge that is untrusted, stale, or never validated. See judges.
  • The judge shares a model family with the evaluated model, so the scores are not an independent measurement.
  • The target runs in agent mode and no attached grader observed what the agent did, so every score is a reading of the response text. See graders.

Exit codes

Code Meaning
0 Every hard gate passed, or the run was a dry run.
1 A hard gate failed, or the command could not run: unknown target, no targets configured, an invalid --split, or a failed JSON write.

A soft gate that misses its threshold logs a warning and leaves the exit code at 0. Which targets are hard and which are soft is a per-target setting, covered in scoring.

In CI

Because a failed hard gate exits 1, a scheduled pipeline job can gate on eval health. AI Eval ships no cron integration on purpose: a full run can be hundreds of LLM calls, and when to spend that belongs to site operations.

# .gitlab-ci.yml, run from a scheduled pipeline
ai_eval_nightly:
  stage: test
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
  script:
    - drush ai-eval:run --source=cron --target=my_target --json
  artifacts:
    when: always
    paths:
      - sites/default/files/ai-eval-results/
    expire_in: 30 days

Point the paths entry at whatever results_path you configured, and create the schedule under CI/CD, Schedules in the project settings. A crontab line does the same job on a host with no pipeline:

0 2 * * * cd /var/www/html && drush ai-eval:run --source=cron

Two things are worth adding to a pipeline that gates on eval results. Run drush ai-eval:judges --json in the same job to record which judges were trusted at the time, since an eval result is only as good as the judges behind it; see judges. And export an envelope if you want the run's evidence to outlive the pipeline log; see sharing results.