Skip to content

16 min read

Two instances of one model share the objection neither raises

On this page

A platform team runs proposed migrations through a two-agent review before they reach production. The migration under discussion adds a status column to a 340-million-row orders table on PostgreSQL 14, with DEFAULT 'pending' NOT NULL. The agent arguing safe opened by pointing at the small size of the change. The agent arguing unsafe replied that adding a column with a default rewrites the entire table under an ACCESS EXCLUSIVE lock, which on a table that size would take the orders service down for something between twelve and forty minutes. The safe agent accepted that premise and moved to a fallback position about running it inside a maintenance window with lock_timeout set. Two rounds later the judge recorded a verdict of unsafe, requires scheduled downtime, with high confidence, and the migration sat in the queue for nine days waiting for a window.

PostgreSQL has not rewritten the table for a non-volatile default since version 11. On PG 14 the statement takes a brief ACCESS EXCLUSIVE lock to update the catalogue, stores the default in pg_attribute, and returns in milliseconds. Both agents believed the pre-11 behaviour, neither had any reason to question it, and the entire two-round exchange was an argument about how to mitigate a rewrite that was never going to happen. The transcript reads as a careful, adversarial review. It contains no sentence that is false about the world except the one both parties assumed.

Opposite instructions do not produce opposite priors

The intuition that debate covers more ground than a single pass comes from a picture of two people with different backgrounds arguing, where each one's experience fills the other's gaps. That picture is an analogy, and it is worth saying where it stops holding rather than leaving it to do silent work. Two human reviewers differ in what they have read: one has operated a database through a major-version upgrade and the other has not, so the objections available to them are drawn from two different stocks. Two instances of one model differ in what they were told to conclude. The stock of objections is one stock, opened twice.

What the protocol actually assigns is a conclusion to defend, and a conclusion is not a background. Both agents are the same weights, holding the same associations between "add column with default" and "table rewrite", drawn from the same corpus in which a decade of pre-11 blog posts outnumber the release note that changed it. Instructing one of them to argue the opposite verdict changes which conclusion it steers towards; it does not swap out what it knows.

It is worth walking the causal chain under that, because each link is checkable and skipping them leaves a reader who can recite the rule and cannot predict the next case. A model's output is a sequence of high-probability continuations given its input. Which continuations are high-probability is fixed by the weights, and the weights were fitted to a corpus in which the pre-11 behaviour was described far more often than the change to it. So the association from "add column with default" to "table rewrite" is strong for both instances, because both instances are the same weights. A role instruction enters as more input, and more input reweights which continuations are reachable from here — it cannot reach a continuation the weights make improbable in the first place. The safe agent therefore steers towards a safe verdict by finding a mitigation for the rewrite, which is the nearest safe-flavoured continuation available, rather than by denying that the rewrite occurs. Denying it was never a nearby option for either of them.

The consequence is specific enough to state as a rule. In a debate between two instances of one model, the space of objections that can be raised is the space of objections that model can raise, and assigning sides partitions that space by conclusion rather than expanding it. An objection that neither instance would produce when asked the flat question will not appear because one of them was told to disagree. And because the exchange then proceeds normally — claims, rebuttals, concessions — every round after the first is built on the unraised premise, which is why the output has the texture of thoroughness. The reasoning is genuinely careful, conditional on a shared error.

Measuring the overlap instead of assuming it away

You can find out how correlated a pair is without a labelled production set, using seeded defects. Take twenty real inputs from your own traffic, inject a known flaw into each one from a small set of defect classes, and run the debate. The metric that matters is not the pair's accuracy; it is the conditional probability that the second agent misses a defect given that the first one missed it, because that is the quantity the whole design assumes is low.

The reason that particular quantity is the one to chase comes out of an identity that holds whatever the agents are doing. The chance that both agents miss a given defect is the chance the first one misses it, multiplied by the chance the second one misses it given that the first did. That is not an assumption; it is what a conditional probability means, and it can be read as a definition of the second factor. Its usefulness is that it splits the pair's joint failure into a part you already had — the first agent's own miss rate, which is just the single-pass failure rate — and a part that is entirely a property of the second agent's relationship to the first. Everything the second call is worth lives in that second factor, and nothing about running two calls instead of one makes it small.

Two things the identity does not license are worth naming while it is in view. It is true for any pair, so measuring it well never confirms that a design is sound, only how far from independence that particular pair sits. And the conditional factor is estimated on the subset of cases where the first agent missed, which on a competent model is a small subset, so the estimate is noisy in a way the marginal rate is not. Both of those push in the same direction: treat the measured number as a floor on how correlated the pair is rather than as a point estimate of it.

The platform team did this over 25 seeded migrations, five per class, with two configurations: both debaters on the same model, and one debater swapped for a different vendor's model of comparable capability.

Seeded defect classSame-model pair: caughtCross-family pair: caughtMissed by both, same-model
Missing CONCURRENTLY on a large index build5/55/50
Backfill in one transaction over 340M rows5/55/50
NOT NULL added before the backfill completes4/55/51
Foreign key added without NOT VALID first2/54/53
Enum value added inside a transaction that also uses it0/52/55

Read the bottom two rows rather than the top two. On the defect classes where the model is strong, both configurations catch everything and the second debater contributed nothing detectable. On the classes where it is weak, the same-model pair misses together — five out of five on the enum case, where both agents produced a confident safe verdict and neither mentioned the transaction boundary.

The way to read that table is by the gaps between its columns rather than by the height of any single cell, because a high catch rate on a class the model finds easy tells you about the class and not about the design. Take the first two columns row by row. On the top two classes the gap is nothing at all. On the third it opens by one case, on the fourth by two, and on the last by two more from a base of none. The gap is the marginal contribution of the second model family, and it is zero exactly where the same-model pair already scores well and largest where it scores worst. That ordering is the finding hiding inside the table: the second agent buys coverage in proportion to how badly the first one is doing, which is the opposite of what a design justified by "two heads" would predict, and it is the same statement as the identity above with the roles of the two factors made concrete.

Across the whole 25-case set the same-model pair's conditional miss rate was 0.86: given one agent missed the seeded defect, the other missed it too 86% of the time. The cross-family pair came in at 0.61, which is a real improvement and is nowhere near the independence the design's intuition assumes.

Computing that number is a dozen lines, and it is worth having in the same repository as the debate loop rather than in a notebook someone ran once:

python
def conditional_miss_rate(runs: list[dict]) -> float:
    """runs: [{'a_caught': bool, 'b_caught': bool}, ...] over seeded cases."""
    a_missed = [r for r in runs if not r["a_caught"]]
    if not a_missed:
        return float("nan")
    both_missed = [r for r in a_missed if not r["b_caught"]]
    return len(both_missed) / len(a_missed)

def marginal_miss_rate(runs: list[dict]) -> float:
    return sum(1 for r in runs if not r["b_caught"]) / len(runs)

# Independence would put these two numbers close together.
# same-model pair:   conditional 0.86, marginal 0.32
# cross-family pair: conditional 0.61, marginal 0.28

The two functions differ in one place and it is the denominator, which is the whole reason both are needed. conditional_miss_rate divides by a_missed, the cases where the first agent already failed. marginal_miss_rate divides by every case that was run. They are therefore rates over two different populations, and comparing them is only meaningful because the smaller population is a subset of the larger one: the cases where both agents missed are, by construction, cases where the second agent missed. Under independence, restricting to the subset would tell you nothing new and the two rates would agree. Any gap between them is the amount by which knowing about the first agent's failure changes what you expect from the second.

The gap between 0.86 and 0.32 is the whole finding. If the agents failed independently, knowing that one missed a defect would tell you almost nothing about the other, and the conditional rate would sit near the marginal one, the marginal rate being how often the second agent misses across all the cases without conditioning on what the first one did. Instead, one agent's miss predicts the other's nearly nine times in ten, which means the second agent is supplying far less new coverage than its cost implies.

One caution about generalising from a seeded set, because it is the caution that makes the number honest. Every defect in that table is from a class somebody was able to name in advance in order to inject it. The defects that produce the expensive incidents are the ones nobody named, and there is no reason to expect the pair to be less correlated on those than on the ones an engineer thought of. The measurement is a floor on correlation for the same reason the identity above is a floor: it is estimated on the failures you could arrange to observe.

Why one model debating itself helps at all

The origin paper ran all of its agents on the same model and still measured real gains, which sits awkwardly next to everything above until you look at what its benchmarks were made of. The improvements came on grade-school arithmetic, chess move validity and factual recall about people — tasks where a large share of the residual errors are slips rather than gaps. A slip is a step executed wrongly on this particular draw: a carry dropped, two operands transposed, a name attached to the wrong date in the middle of a chain the model could otherwise do. A gap is something the model does not have, or has in an outdated form, and will reproduce identically on every draw.

Slips decorrelate almost as well as coin flips — to decorrelate is for two agents' mistakes to stop landing on the same cases, so that one being wrong stops predicting the other — because they are produced by the sampling process rather than by the weights' content. The coin-flip comparison is a picture rather than a claim, and the place it breaks is worth marking, because it is where a reader would over-apply it. Coins are re-flipped. A slip only gets a second draw where the second agent actually re-executes the step from the same starting point; once the first agent's wrong intermediate result is written into the transcript, the second agent is more likely to carry it forward than to recompute it, and a slip that has been read is no longer a fresh draw. What decorrelates is the re-derivation, not the mistake.

When the opposing agent recomputes the same step, it lands somewhere else, the two answers differ, and the disagreement points at the exact line where they diverge — which is precisely the situation where an argument can be driven to a checkable conclusion. Gaps do not decorrelate at all. Both agents reach for the same wrong rule, agree, and the protocol has nothing to work with.

So the question to ask before assuming the published result transfers to your task is which kind of error dominates the cases you are still getting wrong. A cheap way to find out is to sample the flat prompt five times at temperature 0.8 and look at the disagreement rate on the cases you know are wrong. If the five draws scatter, the residual is slip-heavy and an adversarial exchange has something to bite on. If all five produce the same wrong answer with the same reasoning, the residual is gap-heavy and no arrangement of that model's own outputs will recover it.

The migration reviewer answered this clearly. On the enum-in-transaction case, five draws of the flat prompt returned safe five times, with the same two supporting sentences each time. On a handful of long multi-statement migrations where the model mis-ordered the steps, the five draws produced three different orderings, and those are the cases where the two-agent exchange did in fact catch something. One task, both regimes, and the debate was worth its cost on a small subset that nobody had identified because the aggregate accuracy number hid the split.

Temperature, vendor swaps, and what a reviewer's approval is worth

Correlated failure is not evidence that the model is weak. The migration pair caught missing CONCURRENTLY and unbatched backfills every time, which is genuinely useful work. The problem is narrower: the second call buys coverage only in proportion to how differently the second agent fails, and on a same-model pair that proportion is small and unmeasured by default.

Raising the temperature does not fix it either, which is worth saying because it is the cheapest thing to try. Temperature widens the distribution of surface forms and reorders low-probability tokens; it does not install a fact about PostgreSQL 11 that was never learned. The enum defect stayed missed at 0.2 and at 1.0.

That pair of settings is the clearest instance of the mistake this chapter is about, and the reason it is tempting deserves stating rather than mocking. Temperature is the one knob in reach, it demonstrably changes the output, and on a slip-heavy task turning it up genuinely does help. So there is a real effect, in a real place, being applied to a different kind of failure. What the enum result shows is a quantity that stayed exactly where it was across the entire usable range of the knob, while the property that decides the outcome — whether the model holds the fact at all — never varied because nothing in the knob touches it. Any defence that leaves a number flat while the thing you are defending against moves freely underneath it is a defence against something else.

Splitting the debaters across two model families is a real improvement, and the 0.86-to-0.61 drop is what it looks like. It is also bounded in a way that is easy to overstate, because the two families are trained on largely overlapping web text, tuned against similar preference data — recorded human judgements about which of two answers is better, used to shape a model's behaviour after the main training run — and shaped by similar conventions about what a confident answer sounds like. Decorrelation from a vendor swap is partial, and it comes with its own bill: two provider integrations, two rate-limit budgets, two sets of prompt behaviour to maintain, and a fair amount of variance in output format. It is a reasonable thing to reach for once you have established that the debate is doing something. Reaching for it before that establishes only that a system nobody has evaluated now costs more to run.

The most uncomfortable part of this chapter is what it implies about reviewer feedback. A team whose reviewers describe debate output as thorough has learned that the transcripts are coherent, address counterarguments, and read like careful work. Every one of those properties is present in the migration transcript above, and the verdict was wrong. Reviewer satisfaction is compatible with a system producing well-argued wrong answers at scale, and it is compatible with one producing right answers, which means it separates nothing.

What the chapter leaves you with is one number and one habit. The number is the conditional miss rate, which converts the design's central assumption from an intuition into something a seeded set can report in an afternoon, and which will come back near the marginal rate only if the two agents genuinely fail in different places. The habit is to read any pair's result by its gaps — between conditional and marginal, between one model family and two, between the defect classes the model is strong on and the ones it is not — because a level tells you about the task and a gap tells you about the design.

That leaves the component that turns an exchange into an output. The judge in the migration case saw two agents agreeing about the size of a table rewrite and recorded high confidence, and it did so from the same model family as both debaters, reading nothing but the transcript. What it can and cannot assess is the subject of the next chapter.

Debate and adversarial ensembles — read the current version at https://daviddominguez.dev/learn/en/debate-and-adversarial-ensembles/two-instances-of-one-model-share-the-objection-neither-raises

Back to top