lmnr-cli eval run. The fastest path is to let your coding agent build it with the Laminar skill. If you’d rather see every file, follow Get started manually below.
Set up with your coding agent
You need Docker, uv, and Node.js. From the repo root, link the directory to a Laminar project:setup signs you in, writes LMNR_PROJECT_API_KEY to .env, and installs the Laminar agent skill, which knows the evals folder layout. In Claude Code, Cursor, or Codex, ask for the suite directly:
Set up agent evaluations for the refund flow with lmnr-cli eval run. Use our real Postgres and seed each case from a production trace.
The skill follows the same guidance as these docs: it finds the entry point, builds one image, seeds per-case data, writes the verifier, and runs the suite locally. Review what it wrote against Build a production harness, then open the run in Laminar.
Get started manually
This walkthrough evaluates a small support-ticket triage function: it sends a ticket to Claude and returns a category. You’ll package it into an image, add a case, and run it. The same layout scales to an app with a database and several services; Build a production harness covers that. The app is one module, instrumented with Laminar:app/triage.py
anthropic and lmnr in requirements.txt.
1
Install the prerequisites and link the project
You need Docker, uv, and Node.js for
lmnr-cli. lmnr-cli eval run runs a pinned Harbor version with the Laminar plugin through uvx, so you don’t install Harbor yourself.From the repo root, link the directory to a Laminar project. Skip this if you already ran it above:setup signs you in and writes LMNR_PROJECT_API_KEY to .env. See Laminar CLI for details.2
Lay out the evals folder
Create this structure next to your code:
3
Describe the image
lmnr.yaml names the image and how to build it. Paths are relative to evals/, and context: .. makes the repo root the build context, so the image is built from your live source:evals/lmnr.yaml
evals/image/Dockerfile
/logs/agent/result.json:evals/image/shared/run_case.py
4
Write the suite config and the agent
harbor.yaml is a Harbor job config. Secrets are ${VAR} placeholders, never literal values:evals/support/harbor.yaml
install() does nothing. It passes self.model_name on so --model can override the model for a run:evals/support/agent/agent.py
5
Add a case
The task points at the image by name and says where its The reference answer and the verifier live in Make the verifier executable with
environment/ files go:evals/support/tasks/refund-double-charge/task.toml
evals/support/tasks/refund-double-charge/instruction.md
evals/support/tasks/refund-double-charge/environment/case.json
tests/, which Harbor uploads only when the verifier runs, so the app never sees them:evals/support/tasks/refund-double-charge/tests/expected.json
evals/support/tasks/refund-double-charge/tests/test.sh
chmod +x. A second case is a copy of this directory with a different case.json and expected.json.6
Add secrets
Put
ANTHROPIC_API_KEY in .env at the repo root, next to the LMNR_PROJECT_API_KEY that setup wrote. lmnr-cli fills every ${VAR} from your shell, then .env.local / .env, and stops before anything starts if one is missing.Keep secrets and Harbor’s job output out of git:.gitignore
7
Run the evals
From the repo root:
lmnr-cli builds triage-evals with your Docker, runs every task in every suite, and prints a link to the results. The support suite becomes an evaluation group in your project, and this run is a new evaluation in it.8
Read the results
Open the evaluation. Each trial is a datapoint with its reward as the score. Because the app calls
Laminar.initialize(), the trial’s trace shows the Claude call under the agent span as a transcript, so a 0 takes one click to explain.Locally, Harbor’s raw output is in evals/support/jobs/<timestamp>/: each trial has agent/ (your result.json and logs) and verifier/ (reward.txt, test-stdout.txt).Next steps
Run evals with lmnr-cli
Filter suites and tasks, run trials in Laminar sandboxes, and wire it into CI.
The evals folder
Reference for
lmnr.yaml, harbor.yaml, task.toml, and secrets.Build a production harness
Grow this single function into a full stack with services and seeded data.
Read a failing trial
Open a low-scoring datapoint and read the app’s run as a transcript.