Eval Studio
Check how your agent behaves and pick the best model before you ship a change.
The Eval Studio answers two questions in the dashboard:
- Does my agent behave? It should use the right tool, in the right order, and actually answer instead of promising to answer later.
- Which model should it run on? The same cases are replayed across candidate models, and ranked by what they did.
You will find it on an agent's Improve tab. The model dialog's Find best model button runs the same engine against a shortlist of candidates.
Both entry points work the same way. Run all cases and Find best model each create a
run and open it at /improve/runs/:id, so the run has a URL that survives a reload and can
be shared. The studio keeps showing the agent's latest run inline, with a link back to that
page.
There is no CLI. Evals are product objects, stored per agent and edited in the dashboard.
How a case runs
A case runs as a real turn through the agent, with its own instructions, capabilities, delegation, step budgets and tracing. Only two things change:
- the model, so every candidate faces the same cases;
- the tool bodies, which are mocked.
Eval turns run in a playground. Outbound messages are blocked, usage is not billed, and the run stays out of your conversations, search, analytics and org-wide traces. The trace is kept, and every result links into the trace viewer.
Mock and live worlds
| World | Tool bodies | When to use it |
|---|---|---|
mock | The case's canned replies | Comparing models. It is deterministic, while a live run partly measures which candidate got the flakier page. |
live | The agent's real capabilities (scraping, sandbox, browser, MCP) | Proving the capabilities themselves work. Too noisy to rank models with. |
A mocked tool can also fail on purpose (fails), fail only the first few calls
(failTimes), or take time (latencyMs). That is how the recovery cases and the "is it
honest when a tool never works" cases are written.
Checks
Every check renders as its own pass or fail row, with the reason. A single score of 0.62 is not something you can act on.
| Check | What it means |
|---|---|
completes | The turn ended with a reply. |
replies | How many messages the turn may answer with. 0 forbids a reply. Messages a tool delivered on purpose are not counted. |
calls | Tool to exact call count. 0 forbids it. |
callsAtLeast | Tool to minimum calls, since retries make the exact number vary. |
callsAtMost | Tool to maximum calls. The case does not care how the agent gets there, only that it stops. |
order | These tools, in this relative order. |
mentions | Text the reply must contain, compared without separators, so 1,048,552 matches 1048552. |
absent | Text it must not contain, such as stall phrases or leaked notes. |
noMarkup | The reply must be plain text, with no Markdown that a channel would show literally. |
noLists | The reply must not be laid out as a list, which is normal in an email but reads as a form in a chat. |
judge | Open-ended answers, graded 0 to 1 by a fixed cheap model against your criteria. |
effects | State the turn had to leave behind. A promised digest that was never scheduled fails. |
code | A code task, in a live world only: seed a repo, apply the agent's patch, run its tests. |
Tool names are qualified by subagent (web_scraper.scrape_page) whenever the delegation
layer is on, so a case can tell "the scraper subagent scraped" apart from "the orchestrator
scraped it itself".
A case passes only when every check holds. The fractional score exists to rank candidates, not to call a failure a pass.
Code tasks
A code check asks whether the agent can actually fix a repo, which no substring or judge
check can. It only runs in a live world, and turns the sandbox on for the case.
- Before the turn, the case's repository is seeded into a fresh git repo in the case's sandbox, with one baseline commit.
- The turn runs as usual. The case's prompt asks the agent to inspect the repo and reply with a unified diff.
- Afterwards, the working tree is reset to that baseline, so only the patch the agent answered with counts, not anything it edited directly during the turn. The patch is applied, and the repo's test command runs. A zero exit passes the check.
A reply with no patch, a patch that does not apply, or a failing test each fail the check, with the reason shown inline.
Generating cases
When you open the studio for an agent that has no cases yet, Keiki drafts a suite from that agent's own spec. It writes a tool case, a repeat case, a compound lookup case, a self-heal case, a failing-tool case and a conversation-context case, plus channel-conformance and onboarding cases. Generation is a model call. There is no generic starter suite, and nothing is seeded when you read.
The cases are ordinary rows. Edit them, delete them, or add your own.
Model sweeps
Starting a run with candidates runs a sweep. Candidates and cases share one pool, with a
bounded number of turns in flight overall and per vendor, so the measurement is the models
and not a rate limiter. Sandbox cases have their own smaller limit.
Ranking is lexicographic, not blended: pass rate, then mean score, then judge score, then
real cost, then median latency. A model that fails a delegation case is not saved by being
cheap. Cost comes from the provider's own accounting of what was spent, not catalog pricing
times tokens, and unknown cost stays unknown rather than showing as $0.
Keiki never picks a winner for you. The ranking has a Use this model button, and that is the only thing that changes the agent.
Limits
- A paid plan is required. A plan-less organization has its tools stripped and its model forced, so every candidate would score the same.
- One run per agent at a time, and at most 20 candidates per run.
- A run gives up after 15 minutes. Stop cancels it between cases.
API
| Endpoint | What it does |
|---|---|
GET /api/v1/evals/cases?agent_id= | The agent's cases. Empty until you generate them. |
POST/PUT/DELETE /api/v1/evals/cases[/:id] | Write or edit a case. |
POST /api/v1/evals/cases/generate | Draft a suite from the agent's spec. |
GET /api/v1/evals/cases/versions?agent_id= | Case revision history. |
GET /api/v1/evals/suggestions, POST /api/v1/evals/suggestions/create | Suggested cases from your conversations. |
POST /api/v1/evals/runs | Start a run. Leave out candidates to use the agent's own model. |
GET /api/v1/evals/runs/:id | Poll a run. Rankings and per-case results arrive as they land. |
GET /api/v1/evals/runs/:id/cases, GET /api/v1/evals/runs/:id/transcript | Per-case results and the turn transcript. |
GET /api/v1/evals/runs/latest?agent_id=&kind= | The most recent run, for reopening the studio. |
POST /api/v1/evals/runs/:id/cancel | Cancel a run. |
POST /api/v1/evals/diffs, GET /api/v1/evals/diffs | Compare two runs, and read a comparison. |
These routes use a session cookie and are scoped to your organization. The driver surface
under /api/v1/evals/channel/* is separate. See
External-channel evals.