Trail content
8 minEnglish narration, Portuguese subtitlesPodcast (RSS)
Chapters
  1. 0:00Opening
  2. 0:51The idea
  3. 1:23Before and after
  4. 2:23Why it works
  5. 2:59Safe mechanics
  6. 4:22A challenge
  7. 5:04The traps
  8. 6:16Switching models
  9. 7:12Wrap-up
Transcript

Opening

You've met this story in the last two lessons. One line changed in the invoice extractor. Three test invoices looked fine. And in production, invoices with discounts got worse.

Today we do the refactoring that prevents it. And it has a strange first step. Before you change anything, you change nothing.

This refactoring treats three smells. The untested prompt, where changes ship with no test set.

The vibe check eval, where someone asks a few questions and eyeballs the answers.

And the model pinned prompt. A classifier nobody has dared to move to another model in eight months, because the prompt was tuned on this one. In this lesson, you'll build the safety net that makes all three go away.

The idea

The solution fits in one sentence. Before any tweak, build a set of real cases with expected answers, run the current version on them, and store the result as a baseline. From then on, every change is compared against that baseline.

The baseline is the piece that's usually missing. Without it, a lone number, like ninety-one percent accuracy, doesn't tell you whether the change helped or hurt. With it, every discussion becomes a comparison.

Before and after

Before, the change ships with an opinion. Someone changes the prompt, looks at three cases, and deploys.

After, the change ships with a number. Someone proposes a change to the prompt, the model, or a parameter.

The eval runs the new version on the versioned dataset, a few times.

The result is compared with the previous version, field by field, category by category.

With no regression, the change goes ahead.

With a regression, the change stops, and the cases that got worse come up for analysis.

Here's what that looks like on the pull request, as an illustrative report. Version six against version seven. The issuer field holds. The date holds. And the total drops six points, on the discounted invoices.

The run fails, across three runs. Friday's bug never reaches production.

Why it works

The problem with testing by hand isn't laziness. It's statistics. Three examples won't catch a six point drop in a category that shows up in fifteen percent of cases. A set of dozens of real cases, run a few times, will.

The baseline turns a number into a comparison. That's what ends the it got better, it got worse debate.

And the eval changes how the team behaves. When deleting a prompt paragraph costs a few minutes of measurement, people start cleaning the prompt, instead of only adding rules.

Safe mechanics

Step zero. Don't change anything yet. Freeze the current prompt. The baseline belongs to the version that's live, not the one you want to ship.

Gather real cases. Start with thirty to a hundred production inputs, anonymized, covering the main categories and the bugs you already know. Invoices with a discount, with service tax, with more than one line item.

Label the expected result. For extraction and classification, the right value. For open text, criteria you can verify, or a rubric.

Pick the simplest grading that works. Exact match, a rule in code, or a model as judge when there's no alternative. And if the judge is a model, measure how often it agrees with a person.

Run the current version three to five times, and store the mean and the spread, per category. That's your baseline.

Version the dataset and the script in the same repository as the prompt.

Now, finally, make the change, and run the same eval.

Compare per category, not only the average. Agree with the team on which drop blocks a deploy. And wire the eval into your pipeline, so every prompt change runs it automatically.

A challenge

Here's one for you. You ran the eval once on the new version and got ninety-four percent. The baseline is ninety-two, plus or minus three points, over five runs. Can you ship?

Not yet. A single run inside the baseline's noise band isn't an improvement. You may just be looking at variation the baseline already had. Run the new version three to five times, and compare the mean and the range per category with the baseline. Then decide.

And write the ship or block rule before you see the numbers. Afterwards, everyone finds a reason to like the result they wanted.

The traps

Now the traps. The first is the average that hides a rare queue. Overall accuracy rises one point. The chargeback queue drops twenty. If your pipeline looks only at the mean, the pull request passes. The fix is to fail the pull request if any agreed category drops past its threshold, and to print the cases that got worse. That turns the number into a fix.

A dataset that doesn't look like production. A team had forty cases, invented on a whiteboard, all easy. The pipeline always passed, and production failed on service invoices. Replace the whiteboard with real anonymized invoices, including the errors you've already seen.

Overfitting to the eval. Tweaking the prompt until it passes the fifty cases can make the rest of production worse. Hold out part of the dataset, and use it only for the final decision.

A judge nobody evaluated. If grading uses a model, check its agreement with a person from time to time.

And noise mistaken for improvement. We just saw that one.

Switching models

Now the payoff. The team wants to move the ticket classifier to a cheaper model. What needs to exist before you run the test?

A dataset of real tickets with the right queue, covering every queue, including the rare ones.

The current model's baseline. Accuracy per queue, averaged over a few runs.

A decision rule, written before the test. For example, no queue may drop more than two points, and the average may not drop.

And the cost per thousand tickets for both models, to weigh against the quality difference.

With that, the switch becomes an afternoon of work, and a decision backed by a number. Not a bet. And don't wait for the provider to retire your current model. That's the worst moment to start, switching under a deadline, with no baseline. Build the eval now, while the current model still runs.

Wrap-up

So here's the idea to take with you. The eval comes before the change, never after. Freeze what's live, measure it on real cases, a few times, and write down the rule. Then change, and compare per category. That's what turns fear of touching the prompt into an afternoon of measured work.

In the next lesson, we'll look at the machine that runs all of this for you, on every change: the eval harness. See you there.

RefactoringOperations

Add Eval Before Changing the Prompt

Criar Eval Antes de Mexer no Prompt

Add Eval Before Changing the Prompt is an AI refactoring for LLM systems, in the Operations group. Build a test set and measure the current version before any tweak.

Element
31/35
Level
Beginner
Read
4 min
Updated
On this page 9
  1. 01Problem
  2. 02Solution
  3. 03Before and after
  4. 04Why it works
  5. 05Safe mechanics
  6. 06Risks
  7. 07Challenge
  8. 08Related
  9. 09Check your understanding
  10. ··Quick answers

Problem

The NF-e extractor (Brazilian electronic invoices) changed one prompt line: “with two decimal places”. Three test invoices looked fine. In production, invoices with discounts dropped. Nobody had a set to compare against. The prompt changes with no test set to prove it did not get worse. These are the Untested Prompt, Vibe-Check Eval, and Model-Pinned Prompt smells, the last one when nobody dares switch models.

Solution

Before any tweak, build a set of real cases with expected answers, run the current version, and store the result as a baseline. Every change is then compared against that baseline.

Before and after

 # PR: format tweak in the extractor
-# Tested on 3 invoices, looks good.
-PROMPT = "Extract the invoice total."
+PROMPT = "Extract the invoice total, with two decimal places."
+
+# evals/invoices.jsonl (82 real anonymized cases, versioned)
+# $ python -m evals.run --prompt invoices@v7 --baseline invoices@v6
+#
+#   field        v6      v7      difference
+#   total        97.6%   91.5%   -6.1   <- discounted invoices
+#   issuer       98.8%   98.8%    0.0
+#   date         100%    100%     0.0
+#   FAILED: total below baseline across 3 runs

Before: the change ships with an opinion.

flowchart LR
  C["1. Change"]:::tool --> V((("2. Human eye on 3 cases"))):::human
  V --> D["3. Deploy"]:::tool

After: the change ships with a number.

flowchart LR
  C["1. Change"]:::tool --> E["2. Run eval"]:::tool
  DS[("Versioned dataset")]:::store --> E
  E --> R{"3. Worse than baseline?"}
  R -->|no| D["4. Deploy"]:::tool
  R -->|yes| X(["5. Block and show the cases"]):::io
  1. Someone proposes a prompt, model, or parameter change.
  2. The eval runs the new version on the versioned dataset, a few times.
  3. The result is compared per field or category with the previous version.
  4. With no regression, the change goes ahead.
  5. With a regression, the change stops, and the cases that got worse come up for analysis.

Why it works

The problem with hand-testing is not laziness, it is statistics. Three examples will not catch a 6-point drop in a category that shows up in 15% of cases. A dataset with dozens of real cases, run a few times, will. The 82 cases and the percentages in the diff above are an illustrative report, not a published measurement.

The baseline is the piece usually missing. Without it, a lone number (“91% accuracy”) does not say whether the change helped or hurt. With it, every discussion becomes a comparison.

The eval also changes how the team behaves. When deleting a prompt paragraph has a cost measured in minutes, people start cleaning the prompt instead of only adding rules. Anthropic’s “Building effective agents” (Dec. 2024) treats evals as part of the design, not a later decoration. The foundation is What an eval is.

Check +20 XP on first tryThe team tested the extractor change on 3 invoices on screen and approved it. In production, invoices with discounts get worse. What did the 3 cases lack?

Safe mechanics

  1. Do not change anything yet. This refactoring starts by freezing the current prompt. The baseline belongs to the version that is live, not the one you want to ship.
  2. Gather real cases. Start with 30 to 100 production inputs, anonymized, covering the main categories and known bugs. NF-e with discount, with service tax, with more than one line item.
  3. Label the expected result. For extraction and classification, the right value. For open text, verifiable criteria or a rubric.
  4. Pick the simplest grading that works: exact match, a rule in code, or an LLM as judge when there is no alternative. If the judge is a model, measure its agreement with a person.
  5. Run the current version 3 to 5 times and store the mean and spread per category. That is the baseline.
  6. Version the dataset and the script in the same repository as the prompt. See Golden Dataset and Eval Harness.
  7. Make the change and run the same eval.
  8. Compare per category, rather than only the average. Agree with the team on which drop blocks a deploy.
  9. Wire the eval into CI so every prompt change runs it automatically.

Trap: an average that hides the rare queue

Overall accuracy rises 1 point. The “chargeback” queue drops 20. The average lies. The per-category report is what CI should watch. Another trap: tweaking the prompt until it passes the 50 eval cases and making the rest of production worse. Hold out part of the dataset for the final decision only.

Sampling noise misleads too. One run at 93% and another at 89% is not “it improved”. Three to five runs, mean and range. Write the ship-or-block rule before you see the numbers.

Check +20 XP on first tryThe CI eval looks only at mean accuracy. The mean rises 0.8 points; the ‘invoice with discount’ category drops 6. The PR passes. What is the CI fix?

Risks

  • A dataset that does not represent production. Invented or easy-only cases give false confidence. Pull from logs and include the hard ones.
  • Overfitting to the eval. Tweaking the prompt until it passes the 50 cases can make everything else worse. Hold out part of the dataset for the final decision only.
  • An unevaluated judge. If grading uses a model, check its agreement with a person from time to time.
  • Mistaking noise for improvement. A single run can mislead. Run more than once before concluding.

Challenge

The team wants to switch the ticket classifier to a cheaper model. What needs to exist before running the test?

Show solution
  1. A dataset of real tickets with the correct queue, covering every queue, including rare ones.
  2. The current model’s baseline: accuracy per queue, averaged over a few runs.
  3. A decision rule written before the test, for example: no queue may drop more than 2 points, and the average may not drop.
  4. Cost per thousand tickets for both models, to weigh against the quality difference.

With that, the switch becomes an afternoon of work and a decision backed by a number, not a bet.

Check your understanding

Check +20 XP on first tryNobody has switched the classifier's model in eight months, ‘because the prompt was tuned on this one’. What is missing for the switch to stop being scary?

Check +20 XP on first tryYou ran the eval once on the new version and got 94%. The baseline is 92% ± 3 points over five runs. Can you ship?

Check +20 XP on first tryThe golden dataset has 40 cases the team invented on a whiteboard, all easy. CI always passes. In production, the extractor fails on service invoices. What is the next step?

Quick answers

What is Add Eval Before Changing the Prompt?

Add Eval Before Changing the Prompt is an AI refactoring for LLM systems, in the Operations group. Build a test set and measure the current version before any tweak.

What problem does Add Eval Before Changing the Prompt solve?

It treats Untested Prompt, Model-Pinned Prompt, and Vibe-Check Eval.

Which pattern does Add Eval Before Changing the Prompt lead to?

The Eval Harness and Golden Dataset design pattern.

Go further

Relations

Part of the tracks 1
Search