Skip to main content
Evals live in your test suite. There’s no separate framework to learn: toPassEval() is an expectation like any other, and the recording, baselines, and dashboard happen behind it.
tests/Evals/SupportBotTest.php

The subject

expect(...) accepts a Laravel AI agent class-string, an agent instance, or a Closure(Row $row): AgentResponse — the closure escape hatch lets you evaluate anything that can produce an AgentResponse.

The $eval builder

Skip semantics

Evals call real models, so they never run by accident:
Terminal
The --evals flag and PEST_EVALS variable are the same contract used by pestphp/pest-plugin-evals, so the two plugins coexist cleanly in one suite — whichever consumes the flag, both see eval mode. See Vizra Evals vs pest-plugin-evals.

What failure looks like

A failed gate fails the test with the receipts — aggregate score and pass rate, the run id (look it up in the dashboard), regressed rows with before → after scores, and the worst failing rows with sample counts. A run where every sample errored always fails, whatever the gate says.

Suites and recording

Each toPassEval test records under a suite named after the test (e.g. pest: answers support questions from documented policy) — that’s the identity baselines and history attach to. Rename the test and you start a fresh history, so pin it with ->suite('support-quality') if you expect descriptions to churn.
Group your evals (->group('evals') or a tests/Evals directory) so you can run them in a dedicated CI job with its own schedule and budget — see Running in CI.