lmnr-cli eval run. The fastest path is to let your coding agent build it with the Tracevell skill. If you would 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 Tracevell project:setup signs you in, writes LMNR_PROJECT_API_KEY to .env, and installs the Tracevell agent skill, which knows the evals folder layout.
Then prompt your coding agent. For example:
Set up agent evaluations for the refund flow with lmnr-cli eval run. Use our real database and seed each case from a production trace.
If you don’t have production traces yet, describe in plain words what you want to evaluate.
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 Tracevell.
Get started manually
This walkthrough evaluates a support agent that handles billing tickets. It looks up a customer’s charges in a SQLite database and can refund one. Each case starts with its own copy of the database, and the verifier checks two things: whether the right refund is in the database, and whether the reply to the customer is any good. An LLM judge grades the reply. The complete project isexamples/agent-evals in the lmnr-python repo. Clone it to follow along, or copy the files into your own repo as you go.
The agent is a single module, app/support_agent.py. It runs an Anthropic tool loop over two tools, list_charges and refund_charge. It calls Laminar.initialize() and wraps the tools in observe, so each trial’s trace shows the model calls and the tool calls. Trimmed to the parts the harness relies on:
app/support_agent.py
DB_PATH and handle_ticket, and the Harbor agent sets AGENT_MODEL. The app’s dependencies, anthropic and lmnr, are in the project’s pyproject.toml and uv.lock, which the image installs.
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 version of Harbor with the Tracevell plugin through uvx, so you don’t install Harbor yourself.From the repo root, link the directory to a Tracevell project. Skip this if you already ran it above:setup signs you in and writes LMNR_PROJECT_API_KEY to .env. See Tracevell 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 says 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
seed.json, imports the production function unchanged, calls it with the ticket, and writes the outcome to /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. It refers to the API key as a ${VAR} placeholder that lmnr-cli fills in when the run starts, so the key itself does not end up in a file you commit:evals/support/harbor.yaml
install() has nothing to do. It passes self.model_name on to the app, which lets --model override the model for a run:evals/support/agent/agent.py
5
Add a case
The task points at the image by name. Harbor copies everything in the task’s The
The verifier first checks the database, which is the hard evidence of what the agent did. It then asks an LLM judge whether the reply is acceptable. It writes one reward per check, and each becomes its own score in Tracevell. If the agent run itself failed, both rewards are
Make
environment/ folder into workdir (here /case) when the container starts:evals/support/tasks/duplicate-charge/task.toml
[verifier.env] block gives the verifier the key it needs to call the LLM judge.evals/support/tasks/duplicate-charge/instruction.md
evals/support/tasks/duplicate-charge/environment/case.json
environment/seed.json: the database rows for this case
environment/seed.json: the database rows for this case
evals/support/tasks/duplicate-charge/environment/seed.json
evals/support/tasks/duplicate-charge/tests/expected.json
0 and the judge is skipped:evals/support/tasks/duplicate-charge/tests/test.sh
tests/verify.py: the database check and the LLM judge
tests/verify.py: the database check and the LLM judge
evals/support/tasks/duplicate-charge/tests/verify.py
test.sh executable with chmod +x.To add another case, copy the duplicate-charge directory, then change case.json and seed.json to describe a different ticket and account, and change expected.json to match. For example, a customer asking for a refund on a charge that should be declined expects "refund_count": 0.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} placeholder from your shell first, then from .env.local and .env. If one is missing, it stops before anything starts.Keep secrets and Harbor’s job output out of git:.gitignore
7
Run the evals
From the repo root:
lmnr-cli builds refund-agent-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 a
refund score and a reply score. Because the app calls Laminar.initialize(), the trial’s trace shows the model calls and the list_charges / refund_charge tool calls 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.json and test-stdout.txt, which includes the judge’s reasoning).Next steps
Run evals with lmnr-cli
Filter suites and tasks, run trials in Tracevell 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 module 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.