Marketing

Keeping a Tagging Prompt in Sync Across Two Models

Antonio Evans10 min read

A split-screen diagram: left side shows a prompt file with a version tag and a diff against the previous version; right side shows two model output columns with colored rows marking matches, mismatches.
Key points

Key points

  1. 1Put the extraction prompt and its schema in a versioned file so every output can be traced back to an exact prompt and model pair.
  2. 2Build a fixed set of transcripts with expected tags, and treat any drop in label agreement as a regression, not a blip.
  3. 3Run every prompt or schema change against that fixed set before production traffic sees it, and gate the deploy on a pass/fail threshold.
  4. 4Keep the previous prompt version and previous model both deployable, so a bad swap is a one-step rollback instead of an archaeology project.

Why the second model is harder than the first

The first working tagging pipeline is a fragile victory. You wrote a prompt, it produced plausible JSON, you parsed it, and the labels looked right on the twenty transcripts you happened to test. Everything after that point is maintenance, and maintenance is where these pipelines actually fail.

The reason is structural. A prompt is not a specification. It is text that biases a probabilistic process, and the same text can produce different distributions of outputs on a different model, a different parameter setting, or even a different day. Prompt behavior depends on the prompt text, the model, parameters, tools, and retrieved context, so changing any of those inputs can alter results even when the prompt itself stays similar (Braintrust on prompt testing). Practitioners learn this the hard way: a small wording change that seems innocent can flip outputs, which is why prompts deserve the same discipline as application code (LaunchDarkly on prompt versioning).

Structured output formats help, but they solve only part of the problem. Constrained decoding, which masks out invalid tokens during generation so the output conforms to a schema, is now the dominant technique for JSON compliance, and it has been systematically benchmarked against real-world schemas (the JSONSchemaBench paper). What constrained decoding does not do is guarantee that the values inside the valid JSON are correct. A schema can force {"sentiment": "positive"} to parse while the label is still wrong.

There is a second failure mode that has nothing to do with the schema. A model that was asked to output "only JSON" may decide to be helpful and wrap the payload in a sentence. One reported incident involved an invoice pipeline that ran cleanly for three weeks until a supplier sent an unusual invoice, the model prefixed its output with "Here is the JSON data you requested:", and the parser crashed, leaving thousands of documents unprocessed before anyone noticed (Sharad Bharadwaj on structured outputs). Swap the model and you inherit a new set of conversational habits.

So the maintenance phase has three jobs that the first version never had to do: record what is running, decide whether a change is acceptable, and be able to go back. The rest of this article is those three jobs, in order.

Step 1: Treat the prompt and schema as versioned files

The minimum viable versioning scheme is a directory of immutable prompt files, each paired with the schema it expects and the model configuration it was validated against. Do not edit a prompt file in place. Create a new one, and let the old one stay on disk because you will need it for rollback and for reproducing old outputs.

A simple layout that works:

prompts/
  tag-transcript/
    v1/
      prompt.md
      schema.json
      config.json
    v2/
      prompt.md
      schema.json
      config.json

The config.json is the part teams forget. It should carry the model identifier, temperature, maximum output length, and any provider-specific settings that affect output. Prompt versioning only works when the recorded configuration travels with the prompt version, because a score difference between two runs can be traced to a model swap or a temperature change as readily as to an edited instruction (Braintrust on prompt testing). If your config lives in an environment variable that someone changes on a Friday, your version history is a fiction.

Three rules keep this honest. First, versions are immutable: once a prompt version is created, it stays unchanged, and any modification produces a new version rather than altering one already referenced in logs (Braintrust on prompt versioning). Second, every production record stores the prompt version, the config, and the model identifier alongside the output. Third, the schema is versioned separately from the prompt text, because a schema change and a prompt change are different kinds of risk.

That separation matters more than it sounds. Adding a required field to the schema can break a parser downstream even if the model still extracts the same information. Changing the prompt can shift label boundaries even if the schema is untouched. If both are bundled in one file, you cannot tell from the version number which kind of change you made.

What you changeWhat it can breakWhat to re-test
Prompt wordingLabel boundaries, tag granularity, refusal behaviorFull transcript set, human review of disagreements
Schema fieldsDownstream parsers, database writes, dashboardsSchema validity plus full transcript set
Model or model versionJSON cleanliness, label distribution, edge-case handlingFull transcript set, pass/fail threshold
Temperature or parametersConsistency across repeated runsRepeat runs on a subset to measure variance

If you are running several models behind one pipeline, keeping the provider and model settings in a single config file makes the comparison cheap, and routing all of them through one API key with a consistent request format removes a whole class of accidental differences, which is what Quanta Inference is built for.

Step 2: Build a fixed transcript set with expected labels

You cannot gate a deploy without a baseline. The baseline is a set of transcripts that never changes, each with the labels a careful human would assign.

Start smaller than you think. The most common mistake in building these sets is waiting for a "representative" sample of hundreds. A better starting point is twenty to forty transcripts that cover the shapes you actually see: short turns, long monologues, speakers who interrupt, transcripts with missing punctuation from automatic transcription, transcripts where the topic is genuinely ambiguous, and at least three cases where a previous version got it wrong.

Researchers applying language models to interview transcripts have concluded that AI is best positioned to augment rather than replace traditional qualitative methods, and that the balance of human and machine input is what determines whether the analysis holds up (Child Trends case study). The same logic applies to your expected labels: a human assigns them once, they are frozen, and the model is measured against them. If the model disagrees, that is data. If the human revises the expectation to match the model, you have not built a test set, you have built a mirror.

Store each case as a transcript file plus an expected-labels file, and include the reason for any label that a reasonable person might dispute. When a case turns out to be genuinely ambiguous, mark it advisory rather than deleting it, so it still reports a score without blocking a deploy.

Case typeWhy it belongs in the setHow many to include
Clean, well-labeled transcriptConfirms the core task still works8 to 10
Ambiguous topicTests whether the model over-commits3 to 5
Noisy auto-transcriptionTests robustness to formatting damage3 to 5
Known prior failurePrevents the same regression twice2 to 4
Adversarial or off-topic contentTests refusal and scope limits2 to 3

Keep the set out of the prompt. Few-shot examples inside the prompt are useful, but the evaluation set must never be the same examples the model sees, or you are measuring memorization. A few-shot approach is a standard prompting strategy for improving response quality (Philip Adu on prompting strategies), and its benefit collapses the moment the examples are the test.

Step 3: Run every change against the set before production

Now you need a harness that runs the fixed set against a candidate version and reports what changed. It does not need to be elaborate. It needs to be repeatable.

The core loop is: load each transcript, render the prompt template with it, call the model, parse the output against the schema, and compare the extracted labels to the expected labels. Record the result per case, per version, so two candidates can be compared side by side rather than argued about in a chat thread.

def run_case(transcript, prompt_version, config):
    messages = render(prompt_version.prompt, transcript.text)
    raw = call_model(messages, config)
    parsed = parse_against_schema(raw, prompt_version.schema)
    return {
        "case_id": transcript.id,
        "prompt_version": prompt_version.id,
        "model": config.model,
        "labels": parsed.labels,
        "raw": raw,
    }

def score_case(result, expected):
    got, want = set(result["labels"]), set(expected.labels)
    return {
        "exact_match": got == want,
        "precision": len(got & want) / len(got) if got else 1.0,
        "recall": len(got & want) / len(want) if want else 1.0,
        "schema_valid": result["parsed_ok"],
    }

Structured outputs via schema enforcement give you a hard guarantee that the response conforms to the format, which is why constrained decoding frameworks have standardized on JSON Schema (the JSONSchemaBench paper). That lets you separate two questions cleanly: did the output parse, and was the content right. Parse failures are rare with constrained decoding but not zero, and they should be treated as hard failures rather than scored.

Run each case more than once if your configuration has any randomness. Regression testing for language models asserts quality scores over a dataset rather than exact output strings, precisely because probabilistic systems cannot be tested by exact matching (Confident AI on regression testing). Three runs per case on a twenty-case set is sixty calls, which is cheap enough to run on every prompt change and expensive enough that nobody does it casually.

Step 4: Set a pass/fail threshold and a diff you can read

A threshold turns a pile of scores into a decision. Without one, every result is arguable, and the person who cares most about shipping wins by default.

A practical gate for a tagging task:

CheckThresholdAction on failure
Schema validity100 percent of runsBlock the deploy
Exact match on the full setAt least 85 percent, and no lower than the current baselineBlock and inspect
Recall on required tagsAt least 90 percentBlock and inspect
Any case that passed on baseline and fails on candidateZeroBlock, inspect that case first
Latency and token spendNo more than 20 percent above baselineFlag for review, do not block

Set your own numbers from your current baseline rather than copying these. The important structural point is the fourth row: a candidate that improves the overall average while breaking a case that previously passed is a regression, and the average will hide it. Prompt testing is designed to show whether a change improved the intended case without breaking behavior that already worked (Braintrust on prompt testing).

A readable diff makes failures explainable. Print one line per case with the expected labels, the candidate labels, and the added and removed tags.

$ python eval.py --prompt tag-transcript/v2 --model candidate -v
[PASS] t-014  exact  expected=budget,staffing  got=budget,staffing
[FAIL] t-031  recall expected=timeline,risk  got=timeline
[FAIL] t-038  precision expected=risk  got=risk,risk_mitigation

Reading that output, you can guess what happened in the second line without opening the transcript: version two dropped a risk tag on a case the previous version caught. That is a block, and it is worth ten minutes before it becomes a dashboard anomaly.

Step 5: Swap the model with the previous version still deployable

A model switch is not a prompt change, and treating it as one is how teams get surprised. The new model may be stricter about JSON, chattier in its preamble, better at the task, or all three at once. You will not know which until the fixed set tells you.

Run the swap as a formal comparison: same prompt version, same config except the model identifier, same fixed transcript set. If the candidate passes the gate, deploy it and keep the previous pair (prompt version plus model) live as a rollback target for at least a week of production traffic. If it fails, compare the failures to see whether the problem is the model or the prompt.

This matters because the fix for a model swap is often not a rollback but a prompt adjustment that no longer works when you go back. Track that carefully. If model B needs a stricter instruction and you add it, the prompt version is now B-specific, and the rollback target for A is a different prompt version, not the same file with a different model setting. Version the pair, not just the text.

Prompt management, as distinct from versioning, treats the prompt as part of the application infrastructure with runtime updates, monitoring, and access controls (LaunchDarkly on prompt versioning). You do not need a commercial platform to get the critical part: the ability to move a single configuration value and have production return to a known state without a code deploy.

The rollback checklist, run in order:

StepActionWhy it comes here
1Flip the model and prompt version back to the last passing pairStops the bleeding first
2Confirm schema validity is back to 100 percent on live trafficProves the parser is safe
3Replay the last 100 production items through the restored pairConfirms labels match the pre-swap baseline
4Requeue items processed by the failed candidatePrevents a silent data gap
5Mark the failed candidate version as blocked in the repoStops someone redeploying it
6Add the failing production case to the fixed setTurns the incident into coverage
7Write down the failure mode in the version notesSpeeds up the next diagnosis

Step four is the one people skip. Items processed during the bad window have outputs that parsed fine and scored acceptably, so they never trigger an alert, and they sit in your downstream table as quietly wrong labels. Requeue them.

Adding the failing case back to the set (step six) is what makes the fixed set improve over time. Production failures should be added back to the test set so regression coverage stays aligned with the failures users actually encounter (Braintrust on prompt testing).

If you would rather not run this loop yourself for a new line of business, or you want a specialist to own the evaluation set and the rollback runbook with your team, Quanta Experts reads your context and hands back finished deliverables, including the harness and the threshold proposal.

FAQ

FAQ

Do I need a platform to version a prompt, or is a git repo enough?

A git repo with immutable prompt files and a config file next to each one covers the versioning requirement for most teams. Platforms add runtime updates, monitoring, and access controls on top of versioning (LaunchDarkly), which you should adopt when you have several pipelines sharing prompts or several people editing them. The non-negotiable parts are immutability, the recorded config, and the ability to reproduce an old output.

How many transcripts does the fixed set need before it catches regressions?

Start with twenty to forty cases covering clean inputs, ambiguous topics, noisy transcripts, known past failures, and adversarial content. The number matters less than the coverage and the discipline of never editing an expectation to match a model. Add one case every time production fails, and the set grows into something that reflects your real traffic.

What do I do when the new model is better overall but worse on a few cases?

Treat those cases as the deciding evidence, not the average. A candidate that raises overall agreement while breaking a previously passing case is a regression, and you should inspect those failures first. Either accept them explicitly with a documented reason, fix them with a prompt adjustment, or do not swap.

Should I re-run the whole set when I only change the schema?

Yes. A schema change looks mechanical, but adding a required field or narrowing an enum can change how the model fills the other fields, and downstream parsers break in ways the model never sees. Schema validity should sit at 100 percent before you look at labels, and the label comparison still has to run.

How do I stop teammates from editing the production prompt directly?

Make the production prompt read-only and load it from the versioned file at deploy time, with the version identifier logged on every output. When the prompt cannot be edited in place, the only path to a change is a new version plus the evaluation run, which is exactly the friction you want. Prompt changes that seem minor can quietly break other scenarios when they are not versioned and tested (Braintrust).

Sources

Sources

  1. What is prompt testing? Test cases, datasets, and regression ...
  2. Prompt Versioning & Management Guide for Building AI ...
  3. Generating Structured Outputs from Language Models
  4. The AI Series: Structured Outputs, Getting Reliable JSON ...
  5. What is prompt versioning? Best practices for iteration without ...
  6. A Case Study on Using AI to Analyze Qualitative Interview ...
  7. How to Prompt Like a Pro: AI Strategies for Qualitative Research
  8. LLM Regression Testing: How to Gate Every Change with ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles