Skip to main content
Evaluation is the core feedback loop of PromptOps. A weak eval gives you false confidence. A strong eval tells you immediately when a prompt change breaks something — and exactly what broke. This guide covers how to design test cases, choose the right assertions, write good judge rubrics, and build an eval practice that improves over time.

The eval maturity ladder

Start at Level 1 and move up as your prompt matures. Most teams get 80% of the value from deterministic checks alone — don’t skip ahead to AI grading before you have solid deterministic coverage.
Most teams get enormous value from 10–20 deterministic checks before they ever need AI grading. Start at Level 1.

Two evaluation modes

Apastra supports two modes. Use whichever fits your situation:
A single YAML file in promptops/evals/ that combines prompt, cases, and assertions. Best for smoke tests and rapid iteration.
The agent reads this file and internally treats it as a prompt spec + dataset + inline assertions + suite. No other files needed.

Designing test cases

Start from real failures, not hypotheticals

The most valuable test cases come from actual bad outputs your prompt has produced. Every time a prompt fails in production or review, turn that case into a test. If you don’t have failures yet, try the prompt with adversarial inputs and edge cases to find them before your users do.

Break your prompt into features and scenarios

Decompose what your prompt does into discrete capabilities. Write separate test cases for each capability. For a “classify email” prompt, that means:
  • Correctly classifies obvious spam
  • Handles ambiguous emails (could be sales or support)
  • Returns valid JSON
  • Doesn’t expose internal IDs or metadata
  • Handles empty input gracefully

Cover all five categories

Prioritize volume over perfection

50 cases with automated grading is more valuable than 10 perfectly curated cases with careful human review. You can always improve case quality later — you can’t retroactively add coverage. Ask your IDE agent: “Generate 20 test cases for this prompt, including edge cases and adversarial inputs.” Review and curate the results — don’t blindly trust synthetic data.

Dataset format (JSONL)

Dataset files are .jsonl files — one JSON object per line. Each case has a stable case_id and an inputs object:
You can mix expected_outputs (used by suite evaluators) with inline assert blocks (applied per-case). Both apply when present.

Inline assertions on dataset cases

Put assertions directly on a case instead of a separate evaluator file:
This is ideal for case-specific checks. Evaluator files handle suite-wide scoring; inline assertions handle per-case specifics.

Choosing the right assertion type

Deterministic assertions

These are fast, free, and deterministic. Use them for every check where the output can be verified without a model:

Negation

Any assertion type can be negated with the not- prefix:
Use not-contains, not-regex, not-is-json, and so on.

Model-assisted assertions

Use these when deterministic checks can’t capture the quality you care about:
Model-assisted assertions cost tokens and introduce non-determinism. They should complement — not replace — deterministic checks. Use them only when deterministic checks genuinely can’t capture what you need.

Performance assertions

Decision table: if you want X, use Y

Writing good judge rubrics

When using llm-rubric or judge evaluators, the quality of the rubric determines the quality of the grading.
1

Be specific, not vague

Vague: “Is the output good?”Specific: “Does the output mention the company name in the first sentence? Does it use a professional tone? Is it under 100 words?”The rubric should describe observable, checkable criteria — not impressions.
2

Use binary or numeric scales

Ask for “correct/incorrect” or a 1–5 scale, not open-ended qualitative feedback. Open-ended output is hard to aggregate into a score.
3

Ask the judge to reason first

Instruct the judge to think step by step before scoring: “Think step by step about whether this output meets the criteria, then give a score of 1–5.”Chain-of-thought grading consistently improves accuracy.
4

Version your rubrics

Changing the rubric text changes what the metric means. A rubric edit is a new evaluator version. Without versioning, historical comparisons become meaningless — the baseline was graded by a different rubric.
5

Calibrate against human judgment

Periodically score 25–50 outputs yourself and compare against the judge. If they diverge significantly, refine the rubric until they align.

Common eval mistakes

The evolving eval cadence

Build your eval practice incrementally. Here is a realistic week-by-week plan:
1

Week 1: Start small

Write a quick eval file with 5 cases and deterministic assertions only. Run it locally. Get a passing baseline.
2

Week 2–3: Graduate to a full suite

Move to prompts/ + datasets/ + evaluators/ + suites/. Add 20+ cases. Cover edge cases and at least one adversarial case. Establish your first baseline with the apastra-baseline skill.
3

Month 2+: Add AI grading and regression policies

Add llm-rubric or judge evaluators for subjective quality dimensions (tone, helpfulness, completeness). Set up a regression policy. Connect to CI so every PR runs the suite.
4

Ongoing: Never-again suite

When a prompt failure reaches production, add the failing case to a dedicated regression suite. Periodically calibrate AI judges against human judgment.

Tips for robust evals

The trials field on a suite runs each case multiple times and records variance. Use trials: 1 for smoke suites (fast, low cost). Use trials: 3 or more for regression suites where variance matters — especially when using AI-graded assertions.
A flaky case is one that passes sometimes and fails others due to model non-determinism. Steps to handle them:
  1. Increase trials to get a stable average.
  2. Widen allowed_delta in your regression policy for that metric.
  3. If the case is still unreliable, quarantine it and track its flake rate separately — don’t let it silently pass as “random noise”.
Both can apply to the same case — they complement each other. Inline assertions are per-case and ideal for specific edge-case checks. Evaluator files are per-suite and ideal for consistent, reusable scoring rules (like keyword_recall) applied across all cases.
A holdout set is a dataset your prompt has never been tuned against. It guards against benchmark gaming — where you optimize for the test cases you see instead of real-world inputs. Add a holdout dataset to your release-candidate suite so the final gate uses unseen data.