ealeng
Menu

Episode 10 of 10

A model release pipeline

A new version passed all seven offline gates, with an identical error rate on every kind of audio. 85 seconds into its canary, the pipeline rolled it back. How to build a release pipeline, and how to test the pipeline itself.

Capstone B · Model Release Pipeline11 min read
Video transcript5:32

0:00Cold open

0:01

This new version of my speech service is the same model, with a new audio decoder.

0:06

It passed all seven offline gates. On the offline test set, its error rate was identical on every kind of audio.

0:14

Eighty-five seconds into its canary release, at the first check, my pipeline rolled it back.

0:20

How can a version pass every test, and still get pulled?

0:25Intro

0:27

Hands-on voice AI evals, episode ten.

0:30

Today: a release pipeline, and how to test the pipeline itself.

0:3501 · The pipeline

0:36

A release pipeline takes a candidate version from “research says it’s ready” to “serving customers”, with evidence at every step.

0:44

First, an offline benchmark: two hundred and forty clips of read speech, simulated phone calls, and real business calls.

0:52

Then a comparison with the version serving customers now, clip by clip, so noise isn’t mistaken for change.

0:59

Then gates: rules that can block the release.

1:02

Then a canary: the candidate serves a quarter of the traffic, next to the current version. Test clips with known answers run through both, and monitors compare the two versions’ outputs.

1:13

Three possible outcomes: stop before release, roll back, or promote.

1:18

Every threshold lives in one file, pipeline dot yaml, with the reason it exists.

1:2502 · Test the pipeline

1:26

But how do you know the pipeline itself works? You test it with candidates whose right answer you already know.

1:32

Four candidates, and for each one, the expected outcome was written down before running.

1:38

The same model, rebuilt: it should be promoted. A bigger, more accurate model: promoted. A smaller, faster one, known to be worse on phone audio: stopped. And the new decoder: rolled back.

1:51

All four got the expected decision. And each bad one was stopped by a different stage.

1:5703 · Stopped at the gates

1:59

The small model is faster: about twice as fast in one offline run. That’s why it was proposed: to cut costs.

2:06

Overall, it got almost five more words wrong in every hundred.

2:10

On phone audio: fifteen more.

2:13

Three gates failed, and the release stopped after seventeen seconds. No customer ever heard it.

2:2004 · The twist

2:21

Now the decoder. Why was it identical offline? Because the benchmark reads the audio files directly. Only the live service runs the decoder.

2:31

The new decoder ignores the file’s sample rate. So phone recordings, made at eight kilohertz, are read as sixteen: they play twice as fast. Episode eight met this bug too.

2:43

Here’s a test clip through the current version.

2:45

🔊 the stable decoder: “The head of the patchwork girl was the most curious part of her.”

2:50

🔊 the new decoder: the same file, played twice as fast

2:52

The canary caught it at the first check, three ways. One: a phone upload decoded to half its declared length. That never happens normally, so one is enough.

3:03

Two: errors on the test clips jumped from nine percent to thirty-two.

3:07

Three: a phrase that must always come out right, came out wrong.

3:11

And the statistical monitors? With only six phone requests so far, they couldn’t tell yet. Two of the six came back empty, and that still didn’t reach the alarm line.

3:21

Monitors need volume. A rule for something impossible, and test clips with known answers, don’t.

3:2805 · No false alarms

3:29

Catching bad versions is half the job. The other half: letting a good one through. So the identical rebuild went through the whole pipeline four times, with a different draw of traffic each time.

3:41

Promoted all four times. Not one alarm, in twenty canary checks.

3:46

Four runs are too few to measure how often it false-alarms. No rule fired, though one monitor got close: a strength of evidence of three, against an alarm line of three and a half.

3:5805 · No false alarms

4:00

The bigger model was promoted too. On the test clips, its errors dropped from nine percent to six.

4:06

And its own confidence went up, close to the alarm line on podcasts.

4:11

For an improvement. A rule like “confidence moved” could block a better model. So confidence is reported, never used to block a release.

4:2106 · Limits

4:22

One honest limit.

4:24

The phone gate is meant to block a release that’s worse by more than one and a half points.

4:29

But with sixty phone clips, the benchmark can only reliably detect a change of about seven points, by a rough estimate.

4:37

Seeing one and a half would take something like twelve hundred clips, from many more speakers.

4:42

So a subtle regression can pass the gates. The canary’s test clips, and monitoring after release, stand behind them.

4:50

And this canary ran on my laptop: recorded audio replayed, evenly split by customer type, at a quarter share. Real canaries usually take one to five percent, and wait for real traffic.

5:0307 · The report

5:04

Every run writes a release report: the decision and why, a timeline, the evidence from every stage, and the risks of this run.

5:13

And the command to reproduce it.

5:16Summary

5:17

Gates stop versions that are clearly worse offline. The canary catches what only shows up when the service runs.

5:24

Each stage catches something the others can’t. And test the pipeline with answers you already know.

Key takeaways

  • ✓A candidate is a model and the way it's served. The offline benchmark read audio files directly, so a broken decoder was identical offline and passed every gate.
  • ✓Each stage catches what the others can't: gates stopped a worse model in 17 s with no customer exposure; the canary caught the serving bug at its first check, 85 s and 30 requests in.
  • ✓Rules that need no volume act first: an impossible event and known-answer probes caught the decoder while the statistical monitors, with 6 phone requests, still couldn't tell.
  • ✓Repeated canary checks need a margin and a strict z. An identical rebuild moved one signal +33% at z = 3.0, against an alarm line of 3.5. Confidence is reported, never gated: a better model raised it.
  • ✓Test the pipeline with candidates whose answer you know, written down before running. And know its limits: with 60 phone clips, the phone gate reliably sees only about 7 points, not its 1.5-point tolerance.

This chapter puts the earlier chapters together into one release pipeline, and shows why its last stage, a canary on live traffic, matters. In the lab, a new version of the speech service (the same model, with a new audio decoder) passed all seven offline gates, with an identical error rate on every kind of audio, and was rolled back 85 seconds into its canary, at the first check. The pipeline below shows how both can be true.

The pipeline

A release pipeline takes a candidate from "research says it's ready" to "serving customers", with evidence at every step. It's built from the earlier chapters:

candidate (model + serving config)
  → 1. offline benchmark      240 clips: read speech, simulated phone calls, real business calls   (Chapters 1–2)
  → 2. paired comparison      vs the version serving customers now, clip by clip, per slice        (Chapter 4)
  → 3. gates                  any blocking gate fails → STOP, never served a customer             (Chapter 7)
  → 4. canary                 25% of traffic, known-answer probes, monitors on both versions       (Chapter 8)
       → alarm → ROLL BACK
       → enough evidence, no alarm → PROMOTE
  → release report

Every threshold lives in one file, pipeline.yaml, with the reason it exists. A change to a threshold is a change to that file, reviewed like code.

The benchmark is voxbench-v1 (Lab 10's platform): 120 read clips (LibriSpeech), 60 phone clips (LibriSpeech with background talkers, through a phone line), and 60 segments from 5 real earnings calls. A candidate's benchmark always runs fresh; reusing stable's run for a rebuild with the same model name would compare it with itself, which the first version did.

The gates are Chapter 7's kinds: relative WER gates overall (tolerance 0.5 points), on read (1.0), phone (1.5) and calls (1.5), each failing only if the change is beyond the tolerance and its interval excludes zero; a read ceiling of 10%; no clip newly empty; and offline speed, which only warns because a laptop isn't the production machine.

The canary runs both versions side by side on the same service. The candidate serves 25% of traffic, stratified by customer segment. Every 30 canary requests, a check compares the two versions on everything so far, per segment:

Rule Alarm when
Impossible event One canary upload decodes to a different length than the file declares
Known-answer probes Chapter 8's 11 canary clips through both versions: canary probe WER > stable's + 2 points, or a must-pass phrase wrong
Empty rate Canary worse by > 5 points and z > 3.5
Words per second Changed by > 25% either way and |z| > 3.5
Error rate Worse by > 2 points and z > 3.5
Latency p95 Canary > 3× stable's, measured on the same machine at the same moment
Confidence Never alarms: reported only

Roll back at the first alarm. Promote once every segment has 40 canary requests and nothing fired. If 900 requests pass with no decision, roll back: no evidence, no release.

Test the pipeline

How do you know the pipeline itself works? Test it with candidates whose right answer you already know, written down in pipeline.yaml before running:

Try it · Try the model release pipelineFour candidates · expected outcomes known in advance

1. Offline benchmark

16.11% · unchanged

Passed

2. Quality gates

All gates pass

Passed

3. Live canary

150 requests · no alarms

Passed

PROMOTED

Expected

Promote

8.0 min to decision. Four rebuild runs promoted without alarms across 20 canary checks.

Candidate What it is Expected Decision Time
base-rebuild The same model, rebuilt promote ✅ promote 8.0 min
small-v2 A bigger, more accurate, slower model promote ✅ promote 7.6 min
tiny-fast A smaller, faster model, known to be worse on phone audio stop at the gates ✅ stop 17 s
base-newdecoder The same model, with a decoder that mishandles 8 kHz uploads roll back in the canary ✅ rollback 1.9 min

All four got the expected decision, and each bad one was stopped by a different stage.

Stopped at the gates

tiny-fast is about twice as fast, which is why it was proposed: to cut serving costs. Overall it got 4.74 more words wrong in every hundred (+2.65 to +6.93), and 15.05 more on phone audio. Three gates failed (wer_all, wer_read, wer_phone); calls was "can't tell" (+2.32, −2.09 to +7.01). The release stopped after 17 seconds, and no customer ever heard it.

The twist: invisible offline

Why was the new decoder identical offline? The benchmark reads the audio files directly. Only the live service runs the decoder. The new decoder ignores the file's sample rate, so phone recordings, made at 8 kHz, are read as 16 kHz and play twice as fast: Chapter 8's decoder bug, shipped as a release.

The canary caught it at the first check, three ways:

  1. Impossible event: a contact-centre upload decoded to half its declared length. That never happens normally, so one is enough.
  2. Probes: errors on the known-answer clips jumped from 9.2% on stable to 31.6% on the canary.
  3. Must-pass: "the head of the patchwork girl", a phone clip that must always come out right, came out wrong.

And the statistical monitors? With only 6 contact-centre canary requests so far, they couldn't tell yet. Two of the six came back empty (4% on stable), and words per second fell from 3.27 to 1.38, but z was +2.1 and −1.5, short of 3.5. Pick the check below, and try the other statistical rules:

Try it · One canary check, rule by ruleReal checks from Capstone B
Statistical rule

base-newdecoder: same model, new decoder that reads 8 kHz phone uploads as 16 kHz. At 85 s; canary requests so far: 11 podcasts · 6 contact centre · 13 earnings.

Segment · signalStableCanaryChange (margin)zAlarm?
podcasts · words per second2.702.50-7% (±25%)-0.84no
contact centre · empty rate4.2%33.3%29.1 pts (+5 pts)+2.13no
contact centre · words per second3.271.38-58% (±25%)-1.49no
earnings · words per second2.492.594% (±25%)+0.36no
contact centre · confidence (reported only)0.6170.344—-3.19never gates

Rows where both versions were 0 (errors, mostly) are hidden.

Rules that need no volume
  • impossible event: a contact-centre upload decoded to half its declared length
  • probe WER 31.6% vs stable 9.2%
  • must-pass phrase wrong: “the head of the patchwork girl”
With this rule

ROLL BACK

What actually happened: Rolled back at the first check.

Monitors need volume. A rule for something impossible, and test clips with known answers, don't. At a 25% share, they cut the exposure to one check.

No false alarms

Catching bad versions is half the job; the other half is letting a good one through. The identical rebuild went through the whole pipeline four times, with a different draw of traffic and a freshly run benchmark each time. It was promoted all four times, with no alarm in 20 canary checks.

But one monitor got close. In two of the runs, contact-centre words per second came out 33% higher on the canary, with nothing changed: past the 25% margin, at z = 2.9 and 3.0, against an alarm line of 3.5. Switch the widget to "Margin only" and both rebuilds roll back. That's why a canary checked again and again needs both a margin (ignore differences too small to matter) and a strict z (keep repeated looks from turning noise into alarms). Four runs are too few to measure how often the pipeline false-alarms; they show the thresholds don't fire on ordinary noise.

Offline, Lab 10's noise floor showed up too: in three of the four runs, one phone clip differed between identical runs of the same model, moving WER by 0.02 points. The paired interval touched zero, and every gate passed, as it should.

The better model was promoted too. On the probes, its errors dropped from 9.2% to 6.1%, and it was better offline on every slice (−4.67 overall, −10.37 on phone). And its own confidence went up, to z +3.4 on podcasts: close to an alarm line, for an improvement. A rule like "confidence moved" could block a better model, so confidence is reported, never used to block a release.

Limits

The gates can't see regressions the size of their own tolerances. The interval on a paired phone difference was ±4.7 points, so with 60 phone clips from 18 speakers, the benchmark reliably detects only about 7 points; the gate's tolerance is 1.5. Seeing 1.5 would take something like 1,200 clips, from many more speakers.

Gate Clips 95% interval Smallest reliably detected Tolerance Clips needed
wer_all 240 ±1.8 2.6 points 0.5 ≈6,480
wer_read 120 ±1.4 1.9 points 1.0 ≈460
wer_phone 60 ±4.7 6.8 points 1.5 ≈1,230
wer_calls 60 ±3.3 4.8 points 1.5 ≈610

These widths come from candidates that changed a lot, so they're likely somewhat pessimistic for subtle regressions; Chapter 7's injected-regression table measures that case directly. Either way, a subtle regression can pass the gates. The canary's probes, and monitoring after release, stand behind them.

The rest of the limits:

  • This canary ran on a laptop: recorded audio replayed, evenly split by customer type, at a 25% share. Real canaries usually take 1–5% and wait for real traffic, which sets the decision time.
  • The canary measures quality only through 11 probes and reference-free signals. A small regression that moves neither passes.
  • Audio neither the benchmark nor the traffic contains (Chapter 8's drift) is untested by construction.
  • The same machine runs the service, traffic and models, so latency includes contention. The comparison is fair; the absolute numbers aren't production numbers.

The report

Every run writes a release report: the decision and why, a timeline, the evidence from every stage (paired comparison, gates with their reasons, the canary check by check), the service state after the decision, the risks of this run, and the command to reproduce it. The canary never outlives the decision: whatever happens, the service ends up serving one version. That makes a stop or a rollback auditable, and gives the next evaluation a concrete failure to test.

The platform underneath (Lab 10)

The offline benchmark runs on Lab 10's evaluation platform, which has no chapter of its own. Four of its findings matter for any pipeline like this one:

  • Where a clip runs can change its transcript. On hard clips Whisper falls back to random sampling, and a worker's random generator carries state from every clip it transcribed before. One call clip went from 4 errors to a 32-error runaway depending on its shard. Reseeding from each clip's id fixed it; the 1 clip in 240 that still differs between identical runs is measured and reported as the noise floor.
  • Retries need a keyed store. With workers crashing mid-shard, appending every row counted 21 clips twice and moved WER by 0.75 points. Keying results by (run, clip) makes a repeat harmless, and a run with a missing shard is marked incomplete and refused, not reported as a smaller sample.
  • Combine clips, not shards. Averaging 16 shard WERs cut by slice gave 19.91% against a corpus WER of 16.11%, because 60 phone clips weighed as much as 120 read clips.
  • Measure speed; don't assume it. On the lab's laptop, int8 was about 25% slower than float32 and changed 75 transcripts with no WER gain.

Practice: the pipeline's decisions

Implement the canary's rules and the gate's limits: a signal alarm that needs both a margin and a strict z, the probe checks, the decision after each check (roll back, promote, or keep going), and what a gate can actually see. The tests use the release reports' real numbers: the rebuild's +33% at z = 3.0, the decoder's 6 phone requests, and the phone gate's ±4.7.

Try it · Practice in PythonRuns in your browser · ⌘/Ctrl + Enter to run
release_practice.py69 lines

Quiz

Try it · Check your understanding0 of 6 answered
  1. 1.A candidate keeps the same model but ships a new audio decoder. It's identical to stable on every offline slice and passes all 7 gates. Why couldn't the offline benchmark see the bug?

  2. 2.At the decoder's first canary check, 2 of 6 contact-centre canary requests came back empty (4% on stable), and words per second fell 58%. Neither statistical monitor alarmed. What caught the bug?

  3. 3.tiny-fast is twice as fast, but 4.74 points worse overall and 15.05 worse on phone audio. Where should the pipeline stop it?

  4. 4.The identical rebuild went through the pipeline four times. In one run, contact-centre words per second rose 33% on the canary, at z = 3.0. The rule is: change beyond a 25% margin AND |z| > 3.5. What does that show?

  5. 5.small-v2, a better model, raised its own confidence on podcasts (z +3.4). Why does the pipeline report confidence but never gate on it?

  6. 6.The phone gate blocks regressions bigger than 1.5 points. With 60 phone clips from 18 speakers, what can it actually see?

Check your understanding

Try each question before opening its answer.

What does each stage of a release pipeline catch that the others can't?

Offline gates stop models that are clearly worse with zero customer exposure (tiny-fast in 17 s), but can't see the serving path. The canary exercises the real service and caught the decoder in 85 s, but is too slow and exposed for a plainly worse model. Monitoring after release covers drift and anything neither contains.

How do you test the pipeline itself, not just the models?

Write down the expected decision for known-good and known-bad candidates before running (an identical rebuild, a better model, a worse model, a serving bug), then check each decision, the stage that made it, false alarms, and time to decision. Rerun the identical rebuild with different traffic to look for false alarms.

Why can a candidate pass every offline check and still be rolled back? How was it caught here?

The benchmark read 16 kHz files directly, so the new decoder never ran offline. In the canary, real 8 kHz uploads went through it: an upload decoded to half its declared length, probe WER rose from 9.2% to 31.6%, and a must-pass phrase failed, all at the first check.

Which canary signals should gate a release, and which should only be reported? Why?

Gate on signals whose meaning doesn't depend on the model: impossible events, known-answer probes and must-pass phrases, errors, empty outputs, large words-per-second changes, and latency measured on the same machine. Report confidence only: a better model raised it to z +3.4.

How do you keep a repeatedly checked canary from raising false alarms?

Require a practical margin and a strict z together (3.5 here), require a minimum sample per segment before promoting, and use impossible-event rules where they exist. Then run identical rebuilds through the pipeline: here 4 runs and 20 checks with no alarm, though one signal reached z = 3.0 past its margin.

What belongs in a release report?

What changed, the decision and its reason, the timeline, paired comparisons with intervals, gate results with their reasons, the canary check by check, the service state after the decision, risks and limits of this run, and the command to reproduce it.

Evaluation platform (Lab 10)

How do you shard a benchmark and still get exactly one result per clip?

Assign shards deterministically, retry failed shards, and store results keyed by (run, clip) so a retry's repeats are harmless. Check completeness against the dataset; a run with a missing shard is marked incomplete and refused, not compared as a smaller sample.

Sharding changed a transcript. How do you find why, and what if you can't?

Look for state carried between clips, like a worker's random generator used when Whisper falls back to sampling, and make each clip independent (reseed from its id). Whatever variation remains, measure it with repeated runs and report it as the noise floor: here 1 clip in 240.

Why combine per-clip results instead of averaging per-shard WERs?

WER is a ratio of sums. Averaging shard WERs weights small and large shards equally: with shards cut by slice, 19.91% instead of the corpus 16.11%. Store per-clip counts and let each metric combine them.

What extra questions come with evaluating a self-hosted model?

Whether a setting like int8 or batching changes the outputs, and whether it's actually faster on the target hardware: int8 was 25% slower on the lab's laptop and changed 75 transcripts. Treat every setting as a candidate to compare, with cost per audio hour.

End of the Voice AI Evaluation series. Gates stop versions that are clearly worse offline; the canary catches what only shows up when the service runs; monitoring watches what neither contains. Each stage catches something the others can't, and the pipeline itself gets tested with answers you already know.

Learning something useful?

New labs, articles, and videos on evaluating and testing AI systems come out regularly. Follow along so you don't miss the next one.