A/B Test a Prompt Change: a 10% Rollout Caught a 1.9x Cost

Send 10% of production to a new prompt version, split by session. Measured: 8.0% of 200 sessions went to it, all kept one version, and it cost 1.94x per call.

Our picks

live pricing
Model Verdict Price
GPT-6 Luna v1 of support-answer cost $0.000110 per call over the rollout's 1,272 calls, with a median latency of 4.9 s. With reasoning_effort none it cost $0.000034. Production version in the demo from $0.1/M
DeepSeek V4.1 Flash v3, the same prompt on this model, answered faster (median 3.1 s) but cost $0.000213 per call, 1.94 times v1, because it wrote 1.6 times the output tokens at a higher price per token. Candidate version in the demo from $0.3/M
Contents
  1. How do you send 10% of production to a new version?
  2. Which calls go to the candidate?
  3. How do you compare the two versions?
  4. Does turning reasoning off change the result?
  5. Do test runs skew the comparison?
  6. What is left out?
  7. FAQ
  8. How do I A/B test a prompt in production?
  9. Does a rollout keep each conversation on one version?
  10. Can a prompt change raise cost without changing the text?
  11. Related reading

A prompt rollout sends a share of the calls for one prompt id to a candidate version while the rest keep getting production, so you can compare the two on real traffic before switching everyone. In the Synthorai prompt library you set it on the production label (“v1, with v3 at 10%”), and the gateway decides each call’s version by its session id, so a conversation never switches versions halfway. In our run 16 of 200 sessions (8.0%) went to the candidate at a 10% setting and every session kept one version. The candidate moved the prompt to another model, a change whose cost nobody had measured, and at the 10% share alone the per-version totals showed it cost 1.92 times as much per call.

TL;DR

  • At a 10% rollout, 16 of 200 sessions (8.0%, 95% interval 5.0 to 12.6%) got the candidate; at 50%, 111 of 200 (55.5%).
  • All 400 three-turn sessions kept one prompt version, and so did every end user identified by _user, the body’s user or safety_identifier.
  • The candidate, the same prompt on DeepSeek V4.1 Flash instead of GPT-6 Luna, cost $0.000213 per call against $0.000110, at a median 3.1 s against 4.9 s.
  • Calls pinned to prompt_version=1 never went to the candidate: 0 of 40.

This guide builds on Prompt versioning, which sets up the two prompts and their versions. From the same directory of github.com/synthorai-io/use-cases:

cd use-cases/prompt-library
python rollout.py       # 10%, then 50%, then stops the rollout
python effort.py        # each version with and without a caller-sent reasoning_effort

The rollout ran on October 19, 2026 and made 1,840 calls; the checks of how end users are split ran on October 20. Production is v1 of support-answer on GPT-6 Luna. The candidate is v3: the same system text and messages on DeepSeek V4.1 Flash, so only the model changes.

How do you send 10% of production to a new version?

You give the production label a second version and a share. With an admin key it is one call, and another call stops it:

import os
import httpx

admin = httpx.Client(base_url="https://synthorai.io/api/v1",
                     headers={"Authorization": f"Bearer {os.environ['SYNTHORAI_PROMPTS_ADMIN_KEY']}"})

admin.put("/prompts/support-answer/labels/production/rollout", json={"version": 3, "percent": 10})
# ... compare the two versions ...
admin.delete("/prompts/support-answer/labels/production/rollout")   # production is v1 only again

Nothing changes for callers: they keep calling support-answer without a version, and the X-Synthorai-Prompt-Version response header says which version answered each call. To ship the candidate you move production to it, which ends the rollout. In the console the same setting is “Roll out gradually” on the candidate’s page, and the prompt list then shows the label as “production v1 · v3 10%”.

The roll-out dialog for v3: A share of calls set to 10%, a note that a session stays on the same version from call to call, the model change from gpt-6-luna to deepseek-v4.1-flash, and a note that v3 has not been run over a test set

Which calls go to the candidate?

The gateway picks a version from a stable key for each call, so the same key always gets the same version while the share stays the same. The key is the first of these that the call carries:

  1. the session id (X-Session-Id, the header from Trace an LLM agent);
  2. the end user: the _user label in X-Synthorai-Metadata, else the body’s user, safety_identifier, or Anthropic’s metadata.user_id;
  3. none of these: the request itself, so each call is picked on its own.

rollout.py checked each case at a 10% and a 50% share. A session was 3 turns, an end user 2 calls:

CallsAt 10%: on v3At 50%: on v3Kept one version
200 sessions, X-Session-Id16 = 8.0% (5.0 to 12.6%)111 = 55.5% (48.6 to 62.2%)400 of 400 sessions
50 end users, _user label627100 of 100 users
100 calls with neither11 = 11.0% (6.3 to 18.6%)49 = 49.0% (39.4 to 58.7%)not applicable
20 calls pinned to prompt_version=100

The ranges in parentheses are 95% confidence intervals, the range in which the true share is likely to lie given that many trials; every one contains the share we set. A separate run at 50% checked the two body fields: 50 users identified only by the body’s user field and 20 only by safety_identifier, 2 calls each. All 70 kept one version, with 25 of the 50 and 8 of the 20 on v3.

This has two consequences for your own rollout. A session id is what keeps a multi-turn conversation consistent, so send one on every call of a conversation; without any key, the same user can get v1 on one call and v3 on the next. And a pinned call, one that names a version with prompt_version, is outside the rollout: a test or a batch job that pins a version keeps getting exactly that version.

How do you compare the two versions?

The Prompts page totals calls, errors, spend, tokens and latency per version, and the Requests API returns the same figures with prompt_id and prompt_version filters. The demo read them back after the rollout:

VersionRequestsSpendSpend per callMedian latencyOutput tokens per call, meanReasoning tokens per call, mean
v1, GPT-6 Luna1,272$0.1405$0.0001104.9 s198149
v3, DeepSeek V4.1 Flash568$0.1208$0.0002133.1 s321256

Reasoning tokens are the tokens a model writes to think before its reply. They are billed as output and not shown in the answer.

The Calls tab of the prompt shows the same kind of totals. Over 7 days it counts every demo call of each version, not only the rollout’s, so its figures are larger than the table’s.

The Calls tab of support-answer over 7 days with Include test runs off: v3 with 1,294 calls, $0.17 and 2.5 s latency; v2 staging with 228 calls; v1 production with 2,309 calls, $0.20 and 5.5 s

v3 is faster and costs 1.94 times as much per call over the whole run. Two things add up to it. DeepSeek V4.1 Flash costs more per token than GPT-6 Luna on this gateway: $0.15 input and $0.60 output per million tokens in its off-peak band, against $0.10 and $0.50. And v3 wrote 1.6 times the output tokens, almost all of the difference reasoning: 256 reasoning tokens per call against 149, while the replies themselves were 65 and 48 tokens (medians). Neither number shows in the prompt text. The gap was already there at the 10% share alone: the 71 calls v3 answered cost $0.000212 each, against $0.000111 for the 749 calls v1 answered, 1.92 times as much.

The price also depends on the hour. All of the rollout’s DeepSeek calls ran in the gateway’s off-peak band for this model (22:00 to 08:00 Beijing time), billed at half the peak rate of $0.30 and $1.20. The 140-call run on October 20 ran in the peak band, and there v3 cost $0.000394 per call against $0.000107 for v1, 3.7 times as much, on 66 and 74 calls.

Of the 1,840 calls, 1,839 returned 200. One timed out in the client after 300 s and is counted as an error; the session it belonged to still got its other turns.

Does turning reasoning off change the result?

It cuts both versions’ cost by two thirds or more but does not make v3 the cheaper one. effort.py called each version 50 times with no reasoning_effort and 50 times with reasoning_effort: "none" sent by the caller, on the customer messages of the test set in Test a prompt before it ships, which all name an order number (40 messages, 10 of them twice):

Versionreasoning_effortCost per callOutput tokens, meanMedian latencyAnswer has the order numberJudged to answer the question
v1, GPT-6 Lunadefault$0.0001071924.8 s50/5037/50
v1, GPT-6 Lunanone$0.000034451.8 s50/5035/50
v3, DeepSeek V4.1 Flashdefault$0.0002003003.2 s50/5043/50
v3, DeepSeek V4.1 Flashnone$0.000049551.7 s50/5039/50

“none” removed reasoning entirely on both models and cut cost per call 68% on v1 and 75% on v3. The answers held up as far as 50 calls can show: every answer still named the order number, and the judged check (a Claude Sonnet 5.5 call that rates whether the reply addresses the customer’s question) moved by 2 and 4 of 50, with Fisher’s exact test giving p = 0.82 and 0.44. Fisher’s exact test gives the probability of a difference at least this large if “none” made no difference; values this high mean the change is within chance. With “none” on both, v3 still cost 1.44 times v1 per call ($0.000049 against $0.000034). These calls ran in the gateway’s off-peak band for DeepSeek too.

During a rollout the caller does not know which version will answer, so a reasoning_effort it sends applies to both. The effort can live on the version instead: a version can store reasoning_effort, which the gateway writes into Chat Completions and Responses requests when the call does not set reasoning itself. A candidate saved that way carries its own setting into the rollout, and the per-version totals then compare the versions as they would ship.

Do test runs skew the comparison?

Test runs do not skew it by default, because a version’s totals on the Prompts page leave out test traffic: Playground calls, which includes the console’s test runs, and calls labelled source: prompt-test or source: eval in X-Synthorai-Metadata. An “Include test runs” switch counts them. On support-answer, turning it on added 150 calls to each of v1 and v2, the 110 calls each of a labelled eval run and the 40 each of a console test run, and left v3 unchanged. Logs and the Requests API still list every call, so filter by the label there if an eval ran during your rollout:

X-Synthorai-Metadata: {"source":"eval"}

What is left out?

  • Statistical tests on cost and latency. The per-version table above compares totals and means; we did not test the differences. Before acting on a difference, test it, especially a small one.
  • Answer quality on live traffic. A rollout tells you what a version costs and how fast it is. Whether its answers are right is easier to check on a fixed set of inputs before the rollout; Test a prompt before it ships does that for v2.
  • Automatic stops. Nothing in the demo ends a rollout when the candidate’s error rate or cost goes past a limit. An admin changes the share, moves the label to the candidate, or stops the rollout.

FAQ

How do I A/B test a prompt in production?

Give the production label a candidate version and a share, keep calling the prompt id as before, and compare the two versions’ per-version totals. In the demo a 10% share sent 16 of 200 sessions to the candidate, and the totals showed its cost after a few hundred calls.

Does a rollout keep each conversation on one version?

A rollout keeps a conversation on one version when its calls carry a session id. All 400 sessions in the demo kept one version across their three turns. Without a session id, the end user identifier is used, and without either, each call is picked on its own.

Can a prompt change raise cost without changing the text?

A model switch with the same text can, as it did in the demo. The candidate on DeepSeek V4.1 Flash cost 1.94 times the GPT-6 Luna version per call, because it cost more per token on this gateway and spent more tokens reasoning. Compare the cost per call of the whole version, not the model’s price.

← Use cases