How to Make LLM Numeric Judgments Safe with Model Consensus
A single model can produce numbers that look right and are wrong. Cross-check two independent models and recompute the result in code before you trust it.
A language model that summarizes a paragraph can be a little wrong and still useful. A model that computes a threshold, validates an amount, or checks that two figures reconcile does not get that grace. A number is either correct or it is not, and a plausible wrong number is worse than an obvious one, because it passes review. This post describes a pattern that treats a model's numeric output as a claim to be checked, not a source of truth: run the same judgment through two independent models, accept the number only when they agree exactly, and recompute the result in plain code before it reaches anything that matters.
Language is forgiving, numbers are not
Most of what LLMs do well lives in a tolerant space. If a summary drops a nuance or a rewrite picks a slightly odd word, a reader still gets the meaning, and nothing downstream breaks. Natural language has redundancy built in, so small errors wash out.
Numeric judgment has none of that slack. When a model decides that a transfer of 1,050,000 is within a limit of 1,000,000, or that a discounted price rounds to one value when it rounds to another, there is no partial credit. The output feeds a gate, a ledger, or a control flow branch, and a single wrong digit changes the outcome completely.
The failure modes that make this dangerous are specific and repeatable:
- Hallucinated confidence. The model states a figure with the same fluent certainty whether it derived it correctly or invented it. Nothing in the tone tells you which one happened.
- Digit and magnitude errors. A model can drop a zero, transpose two digits, or land an answer one order of magnitude off while the surrounding sentence reads perfectly.
- Unit and currency confusion. Percentages read as fractions, minutes read as seconds, one currency treated as another. The arithmetic is internally consistent and still wrong because the unit was wrong.
- Boundary mistakes. Off-by-one behavior at a threshold, or an inclusive limit treated as exclusive, so a value that should be rejected slips through.
These are exactly the errors a fluent single answer hides best. The sentence is well formed, the reasoning sounds right, and the number is wrong.
Treat the model output as a claim, not an answer
The core shift is to stop asking a model for the answer and start asking it for a claim you will then test. Four rules turn a probabilistic generator into a safe gate.
- Cross-check two independent models and accept only when they agree.
- Recompute the number in deterministic code and reject any mismatch.
- Force structured output so the answers can be compared exactly.
- Isolate the model to a pure judgment, with no tools and no external state.
None of these trusts the model on its own. Each one assumes the output might be wrong and builds a check around that assumption. The rest of this post takes them one at a time.
The consensus gate: agreement or stop
The first rule is a unanimous gate, not a vote. Send the identical judgment to two independent models, with no shared context between them, and compare their answers. If both produce the same number and the same unit, treat that as a provisional accept. If they disagree by any amount, stop and hold the case for a human or a fallback path.
The word unanimous matters. This is not majority rule where the most common answer wins. With numeric safety, a disagreement is the most valuable output of the whole process. It means at least one model is wrong here, and you cannot yet tell which. Resolving that split in favor of the majority throws away the exact signal you paid for. The gate should fail closed: when in doubt, do not pass the number through.
Independence is what gives the gate its power. Two models from the same family, or the same model queried twice, tend to make the same mistake in the same place, so agreement between them proves little. Two genuinely different models trained on different data have different blind spots, so for both to produce the identical wrong number by chance is far less likely. You are not counting votes, you are checking whether two separate estimators land on the same point.
def consensus_gate(task, model_a, model_b):
a = model_a.judge(task) # structured: {"value": ..., "unit": ...}
b = model_b.judge(task) # same task, no shared context
if a is None or b is None:
return HOLD # a model failed to answer, fail closed
if a.value != b.value or a.unit != b.unit:
return HOLD # disagreement is a risk signal, escalate
return Accept(a.value, a.unit) # both agree, provisional accept
Notice that agreement here is only a provisional accept, not a final one. Two models can share a blind spot and be wrong together. The gate lowers that probability, it does not remove it, which is why the next rule exists.
Recompute the number in deterministic code
Consensus filters out most single-model errors, but it still leaves you trusting a number that a model produced. The second rule removes that trust: whenever the answer can be derived by ordinary computation, derive it, and use the model only for the parts that genuinely need judgment.
A useful way to think about it is a division of labor. The model is good at the fuzzy front of the problem: reading a messy input, deciding which rows are relevant, classifying an ambiguous field. It is weak at guaranteeing that a total is exact. So let the model do the reading and the selecting, then compute the arithmetic yourself and check the model's claim against your own result.
The deterministic layer should enforce three kinds of check. Parse the value into an exact type, never a float for money. Range-check it against known bounds. And assert the invariants that must hold regardless of what any model said, such as parts summing to a whole.
from decimal import Decimal
def verify(value, unit, source_rows):
# 1. parse into an exact type, never a float for money
amount = Decimal(value)
# 2. range check against known bounds
if not (MIN_AMOUNT <= amount <= MAX_AMOUNT):
raise Reject("value is outside the allowed range")
# 3. invariant: the parts must sum to the whole
computed = sum(Decimal(r.amount) for r in source_rows)
if amount != computed:
raise Reject(f"sum mismatch: model said {amount}, code computed {computed}")
# 4. unit must match what the pipeline expects
if unit != EXPECTED_UNIT:
raise Reject(f"unexpected unit {unit}")
return amount
The mismatch branch is the important one. When the recomputed total disagrees with the model's number, the code does not average them or prefer one, it rejects. A discrepancy means either the model is wrong or the input the model read is not the input the code read, and both cases need a human before any number ships. This is the layer that catches a magnitude slip that both models happened to share.
Force structured output so the numbers can be compared
You cannot compare two answers, or recompute one, if the model buries the number inside a sentence. The third rule is to require structured output with a fixed schema, and to forbid free prose in the fields that carry values.
A schema does three things at once. It makes the comparison in the consensus gate an exact match on typed fields rather than a fragile parse of English. It gives the deterministic layer a clean value to check instead of a phrase it has to interpret. And it narrows the surface where a model can be creative, which is precisely where numeric drift sneaks in.
Ask for output shaped like this, and reject anything that does not fit:
{
"value": "1050000",
"unit": "KRW",
"basis": "sum_of_line_items",
"confidence": "high"
}
Keep the numeric value as a string in transit and parse it into an exact decimal type on arrival, so no floating point rounding creeps in between the model and your check. Treat a response that is missing a field, adds commentary, or wraps the number in explanation as a failed response, the same as a disagreement. A model that will not answer in the required shape has not answered.
Isolate the model from tools and state
The last rule is about what the model is allowed to touch. For a numeric judgment, give it nothing but the input and the question. No tools, no ability to call out to other systems, no shared memory with the other reviewer, no write access to anything.
Isolation buys two things. It keeps the two models genuinely independent, since neither can see the other's reasoning or anchor on a shared intermediate result, which is what makes their agreement meaningful. And it keeps the blast radius at zero: a model that can only return a structured claim cannot act on a wrong number, it can only propose one, and every proposal passes through the gate and the recheck before anything happens. The model is a sensor, not an actuator. It reports a reading, and code decides what to do with it.
Single model versus the consensus gate
The pattern adds moving parts, so it helps to see plainly what those parts buy against a single call.
| Dimension | Single model | Two-model consensus gate |
|---|---|---|
| Silent wrong number | ships downstream | blocked unless both agree and the recheck passes |
| Confident hallucination | often accepted | caught unless both share the exact error |
| Cost per judgment | one model call | two or more model calls |
| Latency | one model | the slower of the two |
| On disagreement | nothing to notice | flagged and held for a human |
| Best fit | reversible, low-stakes text | money, thresholds, reconciliation |
The single-model row is not wrong for every job. It is wrong for the jobs where a plausible bad number does real damage.
What it costs, and when to skip it
The gate is not free. You pay for two or more model calls per judgment instead of one, and you wait for the slowest of them, since they run in parallel but the last one home sets the pace. There is engineering cost too: a schema to maintain, a deterministic checker to write, and a hold path for disagreements that a human or a fallback has to handle. For a high-volume, low-value stream of judgments, that overhead can outweigh the benefit.
So match the pattern to the stakes. The full gate earns its cost when a wrong number is expensive and hard to walk back:
- Money paths. Amounts, limits, refunds, anything that moves value or gates a transaction.
- Thresholds and controls. A computed cutoff that decides whether something is allowed.
- Reconciliation. Checking that two independent figures agree, where a false pass hides a real break.
- One-way outputs. A number that gets written to a ledger or sent to a partner and cannot be quietly corrected later.
For the opposite case, skip it. A model that suggests a rough estimate for a human to eyeball, a summary of a report, a draft that a person will edit anyway, none of these need two models and a decimal checker. Running the full gate on a throwaway estimate is wasted latency and spend.
The honest limit is the shared blind spot. Consensus lowers the chance of a wrong number, it does not drive it to zero, because two models can be trained on the same flawed pattern and repeat it together. That is why the deterministic recheck sits behind the gate and why a human stays on the disagreement path. The model is strong at generating a candidate and weak at guaranteeing it is exact. Consensus, deterministic recomputation, and fail-closed handling turn that weak guarantee into a gate you can put in front of a number that has to be right.
Related posts
Why a Different Model Should Review the Code an AI Wrote
A model reviewing its own output shares its own blind spots. Route the diff to independent models from other providers and cross-check the results.
How Session Handoffs Keep Context Across AI Coding Sessions
AI coding agents forget everything when a session ends. A short handoff note carries what you did, what is unfinished, and what comes next.
Prompt Injection Defense for Agents That Read Untrusted Text
An agent that reads web pages and files can be hijacked by hidden instructions inside them. Here is the architecture that stops prompt injection.
Use an AI Agent as Your Shell Front End, Not More Aliases
The graveyard of forgotten shell scripts has an alternative. Describe the task in plain words and let an AI assemble grep, awk, and jq for you.