The team had spent a fortnight trying to make the agent reproducible. Same prompt, same seed, temperature pinned to zero — and still, every so often, a different answer. They were treating it as a bug to be hunted down, a loose wire somewhere in the stack. It was not a bug. It was the nature of the thing. The breakthrough came when they stopped asking the model to behave like a deterministic function and started building a system that was safe whatever the model did. The flakiness did not disappear; it stopped mattering.
You cannot just turn determinism on
The instinct is to make the model repeat itself: set temperature to zero, fix the random seed, and expect the same input to yield the same output. It does not reliably work, and it is worth understanding why before you spend a sprint on it. Temperature zero makes sampling greedy, not deterministic. A fixed seed is best-effort, not a guarantee — provider docs say as much. The deeper cause is below the model: production inference runs requests in batches, and the batch an individual request lands in varies with load, so floating-point reductions execute in a different order and the arithmetic is not associative. Add GPU kernel non-determinism and hardware variation, and the same prompt can produce different tokens for reasons that have nothing to do with your prompt. The honest conclusion is the useful one: bit-for-bit reproducibility of a model in production is not a knob you can turn, so stop building on the assumption that it is.
This matters because two different things hide under the word “determinism”. One is reproducibility — same input, same output — which is largely unattainable here and, more importantly, not what you actually need. The other is acceptability — the output always satisfies the properties that make it safe to use. You can have the second without the first, and the second is the one that lets you ship.
Determinism is a property of the system, not the model
So move the determinism from the generator to the gate. The pattern is simple to state and hard to skip: generate freely, then validate strictly — and reject anything that fails, automatically, before it reaches anyone. The model is allowed to be probabilistic; the system around it is not. What makes the output safe is not that the model produced the same thing twice, but that nothing the model produces escapes a deterministic check it has to pass.
That gate is built from ordinary, deterministic engineering applied at the right boundaries. You constrain the shape of the output so it is machine-checkable. You re-derive the claims you can re-derive instead of trusting them. You make the effects idempotent and reversible so a wrong action is survivable. And you test by the properties that must always hold rather than by an exact answer that never will.
| Layer | The probabilistic part | The deterministic guarantee you add |
|---|---|---|
| Output shape | The model phrases things freely | Enforce a schema / typed contract; reject unparseable or out-of-range output |
| Facts | The model may assert things that are plausible but wrong | Re-derive or look up what is checkable; trust the source, not the sentence |
| Actions | The model decides what to do | Idempotent, reversible, least-privilege effects behind a gate — a wrong call is survivable |
| Tests | No two runs are identical | Property-based invariants + a pass-rate threshold over many runs, not one golden answer |
We stopped trying to make the model give the same answer twice and started making the system safe whatever answer it gave. The day we moved the effort from the generator to the gate, the flakiness stopped being a crisis and became a number we could watch.
Testing something that never repeats
Exact-match assertions are the wrong tool for a system whose output legitimately varies. They produce flaky tests, and a flaky test is worse than no test — the team learns to ignore red, and a real failure hides in the noise. Two techniques replace them. The first is property-based testing: instead of asserting a specific output, you assert the invariants that must hold for every output — it parses, it stays within bounds, it never references a record the user cannot see, the total reconciles. Tools built for this generate many varied inputs and hunt for a case that violates the property. The second is statistical: you run the same case many times and watch the pass rate against a threshold, treating a drop as the signal rather than expecting a perfect score. A single run tells you almost nothing about a probabilistic system; the distribution tells you whether it is healthy.
A good acceptance check has a useful test of its own: it is one two competent reviewers would independently agree on. If the gate is subjective, it is not a gate — it is an opinion you have automated. Pin it to properties that are objective and re-runnable, and you have something you can enforce on every change without argument. This is the runtime, engineering-level face of treating the evaluation harness as the real specification.
What this buys you
Once determinism lives in the system rather than the model, the whole problem changes shape. Non-determinism becomes a managed quantity — a pass rate you monitor, a rejection you handle — rather than a mystery you chase. You can swap models, or accept that a provider’s inference will drift under load, without the floor falling out, because the floor is your gate and you own it. And the conversation with the business changes from the unanswerable “can you guarantee it always says the same thing?” to the answerable “here is what we guarantee it will never do, and here is the rate at which we catch and reject the rest.” You do not need the model to be deterministic. You need to be able to trust the output — and trust is something you build at the boundary, not something you hope the model provides.
Frequently asked
- Can you make an LLM fully deterministic?
- Not reliably in production. Temperature zero makes sampling greedy, not deterministic, and a fixed seed is best-effort. Underneath the model, requests are batched by load, so floating-point reductions run in different orders (and the arithmetic is not associative); GPU kernel non-determinism and hardware variation add more. Bit-for-bit reproducibility is not a knob you can turn — so design for it being absent.
- How do you make probabilistic AI output safe to ship?
- Move the determinism from the generator to the gate: generate freely, then validate strictly and reject anything that fails, automatically, before it reaches anyone. Enforce a schema on the output shape, re-derive checkable facts instead of trusting them, make actions idempotent and reversible, and test by properties. The model stays probabilistic; the system around it does not.
- How do you test a system whose output changes every run?
- Stop asserting exact matches (they produce flaky tests). Use property-based testing — assert the invariants that must hold for every output (it parses, stays in bounds, never leaks a record) — and statistical gates: run a case many times and watch the pass rate against a threshold rather than demanding one golden answer.
- Is reproducibility or acceptability the right goal?
- Acceptability. Reproducibility (same input, same output) is largely unattainable for production LLMs and is not what you need. Acceptability — the output always satisfies the properties that make it safe — is achievable without reproducibility, and it is what lets you ship with confidence.