WikiDesignCo THE GIGA LIBRARY · ∞ STACKS Request a stack
← The Library
Essay 03 of 12 · Epistemics

The Contradiction Engine: How to Hold Three Disagreeing Experts Without Going Insane

Most knowledge systems paper over conflict with averaged confidence. The right ones structure the disagreement itself.

If you ask three serious people in any technical field a hard question, you will get three answers. That spread is the actual structure of expertise. It marks the boundary at which a field is still working things out, the surface at which novel work happens, the live frontier where the experts have looked at the same evidence and come to different conclusions because the evidence underdetermines the conclusion. Cricket pitches and chess openings are settled. Optimal learning rates for transformers, the correct activation function for a specific architecture, whether a particular benchmark measures what it claims to measure: these are alive, and the disagreement around them is doing work. Any knowledge system that flattens them into a single confident answer is lying by construction.

Most knowledge systems lie by construction. They are built on the assumption that for every query, there is a single best answer, and the system's job is to retrieve it. When the system encounters three contradicting sources, it picks one, usually the one with the highest similarity score, which is to say the one whose vocabulary happened to match the query most closely, which is to say a feature of phrasing rather than truth. The user receives a confident answer. The user has no way to know that two other equally credible sources said the opposite. The system has done its job, as defined. The user has been quietly misled, also as defined.

Live Three defensible readings of the same evidence. The bar under each is how closely its wording matched the query, which is the only thing the score can see. One gets picked, the other two never surface, and what reaches the user is a single unmarked answer.

Stopping the lie takes a specific piece of machinery. The contradiction engine runs continuously over a knowledge graph, detects when two claims structurally conflict, classifies the type of conflict, creates the appropriate metagraph structures to represent the disagreement, and surfaces calibrated uncertainty to downstream agents. It is the subsystem that makes the difference between a knowledge platform that handles a contested field gracefully and one that confidently picks sides at random. It is also the subsystem that most teams underestimate, ship without, and regret later.

What counts as a contradiction

The first hard problem is definitional. What does it mean for two claims to contradict? At one extreme, you could be strict: only claims with identical entities, identical predicates, and explicitly opposite objects count. Python is faster than R and R is faster than Python would be a contradiction; Python is good for data work and R has better statistical libraries would not, even though in conversational context they might be experienced as conflicting views. At the other extreme, you could be loose: any two claims that have semantically opposed framings count, regardless of structural identity. The strict definition misses contradictions that matter. The loose definition flags non-contradictions and produces noise.

Live The dial runs from strict to loose across four real pairs. Tighten it and the two counters at the bottom show conflicts that matter going unflagged. Loosen it and a pair that was never a conflict joins the queue. Neither end is a setting anyone would ship.

The right answer, as usual, is a stratified approach. The contradiction engine has multiple detection layers, each tuned for a different precision-recall trade-off, each producing differently weighted candidates that downstream resolution can act on.

Layer one is structural contradiction. Two claims with the same subject, the same predicate type, and contradictory objects. (Karpathy, BELIEVES, scaling_is_sufficient = true) vs. (Karpathy, BELIEVES, scaling_is_sufficient = false). This is the cleanest signal, easiest to detect via Cypher pattern matching, and produces the highest precision flags. The engine should always surface these.

Layer two is semantic contradiction within a controlled vocabulary. Two claims with the same subject and a predicate that comes from a known antonym pair: SUPPORTS vs. OPPOSES, VALIDATES vs. FALSIFIES, RECOMMENDS vs. DISCOURAGES. The engine maintains a vocabulary of antonym predicates and detects when two claims about the same subject use opposing ones. Slightly noisier than structural, still useful.

Layer three is embedding-based semantic conflict detection. Two claims about the same subject whose conclusions embed far apart in semantic space relative to the typical distance between claims about that subject. This catches cases where the predicates differ but the substance conflicts. It is noisy, and the engine should treat its output as candidate contradictions for further classification rather than as confirmed flags.

Layer four is LLM-assisted detection on narrow queries. For high-stakes domains, the engine periodically issues queries to an LLM: "given these two claims about the same subject, do they contradict, and if so how?" The LLM's output is structured: a contradiction-confidence score, a classification, and a brief justification. This is the slowest and most expensive layer, and is reserved for review queues rather than continuous operation.

Together, these four layers produce a detection pipeline that catches most genuine contradictions while keeping false positives manageable. The key insight is that you do not pick one layer. You run all four, weight their outputs, and let the resolution layer prioritize which contradictions are worth surfacing to humans vs. which can be auto-resolved vs. which can be left to coexist as structured disagreements.

The four detection layers, and the trade each one takes
LayerWhat it matchesHow noisyWhen it runs
StructuralSame subject, same predicate type, contradictory objects. (Karpathy, BELIEVES, scaling_is_sufficient = true) against the same triple carrying falseThe cleanest signal in the set, and the highest precisionAlways. Cypher pattern matching, cheap enough to run on everything
Controlled vocabularySame subject, predicates drawn from a known antonym pair: SUPPORTS against OPPOSES, VALIDATES against FALSIFIES, RECOMMENDS against DISCOURAGESSlightly noisier than structural, still usefulContinuously, against a maintained antonym vocabulary
Embedding distanceSame subject, conclusions that embed far apart relative to the typical distance between claims about that subjectNoisy. Its output is a candidate for further classificationContinuously, feeding the classifier rather than the flag list
Model-assistedA narrow query on a claim pair, answered with a contradiction-confidence score, a classification and a short justificationThe most informative layer and the most expensiveReview queues in high-stakes domains, on a longer cadence

Classifying the type of conflict

Once a contradiction is detected, the next question is what kind of contradiction it is. Not all contradictions are the same kind of thing, and the engine that treats them uniformly will mis-route most of them. There are at least five distinct types, each requiring different resolution machinery.

The factual dispute. Two claims, both about the same world-state, one of which is simply wrong. The Transformer architecture was introduced in 2017 vs. The Transformer architecture was introduced in 2015. There is a fact of the matter. One source has it right, the other has it wrong. Resolution: evidence-counting, credibility-weighting, eventually a confidence assignment that strongly favors the correct claim and marks the incorrect one as historically asserted but currently low-confidence.

The definitional dispute. Two claims that look contradictory but are using the same word to mean different things. RNNs are obsolete for long sequences vs. RNNs are the right choice for long sequences. If you dig into it, the first speaker means "vanilla RNNs" and the second means "modern recurrent variants like Mamba." Same surface word, different referent. Resolution: ontology clarification, splitting the entity into more specific concepts, retroactively re-tagging the claims.

The contextual dispute. Two claims that are both right, but in different contexts. Use ReLU for hidden layers vs. Use GELU for hidden layers. Neither is wrong. They apply in different model families, different problem domains, different training regimes. Resolution: attach applies_when conditions to both claims, so the agent retrieves the appropriate one based on context.

The temporal dispute. Two claims that were both right at different times. The bi-temporal model handles most of these automatically: when a new claim arrives that supersedes an old one, the contradiction is resolved by closing the old validity window. Some temporal disputes are subtler, like a claim that was true in 2020, false in 2022, and arguably true again in 2025. Resolution: validity-window timelines, with multiple intervals.

The epistemic dispute. Two claims about the same subject, both made by credible sources, where the underlying disagreement is genuine. The experts have looked at the same evidence and reached different conclusions, and there is no obvious tiebreaker. The optimal learning rate question. The activation function question. Most of the interesting questions, in most of the interesting fields. Resolution: do not pretend to resolve. Create a Controversy node that holds both positions, link them to their sources, mark the dispute as ongoing, and surface the disagreement to agents querying the topic.

That last category is the one most knowledge systems handle worst. They try to resolve epistemic disputes algorithmically, picking the source with higher credibility, averaging the positions, using the more recent one, and the result is a system that confidently asserts contested conclusions. The right answer is to admit the dispute. To represent it structurally. To let downstream agents reason about it, hedge appropriately, and present calibrated uncertainty to their users. The engine's job is to make hard questions visible, leaving them as hard as they actually are.

Live Two credible sources, the same evidence, no tiebreaker in the world. Each of the three shortcuts produces one confident output, including the average, which is a position neither source holds. The node underneath holds both and stays queryable.

The Controversy node

The structural representation of an unresolved contradiction is a Controversy node. It is a first-class graph object with its own properties and edges, not a flag on an existing claim and not a metadata field. Every controversy node holds the set of competing positions, the sources backing each position, the type classification, the detection timestamp, and a computed resolution_status property that can be open, contextually_resolved, temporally_resolved, or epistemically_stable.

The "epistemically_stable" status is the interesting one. It marks controversies that the engine has decided are not going to resolve, disputes where the system has accumulated enough evidence on both sides that further evidence is unlikely to tip the balance, and the right response is to treat the dispute as a stable feature of the knowledge base rather than something to keep actively trying to settle. The optimal-learning-rate question, in most contexts, is epistemically stable. The system represents the controversy, presents it transparently, and stops trying to algorithmically pick a winner.

The four values of resolution_status, and what each one licenses
StatusWhat it recordsWhat the agent does with it
openDetected, with the engine yet to decide which kind of dispute it isSurface the disagreement and wait for the classification to settle
contextually_resolvedBoth positions hold, each under its own applies_when conditionsRetrieve the position matching the caller’s context
temporally_resolvedBoth were right at different times, and the validity windows carry the splitAnswer from the window the query’s time cursor falls inside
epistemically_stableEnough evidence has accumulated on both sides that further evidence is unlikely to tip itPresent the dispute as a standing feature of the field and stop looking for a winner

An agent that queries a topic with an active controversy receives the controversy structure as part of its result set. It does not get one of the positions presented as truth. It gets both, with their sources, with their credibility weights, with the controversy's status, and with whatever guidance the system has accumulated about which position applies in which context. The agent can then decide how to respond: present the disagreement transparently to the user, take a position with appropriate hedging based on the user's domain, or escalate to a human if the stakes are high. The decision is made with full information rather than being made for the agent by an upstream system that flattened the disagreement.

Running the engine continuously

The contradiction engine is not a one-shot job that runs at ingestion time and never again. It runs continuously, for two reasons. First, contradictions are often only detectable in the presence of later evidence. Claim A is ingested in January. Claim B is ingested in March. The contradiction between them is only detectable in March, when both are in the graph. The engine has to revisit historical claims as new evidence arrives, and re-evaluate whether the corpus now contains conflicts that were not visible before.

Live Claim A lands in January, its opposite in March. The upper lane checked each claim as it arrived and correctly saw nothing both times. The lower lane is still running in March, which is the first moment the conflict exists at all.

Second, the engine's own classifiers improve over time. A controversy that was marked as open in January, with the engine unable to decide between contextual and epistemic, may be classifiable in March when more sources have weighed in. The engine periodically reprocesses open controversies, asks whether new evidence has shifted the picture, and updates statuses accordingly. This is housekeeping work, but it is what keeps the graph accurate as the corpus grows.

Operationally, in production, this looks like a scheduled job that runs every few minutes, querying for newly-ingested claims, checking each against the existing graph for structural conflicts, classifying any candidates produced, and writing the resulting Controversy nodes or supersession edges. Heavier work, the LLM-assisted layer and the reprocessing of open controversies, runs on a longer cadence, hourly or daily. The work is async, idempotent, and gracefully handles partial failures. A single bad classification does not corrupt the graph because every classification writes to a structured node that can be audited, corrected, and rolled back.

Live Two clocks. The structural pass cycles constantly through query, check, classify and write. The expensive lane, the model-assisted layer and the reprocessing of open controversies, completes one pass in the time the cheap one completes six. Which lane the bill lives in is visible from the rates alone.

Confidence propagation through contradictions

The interesting question, once contradictions are represented structurally, is what happens to the confidence values of the claims involved. A claim that stands alone in the graph might have a confidence of 0.85, because the system is fairly sure based on its source and the corroborating evidence. The same claim, when a contradicting claim is detected and structurally linked, should have its confidence reduced. The amount of reduction depends on the credibility of the contradicting source, the strength of the contradiction (structural vs. semantic), and the type classification (factual disputes reduce more than epistemic disputes, where both sides are expected to coexist).

This propagation is the system's calibrated representation of how sure it is about claims, and it is what enables agents to hedge appropriately. It is load-bearing. An agent querying a claim with confidence 0.85 might present it as established fact. The same agent querying the same claim, after a credible contradiction arrives and the confidence drops to 0.62, will present it as "this is the dominant view, but the field is not unanimous." The downstream user experience changes, automatically, because the underlying epistemic state changed.

Live The contradiction links in and the number falls from 0.85 to 0.62. Read the sentence at the bottom across the drop. Nothing in the prompt changed, and the agent went from asserting to hedging because the substrate underneath it did.

The PLN-inspired truth-value system that essay five will cover in detail handles this propagation natively. Each claim has a (strength, confidence) pair. When a contradiction is detected, both claims' strengths shift toward the midpoint, weighted by source credibility. Both claims' confidences shift downward, weighted by the strength of the contradiction. The shifts are deterministic, auditable, and reversible if the contradiction itself is later resolved or invalidated.

The agent-facing API

Everything described so far is internal machinery. The thing that matters at the end is what the agent sees when it queries. The agent should not have to know about Controversy nodes by name. It should query the knowledge graph in its normal way and receive a response that contains, as part of the standard payload, any active controversies relevant to the query.

Concretely, a query like world_model.query("what is the consensus on contrastive learning for vision?") returns a payload that looks roughly like: a primary claim or set of claims with confidences, a list of zero or more controversies the query touches, each controversy containing its competing positions and statuses, the sources backing each position, and a recommended hedging strategy ("present-with-disagreement" vs. "take-position-with-context"). The agent's natural-language generator consumes this payload and produces a response that is automatically calibrated to the epistemic state: confident where the system is confident, hedged where the system is divided, transparent about the divisions.

Live The query says nothing about controversies. The payload carries the claims and their confidences, every controversy the query touched with its positions and status, the sources behind each side, and the hedging strategy the generator should take. Calibration arrives as data.

This is the deepest payoff of the contradiction engine. Contested topics are handled gracefully, and beyond that, the agent's output, with no additional prompting, is automatically more calibrated because the substrate it queries is more calibrated. Hedging is not added by clever prompt engineering. It emerges from the data structure.

What this costs

The contradiction engine is a real engineering project, and it is worth being precise about where the work concentrates. The detection pipeline carries the four layers, each with its own precision-recall tuning. The classification logic sits on top of it, routing each candidate to one of the five dispute types. The heaviest part is the continuous-running infrastructure: idempotent retries, partial-failure handling, and an audit trail for every classification the engine writes. The maintenance load grows with the corpus, because new contradiction patterns surface as the graph fills, and the antonym vocabulary and the classifier thresholds get tuned against what the corpus actually contains. The cost is real and it is front-loaded into the substrate, where it belongs.

Live Same corpus, same conflicts, two graphs. On the left every conflict was caught at the moment it became detectable and carries a status. On the right the same conflicts sat unflagged while claims were derived on top of them, so what arrives for the engine to fix is a tangle rather than a pair.

You will be tempted, especially early, to skip the engine and rely on ingestion-time detection only. Don't. The continuous-running structure is what catches contradictions that only become visible after later evidence arrives, and that category is the majority of interesting contradictions in any growing corpus. Ship the engine, even in its simplest form, on day one. Improve it over time. The graph that grew up with the engine is qualitatively different from the graph that had the engine bolted on after six months of corruption.

The next essay turns to the metagraph patterns themselves, the specific Neo4j shapes that implement edges-of-edges, facts-about-facts, and rules-about-rules in a production-grade property graph database. Less philosophy. More Cypher. The blueprint for the substrate.


END OF ESSAY 03 · CONTINUE TO ESSAY 04 →