What a prompt lab actually validates
Three fixture-backed extraction lessons show how prompt contracts grow — and why an evaluation can only prove the fields its grader checks.
| Resource | Link |
|---|---|
| Source | ravikanchikare/prompt-eng-code-lab |
| Evaluation runner | PromptFoo |
| Model API | Anthropic Messages API |
Automated prompt evaluation can prove only what its grader checks. This lab sends the same 20 synthetic review pages through three PromptFoo lessons, each using claude-haiku-4-5-20251001 through Anthropic’s Messages API.
The prompts grow from a two-field CSV row to a nested, season-grouped JSON object. The graders do not grow at the same rate. That difference is the most useful part of the implementation: it separates a prompt’s declared contract from the evidence produced by its tests.
| Fact | Current implementation |
|---|---|
| Lessons | 3 self-contained directories |
| Fixtures | 20 HTML review pages reused by every lesson |
| Configured tests | 20 per lesson, 60 total |
| Provider | anthropic:messages:claude-haiku-4-5-20251001 |
| Evaluator | PromptFoo with a Python assertion per lesson |
| Recorded results | None committed |
Three additive lessons
Each lesson contains system_prompt.md, prompt.yaml, promptfooconfig.yaml, grader.py, and a local package.json. The prompt file is the variable under test; the page fixture arrives as the user message.
Basic CSV
Extract reviewer and rating. The prompt rejects visual-star inference, preserves decimal and zero ratings, and emits exactly one RFC 4180 row.
Extended CSV
Add exact-source location and five sentiment labels. The format contract expands to four quoted fields and forbids blank placeholders.
Season-grouped JSON
Add date, stats, season, review text, and an optional previous review. The prompt requests one object under the matching season.
The shared fixtures contain explicit and missing ratings, decimal and zero values, irregular reviewer names, locations with commas and punctuation, sentiment boundaries, dates, review statistics, and previous-review content. Reusing them makes the added requirements visible instead of changing both the task and the test data at once.
Run one lesson
Each directory installs and runs independently.
cd 01_basic_table
npm install
export ANTHROPIC_API_KEY=sk-...
npm run eval
npm run viewnpm run eval sets PROMPTFOO_PYTHON=python3 and invokes promptfoo eval. The repository therefore requires Node.js, npm, Python 3, an Anthropic API key, and network access to the configured model.
The prompt contract
The first lesson establishes rules that every later prompt carries forward:
- reject ratings inferred from stars, fill width, or icon count;
- preserve explicit decimals and
0; - normalize the reviewer to
FirstName LastName; - use
nullwhen the rating is absent; and - ignore previous-review content.
Its format contract requires a header followed by one quoted, two-field CSV row.
<format_contract>
- Output raw CSV only — no prose, markdown, fences, or extra columns.
- The very first line of your output MUST be the header: reviewer,rating
- Double-quote every field value in every data row (RFC 4180).
- After the header, output one data row per page with exactly 2 comma-separated fields.
- Use "null" for missing `rating`.
</format_contract>Lesson 2 adds exact-source locations and five sentiment values: positive, negative, neutral, mixed, and unclear. The rules define neutral, mixed, and unclear positively instead of leaving the labels as names alone.
Lesson 3 changes the output to JSON. It requests all five season arrays and a review object containing reviewer, location, stats, rating, date, season, sentiment, current text, and an optional previous review.
| Requirement | Basic CSV | Extended CSV | Advanced JSON |
|---|---|---|---|
| Reviewer | Yes | Yes | Yes |
| Rating | Yes | Yes | Yes |
| Location | No | Yes | Yes |
| Sentiment | No | Yes | Yes |
| Date and season | No | No | Yes |
| Review stats | No | No | Yes |
| Current and previous text | No | No | Yes |
| Declared output shape | 2-field CSV | 4-field CSV | Season-grouped object |
The grader contract
The Python graders parse one model response at a time and return a PromptFoo pass/fail object. They normalize values before comparison so formatting noise does not obscure semantic errors.
| Lesson | Parser | Shape check | Fields compared |
|---|---|---|---|
| Basic CSV | csv.DictReader |
Exactly 1 data row | reviewer, rating |
| Extended CSV | csv.DictReader |
Exactly 1 data row | reviewer, rating, location, sentiment |
| Advanced JSON | json.loads |
Exactly 1 flattened record | reviewer, rating, location, sentiment |
The CSV graders normalize honorifics, Last, First names, missing-value synonyms, and numeric ratings. The extended grader preserves non-null location text exactly and lowercases sentiment before comparison.
The advanced grader accepts three structures:
- a season-grouped object under
reviews; - a flat list under
reviews; or - a bare list.
It then flattens the records and compares only reviewer, rating, location, and sentiment.
Tighten the advanced evaluation
The smallest credible follow-up keeps the current fixtures and makes the assertions match the prompt:
- Reject bare arrays and flat
reviewsarrays. - Require exactly the five declared season keys.
- Check that the record appears in the array matching its
season. - Compare date, stats, current review text, and previous-review fields.
- Reject missing or extra object keys when schema conformance matters.
- Commit a machine-readable result artifact with the model ID and PromptFoo version.
That change would turn the advanced lesson from a four-field extraction check into evidence for the schema it publishes.
What the lab demonstrates
The repository is a compact example of incremental prompt construction. One fixture set supports three levels of output complexity, and each lesson can be run without a shared build system.
Its stronger lesson is about evaluation design. Prompt text, parser behavior, normalization, fixture coverage, and saved results are separate parts of the system. Reliability claims are no stronger than the narrowest one.
Takeaways
The grader defines the evidence
A detailed prompt does not create detailed test coverage. Only assertions in the grader turn a requirement into evidence.
One fixture set exposes additive complexity
The same 20 pages isolate what changes as the output grows from two CSV fields to nested JSON.
Normalization belongs in the test harness
Reviewer names, null tokens, and numeric ratings need explicit normalization before semantic comparisons are useful.
Strict prompts need strict parsers
Accepting alternate JSON shapes while the prompt requires one exact schema can produce a passing result that violates the published contract.