Eval format

Writing a suite of your own.

A suite is JSON.

{
  "name": "Checkout assistant",
  "description": "Prompts from the live product, sampled over a week.",
  "model": "gpt-4o-mini",
  "prompts": [
    {
      "id": "refund-policy",
      "category": "fact",
      "prompt": "What is our refund window?",
      "expect": { "contains": ["30 days"], "maxWords": 80 },
    },
  ],
}

Suite fields

Field
nameShown in the report
descriptionFor your own benefit
modelDefault model. --model overrides it
evaluatorPath to a module exporting a custom evaluator
promptsAt least one prompt

Prompt fields

Field
idUnique. Shown in under-allocation output
categoryGroups the per-category breakdown. Default general
promptShorthand for a single user message
messagesFull message list, when a system prompt or prior turns matter
expectDeterministic assertions
judgeA rubric for the optional LLM judge
skipSkip without deleting

Use messages when conversation state is part of what you are testing:

{
  "id": "followup-expand",
  "category": "conversation",
  "messages": [
    { "role": "user", "content": "What is a monad?" },
    {
      "role": "assistant",
      "content": "A way of sequencing computations that carry extra context.",
    },
    { "role": "user", "content": "tell me more" },
  ],
  "expect": { "minWords": 60 },
}

Assertions

Key
containsAll of these substrings must appear (case-insensitive)
containsAnyAt least one must appear
excludesNone may appear
regexThe answer must match
minWords, maxWordsWord-count bounds
jsonMust parse as JSON, with or without a code fence
codeMust contain a fenced code block
minItemsMust contain at least this many list items

A prompt with no assertions is still evaluated for emptiness and truncation.

Writing good assertions

Assert the floor, not the shape. minWords: 300 on a research prompt catches under-allocation. maxWords: 320 on the same prompt fails on a good answer.

contains and containsAny differ in how many entries must match. Every string in contains must appear. At least one string in containsAny must appear. Use containsAny to accept alternatives:

"expect": { "containsAny": ["Canberra", "canberra"] }

Check the fact, not the phrasing. ["Canberra"] passes whatever sentence the model builds around it. ["The capital of Australia is Canberra"] fails on any rewording, which reports a truncation problem that is not there.

Use code: true for anything that must ship code.

Keep rubrics for what deterministic checks cannot see. Every prompt with a judge rubric costs an extra model call on every run.

Regenerating the built-in suite

pnpm --filter @meted/eval build
node packages/eval/scripts/emit-builtin.mjs

packages/eval/src/builtin.ts is the source of truth; evals/builtin.json is generated from it.

Was this page helpful?