ai newsMi., 4. Nov.
EilmeldungIntroducing Lockstep: A System of Record for AI Coding Agents

Prompt Versioning and Release Gates for LLM Apps

The fastest way I know to break a working LLM feature is to "just tweak the prompt" on a Friday. Nothing crashes. The output simply gets a little worse, and nobody notices until a customer does. So I've stopped treating prompts as config strings. They're code. They get versions, tests, and a gate i

dev.toMittwoch 04:20 · 04.11.5 Min. Lesezeit
Bild: dev.to

The fastest way I know to break a working LLM feature is to "just tweak the prompt" on a Friday. Nothing crashes. The output simply gets a little worse, and nobody notices until a customer does.

So I've stopped treating prompts as config strings. They're code. They get versions, tests, and a gate in CI that can say no.

The first step is boring and does most of the work. Every prompt lives in its own file, with a version and a short changelog note.

# prompts/ticket_summary.yaml id: ticket_summary version: 7 model: your-model-id changelog: " Ask for the customer's requested outcome as a separate line." template: | Summarize this support ticket in 3 bullet points. Then add one line: "Requested outcome:..." Ticket: {ticket_text} Enter fullscreen mode Exit fullscreen mode Now a prompt change shows up in a pull request, with a diff, a reviewer, and a version bump. That alone kills most silent regressions, because someone has to look.

Next, collect real cases. Not synthetic ones, at least not at first. I start with 30 to 50 examples pulled from production logs, each with what a good answer must contain.

# evals/ticket_summary_cases.py CASES = [ { " ticket_text ": " My invoice for March was charged twice. Please refund one. ", " must_include ": [ " refund ", " Requested outcome " ], " must_not_include ": [ " I don ' t know " ], }, #... more real cases ] Enter fullscreen mode Exit fullscreen mode String checks are crude, and that's fine for a gate. They catch the big breaks: missing sections, wrong format, refusals. For subtler quality, add a scored review later. I wrote about scoring beyond pass/fail in this piece on AI agent evaluation, which covers rubrics for actions as well as answers.

The gate runs every case against the new prompt version and fails the build if the pass rate drops below the current version.

# evals/run_gate.py import sys import yaml from evals.ticket_summary_cases import CASES def call_model ( prompt: str ) -> str: """ Wrap your LLM provider ' s SDK here. """ raise NotImplementedError def check ( output: str, case: dict ) -> bool: has_all = all ( s. lower () in output. lower () for s in case [ " must_include " ]) has_none = not any ( s. lower () in output. lower () for s in case [ " must_not_include " ]) return has_all and has_none def pass_rate ( template: str ) -> float: passed = 0 for case in CASES: output = call_model ( template. format ( ticket_text = case [ " ticket_text " ])) passed += check ( output, case ) return passed / len ( CASES ) if __name__ == " __main__ ": spec = yaml. safe_load ( open ( " prompts/ticket_summary.yaml " )) baseline = float ( open ( " evals/baseline_ticket_summary.txt " ). read ()) score = pass_rate ( spec [ " template " ]) print ( f " v { spec [ ' version ' ] }: { score:. 0 % } (baseline { baseline:.

0 % } ) " ) sys. exit ( 0 if score >= baseline else 1 ) Enter fullscreen mode Exit fullscreen mode Run it in CI on any change under prompts/. If the new version scores lower than the stored baseline, the merge is blocked. When it scores higher, update the baseline in the same PR.

A note on cost: running 50 cases on every commit adds up. I only trigger the gate on changes to the prompt folder or the model setting, not on every push.

This is the part people skip. A new model version is a release too. Run the same eval set before switching, and keep the old model ID in config so rollback is a one-line change.

I've seen "better" models fail format checks that the old one passed every time. The gate catches that in minutes instead of a week of confused tickets.

A prompt rarely works alone. It depends on the model ID, the temperature, the tool definitions, and sometimes a system message that lives somewhere else. If you version only the template, you can still ship a "same prompt, different behaviour" change without noticing.

What I do now is treat these as one bundle. The YAML file holds the template, the model setting, and a hash of the tool schemas the prompt expects. The gate fails if any of them changes without a version bump. It sounds strict, but it's the difference between "the prompt didn't change" and "nothing changed", and only the second one is useful at 2am.

Rolling back is a one-line change only if someone has actually done it. Once a month, I revert the prompt to the previous version on a staging environment and run the gate. It takes ten minutes, and it proves the old version still passes against today's cases.

String checks won't tell you whether a summary is actually useful. For that, you need a second layer: a sampled human review, or a scoring rubric run by a judge model, checked against human labels now and then.

Start with the gate anyway. It's an afternoon of work, and it turns "I think the prompt is fine" into a number.

How do you test prompt changes today, and has a model upgrade ever surprised you?

I'm Darius Tran, and I work on putting AI agents into production at AI Hive.

Quelle: dev.to (dev.to). Original öffnen