
Determinism Where You Can, Judgement Where You Must: The Technique Boundary for AI Systems
Every part of an AI system has exactly one right home: a rule, a state machine, or a bounded judge. Control belongs to the orchestrator, not to an autonomous loop. A first-principles boundary for deciding which technique owns which part.
Table of Contents
I have now written this same sentence three times in three pieces, so it is time to write the sentence underneath it.
The first companion piece, Generative AI Builds Shapes, Not Games, argued that generative AI produces plausible shapes and that correctness has to come from structure and verification wrapped around the generator. That answers whether you need something outside the model. The second, Shrink the Stochastic Surface, argued that reliability is set by how much of the output you leave the model solely responsible for, and that the work is shrinking that fraction. That answers how much structure, and where the line goes between model and machine.
Both leave one question open. Once you have decided that a part of the system should be deterministic structure rather than a raw sample, which structure? A rule? A state machine? A model reasoning inside a harness? And the question almost everyone is getting wrong in 2026: should any of it be an agent?
This piece is about that boundary. It is less glamorous than the other two because it is the part you have to get right in the code, not on the whiteboard, but it is where systems actually live or die. Here is the law, as plainly as I can put it:
Determinism where you can, probabilistic judgement only where the question genuinely demands it, and control always with the orchestrator, never with an autonomous loop. The model may reason under that control; it may not direct the control itself.
Everything below is an unpacking of that one sentence.
Four techniques, not one tool
The dominant failure mode in AI system design right now is that everything collapses into a single verb: call the model. Need to validate something, call the model. Need to route, call the model. Need to decide what to do next, call the model. It feels modern and it is almost always wrong, because it treats four fundamentally different kinds of computation as if they were one.
There are four techniques, and each has exactly one correct home.
A rule engine owns any question with one machine-computable, reproducible answer. Is this valid syntax? Is the payload under the size bound? Does this reference resolve? Is the structure connected? These are not opinions. A parser answers them in microseconds, for free, and is right every single time.
A state machine owns lifecycle and flow, the questions where what happens next depends on where you are now and only some transitions are legal. A job goes pending → running → done | failed | timeout. A retry goes closed → open → half-open. The point of making this explicit is that it kills an entire bug class, the one you get from ad-hoc boolean flags, where a job somehow transitions from completed back to running because two flags disagreed.
An LLM owns probabilistic judgement, and only that: the questions that are genuinely subjective and that no rule can answer. Is this writing coherent? Does this read as the thing the prompt asked for? Is it good versus merely acceptable? There is no formula for “cozy.” Something has to make a perceptual call, and for the parts a rule cannot reach, that something is a model.
An agent, an LLM in a loop choosing its own next actions, owns far less than the current enthusiasm suggests, and in a large class of systems it owns nothing at all. We will get to why.
The reason the boundary matters is that placing work in the wrong tier is expensive in both directions, and people usually only notice one.
Put an LLM where a rule belongs and you get the obvious costs, slower and more expensive, plus a subtle one that is worse: it is less correct. An LLM can hallucinate a syntax verdict. A parser cannot. You have taken a question with a perfect deterministic answer and handed it to the one tool in your kit that can be confidently wrong about it. That is not a trade of accuracy for flexibility. It is strictly worse on every axis.
Put a rule where the question is genuinely subjective and you get the opposite failure: the rule simply cannot answer, so you either ship a gate that misses the thing it exists to catch, or you contort a threshold into pretending a taste question is arithmetic. Both are ways of lying about the nature of the problem.
Getting the boundary right is not a matter of preference. It follows from what each tool is.
The test that draws the line
There is a single question that sorts most work into the right tier, and it is worth memorizing:
Would two informed people always reach the same answer from the data alone?
Run any criterion through it and one of three things happens.
Yes, always. The answer is a mechanical fact: a count, a dimension, a material, a connectivity check. This is the rule engine’s territory, no exceptions. If two experts with the raw data in front of them cannot disagree, there is nothing for a model to add except cost and the possibility of error.
Not from the raw data, but the criteria can be articulated. Two reviewers might land in slightly different places, but they can explain what they are looking at, and that explanation can be written down as an explicit standard. This is the model’s territory, but with a crucial constraint: the model’s job is not ineffable taste, it is reasoning over that written standard, and the reasoning is the product. A judgement that hangs on a named criterion is auditable and improvable. A bare number is neither.
The judgement is real, but nobody has written it down yet. The reviewer knows excellent when they see it and cannot articulate why. This is the bucket everyone skips, and skipping it is the most common way an LLM judge silently fails. If you hand this straight to a model, it does not decline to answer. It scores against its own generic, unstated taste, and you get something reliable against the wrong ruler. The work here is elicitation: dragging the tacit standard out of the domain owner and into explicit criteria, at which point it becomes the second bucket and a model can apply it. Until that happens, the honest move is to route it to a human and log what you could not yet express, not to let the model paper over the gap.
The three buckets are not academic. They are the difference between a judge that applies your standard and a judge that applies its standard while wearing your logo. Most of the effort in building a trustworthy judge goes into the third bucket, the elicitation, and almost none of the discourse does.
The line that actually matters: control versus reasoning
Now the hard part, the one the whole law turns on.
The instinct, once you have accepted “determinism where you can,” is to read the LLM tier as narrow: one call, one score, get in and get out. That is the wrong lesson, and it leaves enormous value on the table. The boundary is not about limiting how much the model reasons. A model can reason at length, chain several steps, weigh evidence, propose a plan, and all of that can be perfectly sound engineering. The boundary is about who owns control.
Reasoning is the model producing a judgement, a diagnosis, a plan, a score, as a step inside a flow that something else drives. Control is deciding what step happens next and when the work is done. An agent is precisely a model that has been handed control: it chooses its own actions, re-runs stages at will, and self-determines completion. That single property, self-direction of control, is the thing to withhold.
Say it as a rule: let the model propose, keep the disposing with the orchestrator.
- Fine: the model emits a diagnosis, a suggested fix, and a confidence. The orchestrator decides whether to apply it, retry, or escalate.
- Fine: the model reasons through five criteria and returns a structured verdict for each.
- Not fine: the model decides which criteria to check, re-runs whichever stages it feels like, and announces on its own that it is finished.
The cleanest place this distinction shows up is function calling, because the same API serves both sides of the line. Using a tool schema to make the model emit a structured result, to fill a form with a sub-score and the fact it relied on, is just an output format, and it is fine. Letting the model choose which tools to call, in what order, and when to stop is a control loop, and that is what makes it an agent. Tool schemas appearing in your code tells you nothing. Who owns the loop tells you everything.
Here is the part that makes the whole thing click, and it is an argument from engineering, not from taste. Handing control to a model is only worth it when choosing the next step actually requires intelligence. In a surprising amount of real work, it does not. If the list of things to check is fixed, and each check is a static, design-time mapping from criterion to computation, then there is nothing to decide. The control flow carries no intelligence. Handing it to the model in that case buys you exactly nothing and costs you a great deal: non-reproducibility, latency, token spend, and a fresh set of failure modes, loops that never terminate, checks silently skipped, tools hallucinated. You pay the full price of an agent for a control flow a for loop expresses perfectly.
So the question is never “could this be an agent?” Almost anything could be. The question is “does choosing the next step here require judgement that is not known at design time?” Reserve autonomy for the cases where the answer is genuinely yes: open-ended or branching work whose shape you cannot lay out in advance, or an exploration phase whose entire purpose is to discover the steps, which you then harden into a fixed pipeline. Everywhere else, a fixed loop with a model reasoning inside it is not a compromise. It is the better design on its own merits, before you even invoke reproducibility.
There is one seam worth leaving open, because it is the place agency earns its keep even inside a fixed pipeline: the unexpected. If the model, while judging, notices something worth checking that is not on the static list, let it propose an extra check. The orchestrator still decides whether to run it. That places the model’s intelligence where it actually helps, noticing the unanticipated, and keeps it away from the part that needs none, selecting among known checks. Bounded, and still not driving.
The same boundary on the generation side
Everything so far has been about inspection: taking an output and deciding whether it passes. But this is not an evaluation-only law. It governs generation just as strictly, and the mistake there is the mirror image. The default way to make a model produce an artifact is to ask it for the whole thing from a blank canvas, which hands the entire construction to the sampler and maximizes exactly the surface the companion pieces warned about.
The disciplined version applies the same split. Let the model do the subjective, creative part, planning, choosing, proposing parameters, and let a deterministic layer execute that plan against a pre-validated structure. A model that plans a bounded edit to a known-good template, with a rule engine applying that edit inside the template’s constraints, inherits the template’s validity and its taste, and confines the model to a change small enough to stay valid. The invalid result becomes unreachable instead of caught downstream, which is the strongest form of the funnel from Shrink the Stochastic Surface: the cheapest possible check is making the wrong output impossible to emit. Same boundary, same reason. The model proposes, deterministic structure disposes, whether the thing being produced is a verdict or an artifact.
The case that makes it obvious: never put an agent in the gate
If you want a single place to feel this law in your gut, look at evaluation.
An evaluator, a quality gate, a validator, anything whose job is to render a verdict, has one non-negotiable property: same input, same verdict. Reproducibility is not a nice-to-have for a gate; it is the whole basis of trust in it. A gate that rejects someone’s work with 0.42 today and 0.71 tomorrow for the same input is unappealable, undebuggable, and uncalibratable. Nobody can act on it, including you.
Now notice what an agent is: a thing whose defining virtue is self-directed adaptivity, whose whole selling point is that it decides for itself what to do next based on what it sees. That is a wonderful property for an explorer and a catastrophic one for a ruler. An evaluator that decides for itself what to check next is non-reproducible by construction. Its adaptivity, the exact feature you would be buying it for, is the thing that destroys its authority.
This is the irony of the current moment. The industry is racing to make everything agentic, and the single most seductive place to reach for an agent, “just let the smart model look at the output and judge it,” is one of the places it is most wrong. The correct relationship between an agent and a gate is not that the gate is an agent. It is that the gate watches the agent. Measure the agent’s behaviour, count its convergence rounds, score its output, but the thing doing the scoring must be the opposite of an agent: fixed, predictable, the same ruler every time. You do not want your ruler to have opinions about how long it is today.
Where determinism and judgement collaborate: the rule as lie detector
The law is not “rules and models live in separate rooms.” Their most valuable interaction is when they work on the same judgement, and it is the mechanism that turns an LLM judge from a black box into something auditable.
Start from the honest position: the subjective part is irreducible. No rule computes “harmonious” or “coherent.” A model has to produce that number. But producing the number is not the same as being trusted for it, and this is where the rule engine earns a second job.
When the model scores, make it state the evidence it relied on. Not just “this summary is inaccurate, 0.3,” but “this summary is inaccurate because it claims the report recommends option B.” Now that stated evidence often reduces to a checkable fact, and a deterministic check verifies it. Does the source document actually recommend option B, or does a string search show it never mentions B at all? Does the code the model called unsafe actually reach the unchecked path, or does a static check show the guard is present? Does the answer it praised for citing three sources actually cite three, or is it one repeated? Whenever the model’s load-bearing evidence is a fact, the rule engine confirms or refutes it, and a score built on a refuted fact is voided.
The division of authority is precise, and neither side can do the other’s job:
- The model has the scoring power. It produces the perceptual judgement and names the fact it rested on. The rule engine cannot author a quality score, because “harmonious” is not computable, so it never scores.
- The rule engine has veto power over the evidence. It verifies the cited fact and can void a score built on a false one, but it cannot replace that score with a number of its own.
So neither is solely decisive. When the cited fact checks out, the model’s score stands. When it is refuted, the rule does not overwrite the score, it invalidates it and routes to a bounded re-judge or a human. The judge becomes trustworthy not because you have decided to trust it, but because its evidence can be deterministically refuted. That is the difference between “we believe the model” and “the model cannot lie about the parts that are checkable.” Only the second one is engineering.
What it looks like assembled
None of these techniques is interesting alone. The value is in how they compose, and the composition has a shape: a deterministic spine that owns the loop, with the model hanging off it as a called reasoning node that proposes and never drives.
Read one concrete gate end to end. Content arrives. Cheap rules run first, syntax and schema and size, and anything that fails dies immediately for free. What survives enters a lifecycle state machine that governs the run. The orchestrator then calls the model as a judge: it reasons over the standard and returns per-criterion sub-scores plus the facts it relied on. Those facts go straight to a rule fact-check, which refutes any that are false and sends the judgement back for a bounded re-score. Only what clears both the rules and the verification becomes a verdict.
flowchart LR
IN([content in]) --> RULE1
RULE1["cheap rules
syntax · schema · size"] -->|fail fast| OUT
RULE1 -->|pass| FSM
FSM["lifecycle FSM
submit → running → terminal"] --> CALL
CALL["orchestrator
calls judge"] --> FACT
FACT["rule fact-check
refute false evidence"] -->|refuted, bounded re-score| CALL
FACT -->|confirmed| OUT([verdict])
JUDGE["LLM judge · reasoning node
proposes sub-scores, cites facts
no control, no loop"]
CALL -.request.-> JUDGE
JUDGE -.proposal + cited facts.-> FACT
classDef rule fill:#f0fff4,stroke:#2f855a
classDef llm fill:#fef5e7,stroke:#b7791f
classDef fsm fill:#ebf8ff,stroke:#2b6cb0
class RULE1,FACT rule
class JUDGE llm
class FSM,CALL,OUT fsm
Notice what never happens in that picture: the model never holds the arrow that decides what runs next. It is called, it reasons, it returns. Every solid line, the loop itself, stays with the orchestrator. The dotted lines are the model’s entire role, a request out and a proposal back. That is the whole law in one diagram.
The ordering falls out for free
One more thing drops out of the boundary without any extra design, and it is the reason the whole arrangement is also the cheap one.
Deterministic checks cost microseconds and no money. Model calls cost seconds and real dollars. So the boundary hands you the correct ordering by default: run the cheap, reproducible gates first. They weed out the obviously broken before any paid work happens. The expensive judge runs last, only on content that already cleared everything cheap. You never spend a model call scoring something that fails a syntax check, which would be burning money to grade garbage.
Cost then scales with quality instead of volume: bad inputs die cheap at the front, and you only pay the expensive price for things good enough to deserve it. This is the same funnel shape the stochastic-surface piece described for reliability, and it is not a coincidence that the reliable arrangement and the cheap arrangement are the same arrangement. They are both consequences of putting each question in the tier that fits it.
Reserve the seams, so reasoning stays additive
The last clause of the law, control never to the agent, is the one most likely to be misread as “never add model reasoning to the loop.” It is not. It is “add it without moving control into a loop the model owns.” Those are different, and keeping them different is what lets a deterministic system grow bounded intelligence later without a rewrite.
If you expect to add diagnosis, repair recommendation, or deeper failure analysis down the line, build the seams now, cheaply:
- Replayable events. Every transition and finding emitted as a durable, ordered record, so a later reasoning step can replay exactly what happened instead of guessing.
- Independently addressable stages. Keep the pipeline decomposed, so a diagnosis step can point at one stage without unpicking the whole run.
- A read-only retrieval seam. A way to pull prior records and context as evidence for a diagnosis, without letting the diagnosis mutate the verdict path.
- Post-verdict hooks. Failure analysis runs after the reproducible verdict, never inside it, so it can never change the result it is analysing.
- Advisory output only. A repair step produces a suggested fix, a rationale, and a confidence. Whether to apply, retry, or escalate stays the orchestrator’s call.
The invariant across all of them is the law restated: repair and retry are controlled by the orchestrator, not by a model acting on its own. Anything that would move control into an autonomous loop is not a small extension, it is a change to the fundamental property of the system, and it should be treated as one.
The same law, one level down
These three pieces are one argument at three depths. The first said correctness comes from structure around the generator, not from a bigger generator. The second said the engineering is minimizing the fraction of output only the model owns. This one says: of the part you keep deterministic, choose the determinism that fits the question, a rule for facts, a state machine for flow, and of the part you keep probabilistic, let the model reason as much as the problem needs but never let it hold the wheel.
The probabilistic part proposes. The deterministic part disposes. And control, the decision of what happens next and when the work is done, is deterministic, always. That is not a constraint on what your system can do. It is the thing that makes what it does trustworthy, reproducible, cheap, and yours to debug at three in the morning when it matters.
Determinism where you can. Judgement where you must. Control never to the agent.
This is the third piece in the arc. The first: Generative AI Builds Shapes, Not Games. The second: Shrink the Stochastic Surface.
Related, the same law at other layers: Agent Memory Is a Cache Coherence Problem and Agent Architecture Is a Compute Allocation Problem: The Advisor Strategy.
🎧 More Ways to Consume This Content
I occasionally advise small teams on backend reliability, Go performance, and production AI systems. Learn more: /services
Comments
This space is waiting for your voice.
Comments will be supported shortly. Stay connected for updates!
This section will display user comments from various platforms like X, Reddit, YouTube, and more. Comments will be curated for quality and relevance.
Have questions? Reach out through:
Want to see your comment featured? Mention us on X or tag us on Reddit.