Verify multi-hop relational claims before an agent asserts them β zero tokens, with proof paths.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
TL;DR. LLMs hallucinate on multi-hop relational reasoning. This is a relation-algebra verifier an agent calls to check a claim before asserting it: zero model tokens, precision-guaranteed (accepts a claim iff a grounded proof path exists), language-agnostic, and provider-agnostic. Plugs in as a library, a function-calling tool, or an MCP server. Validated on real LLMs (DeepSeek et al.) and the public CLUTRR benchmark. See docs/integration.md.
π Full paper: PAPER.md Β· Integration guide: docs/integration.md Β· Try it in 30 seconds: quickstart notebook
Δα»c bαΊ±ng tiαΊΏng Viα»t: README.vi.md
LLMs are solid on one-hop facts but collapse on composition β chaining several correct facts into a multi-step conclusion. On CLUTRR (kinship reasoning), DeepSeek's accuracy falls off with depth, while a grounded operator-composition solver holds ~100% flat β at zero tokens:
(CLUTRR/v1 gen_train234_test2to10, clean-chain, n=12/hop; full test set n=635: solver
covers 99.5%, accuracy 99.2%. grounded_reasoning/experiments/clutrr_eval.py.)
Is: a guaranteed reasoning-verification layer built on relation operator algebra.
calibrate_transitivity doesn't care
whether facts came from an external KB or the model's own assertions, so it
already calibrates SGDC's real output precision with zero new code (see
self_grounded_calibration_eval.py,
PAPER.md Β§6's remark).Is not: an "unprecedented breakthrough." The Katz index, the Neumann series, graph reachability, and neuro-symbolic grounding are all classical math and technique. The contribution here is unification, a measured guarantee, and benchmark numbers β not a new primitive. The guard needs a relation graph (supplied, or extracted from LLM facts); flexibility is bounded (see PAPER Β§5).
Raised in review, reproduced, and fixed with an opt-in guard each β not swept under the rug:
"Bob" vs "bob"), the graph
treats them as two nodes and a real path silently breaks β the guard then
(correctly, per its own contract) rejects a claim that is actually true.
Fix (binary): GroundedReasoner(normalize=lambda s: s.strip().casefold())
folds surface-form variants together before they become graph keys; proofs
still display each entity's original first-seen spelling. Theorem N
characterizes exactly when this is safe: precision stays exactly 1.0 as
long as normalize never merges two genuinely distinct entities β that's
the only way it can go wrong, so it's exactly what
gr.calibrate_normalization(labeled_pairs) measures from held-out evidence,
reusing the same Clopper-Pearson machinery as Theorem M.via is transitive in reality. It guarantees
"a path exists under the closure of via," not "via actually composes in
the world." Compose a relation that's only partially/conditionally
transitive ("trusts": A trusts B, B trusts C, does not imply A trusts C)
and you get a confident, mathematically correct grounded=True that answers
a different question than the one you meant to ask. Fix (binary):
GroundedReasoner(transitive_relations={"parent", "is_a", ...}) makes the
guard raise ValueError for any undeclared relation, turning a silent
modeling assumption into an explicit, checked one. Fix (measured β Theorem
M): gr.calibrate_transitivity(rel, labeled_pairs) replaces the binary
declare-or-reject with an actual number β a Clopper-Pearson lower confidence
bound on "a graph-grounded claim for rel is really true," computed from
held-out labeled pairs. Where the binary guard can only guess or block
outright, the calibrated bound tells you how much to trust it.Both opt-in guards are off by default (identical behavior to previous
releases). Reproductions: tests/test_agent.py::TestEntityNormalization,
::TestTransitiveRelationsGuard, ::TestTransitivityCalibration,
::TestNormalizationCalibration; the A/B comparisons:
transitivity_calibration_eval.py,
normalization_calibration_eval.py.
Heterogeneous relation chains. verify(via=rel) composes ONE relation with
itself; gr.verify_path(subject, obj, via=["parent","employer"]) composes an
exact sequence of different relations (e.g. a derived "financially dependent
on" claim) β not new math (OperatorRelationAlgebra.follow already composes
mixed-relation chains exactly per Theorem G, this just exposes it at the
facade with proof-path reconstruction) β and gr.calibrate_path(via, labeled_pairs) calibrates that fixed pattern with the same Clopper-Pearson
engine as calibrate_transitivity (see PAPER.md Β§5.3.4). Checked against
independent ground-truth BFS across 8,000 triples with zero mismatches:
tests/test_agent.py::TestHeterogeneousPathVerification,
heterogeneous_path_calibration_eval.py.
| Approach | Extra tokens | Guarantee | Needs an external KB |
|---|---|---|---|
| LLM self-verification (2nd call) | +110% | none (measured 34% precision) | no |
| Self-consistency / majority vote | multiplies with sample count | none, statistical only | no |
| RAG / external KG grounding | varies | only as good as retrieval | yes |
| This guard | +0 | precision = 1.0 (Theorem G) | no |
| This guard, self-grounded (SGDC) | +0 | precision = 1.0 given sound atomic facts (Theorem I) | no |
| This guard, conformal | +0 | coverage β₯ 1βΞ±, distribution-free (Theorem K) | no |
The reasoning core rests on a single unification (numerically verified, zero error):
| View | Theorem | Content |
|---|---|---|
| Fuzzy diffusion inference | F | conf(aβb) = Ξ£ Ξ±α΅(Pα΅)[a,b], calibrated + grounded |
| Relation operator algebra | G | composition = operator product, transitive closure = Ξ£ powers |
| Spectral analysis (Katz) | H | engine.infer = resolvent (IβΞ±P)β»ΒΉβI (matches 0.0 error) |
βΉ fuzzy inference is spectral analysis of the relation operator. grounded_reasoning/reasoning/.
Six further theorems extend this core: I (two-sided precision/recall guarantee for a self-grounded, no-external-KB variant), J (closure-learning completeness, validated on CLUTRR), K (conformal reasoning β distribution-free coverage under a noisy relation graph, including one extracted by an LLM from raw text), L (Horn forward-chaining, generalizing transitive closure to conjunctive rules), M (empirical transitivity calibration β a Clopper-Pearson confidence bound replacing a blind transitivity assumption with a measured one), and N (normalization precision isolation β precision=1.0 breaks only via an over-merge, and only that is what needs calibrating). All nine are stated, proved, and numerically verified in PAPER.md.
| Experiment | Result |
|---|---|
| Hallucination guard (kinship) | precision 33% β 100%, catches 92/92 (two seeds), 0 false rejects |
| Hallucination guard, harder stress test (48-person tree, sibling/spouse distractor facts, shuffled prose, T=0.7, guaranteed-empty trap questions) | raw DeepSeek precision 4.6% (2124 fabricated names, 86/90 trap questions answered with a fabrication); guarded precision 100%, 0 leaked, 0 correct answers dropped β guard_llm_stress_eval.py |
| Guard token cost | +0 tokens (vs. LLM self-verify: +110% tokens, 34% precision) |
| SGDC (self-grounded, no external KB) | precision 78% β 100% from internal consistency alone |
| Dense, anti-commonsense ontology | precision 31% β 100%, catches 106/106, 0 false rejects β nl_ontology_eval.run_dense |
| CLUTRR (public benchmark) | solver ~100% at every hop vs. DeepSeek 83%β8% |
| Hard passage (9-step chain, 8 questions) | DeepSeek fabricates 1/8 (wrong direction); grounded system 8/8, with proofs β examples/hallucination_demo.py |
The guard/solver needs a clean graph. But if you let an LLM extract relations from natural-language text, the graph is noisy (missing/spurious edges). Conformal Reasoning (Theorem K) fixes exactly that: use operator confidence as a score, calibrate a threshold βΉ distribution-free coverage β₯ 1βΞ±, even on a noisy graph.
End-to-end demo: DeepSeek extracts an "is a" graph from text β conformal runs on that extracted graph (ground truth is used only for scoring):
| Text | LLM extraction (P / R) | Coverage (target β₯90%) | Efficiency (FPR) |
|---|---|---|---|
| Easy | 100% / 99.7% | 91.3% | 0.0 |
| Hard (nested clauses + near-miss distractors) | 99.5% / 68.5% | 93.0% | 0.77 |
The LLM's extraction drops 31% of the edges (a genuinely noisy graph) β the coverage guarantee still holds (93% β₯ 90%), only efficiency degrades. Validity always holds; efficiency scales with graph quality.
βΉ A path to guaranteed reasoning over natural-language relations β where the hard
guard can't reach. grounded_reasoning/experiments/conformal_llm_eval.py.
Efficiency can be pushed further under dropout-dominant noise, at no cost to
validity. ConformalReasoner.calibrate(..., group_fn=...) calibrates a
separate threshold per group instead of one global one (Mondrian conformal β
classical, not new); redundancy_group groups a pair by whether it has more
than one walk in the extracted graph, computable with no ground truth. A
different grouping tried first (hop-distance) was numerically falsified
before shipping β it made efficiency worse, not better, and was discarded.
Redundancy grouping cuts FPR from 98.7% β 80.8% when dropped edges
dominate the noise (matching this system's real LLM-extraction noise mode)
while coverage still holds β₯90% β and honestly gives ~no benefit when
spurious added edges dominate instead.
redundancy_conformal_eval.py,
PAPER.md Β§7.1's remark.
A different, orthogonal weakness β the noise level DRIFTING over time, not
being heterogeneous β needs a different classical tool. Split-conformal
(and its Mondrian extension above) assumes calibration and test data share a
distribution; that breaks if extraction quality changes between document
batches. AdaptiveConformalReasoner (Adaptive Conformal Inference β Gibbs &
Candès, 2021, classical, not new) updates its threshold from a stream of
confirmed-true examples instead of freezing it after one calibration. When
noise shifts partway through a stream (p_drop 0.05 β 0.45), a frozen
threshold's coverage collapses from 88.6% to 47.6% β well below the 90%
target, silently β while ACI recovers to 89.6%, in 15/15 trials tested.
drift_conformal_eval.py,
PAPER.md Β§7.1's remark.
Strongest efficiency result: removing the specific bad edges beats calibrating
around them. identify_suspect_edges removes any edge that appears on a
held-out FALSE-labeled claim's proof path and NO true-labeled claim's β a
simple decision rule, not a statistical guarantee. Verified across 5 noise
regimes (60 seeds each): FPR drops substantially and consistently
everywhere, e.g. 77.0% β 49.2% (dropout-dominant) and 58.7% β 15.7%
(spurious-dominant, where redundancy_group gives almost nothing) β
coverage on the remaining graph essentially unaffected. Unlike every
calibration method above, this one carries no false-discovery-rate
guarantee β a real, measured tradeoff: at the default configuration
(identify_frac=0.5, min_evidence=1), the pooled wrongly-removed rate
ranges 13.2%β32.2% across regimes. At the recommended configuration
(identify_frac=0.85, min_evidence=2, found by a Pareto sweep β the
default of identify_and_prune_edges, which applies it automatically so
it's the path of least resistance), it drops to 1.5%β3.1% (95% upper
confidence bound 2.6%β6.6%), at the cost of cleaned FPR rising somewhat
(e.g. ~49% β ~59% in the dropout-dominant regime, still far below the 77%
raw baseline) and a smaller reserved evaluation set. Checked against a real
LLM (DeepSeek), not just simulated noise, on data where each candidate
edge is backed by exactly one labeled encounter (no query repeated β the
realistic case for a deployment that verifies each claim once): the
count-based rules above (min_evidenceβ₯2, and its hub-aware
use_propagation=True variant) never fire at all on this regime, since
they require an edge to independently clear the evidence bar twice, which
never happens with single-encounter evidence. Lowering to
min_evidence=1 does block real hallucinated edges, but on its own makes
downstream FPR worse than doing nothing (63.0% β 70.7%, beats raw in
only 4/15 splits) β traced to the diffusion engine's row-normalization
concentrating transition probability onto a source's surviving edges once
its OTHER edges are pruned. Pairing that same blocking decision with
masked_infer (normalizes by each source's pre-prune degree, so removal
only ever removes confidence mass, never redistributes it) recovers a
real improvement: 63.0% β 54.0%, beats raw in 12/15 splits, with no
regression on the synthetic benchmark. A learned (logistic regression)
alternative was also tried and rejected: it failed to generalize from
synthetic training data to the real data at all. Pruning also costs real
recall for any true claim that depended solely on a removed edge, and it
edits the graph in place (a one-way change, unlike calibration which only
adjusts a threshold).
edge_pruning_eval.py,
edge_pruning_llm_eval.py,
PAPER.md Β§7.1's remark.
The guard above needs some relation graph handed to it. Self-Grounded Deductive Consistency (Theorem I) removes even that: it exploits the fact that LLMs are reliably accurate on atomic (1-hop) facts but hallucinate on composition. Take the model's own confident 1-hop facts, build the operator closure from those, then reject any of the model's own multi-hop conclusions that fall outside its own closure β self-contradiction is the hallucination signal, not disagreement with an external source.
| precision | recall | |
|---|---|---|
| Raw multi-hop (LLM) | 78% | 87% |
| SGDC (self-grounded, zero external knowledge) | 100% | 72% |
| Ceiling: filtering with an external graph | 100% | 87% |
The honest cost is recall (72% vs. 87%): self-closure is conservative. And Theorem I's precision=1.0 is conditional β it holds if the model's own atomic facts are sound; in a counter-prior domain (e.g. "a whale is a fish"), atomic precision itself can drop, and recall suffers with it (PAPER.md Β§6 records this honestly rather than hiding it).
That assumption can be measured too, with zero new code.
gr.calibrate_transitivity(rel, labeled_pairs) (Theorem M) doesn't care
whether gr's facts came from an external KB or the model's own atomic
self-assertions β so calling it on a reasoner built purely from an LLM's own
facts calibrates SGDC's actual output precision directly, from held-out
evidence, instead of assuming atomic soundness. In a synthetic domain with
15% of the atomic facts deliberately wrong, SGDC's real precision fell to
~74% (not the naively-expected ~85% β a single wrong atomic edge
composes into several downstream claims, amplifying its damage), and the
calibrated bound correctly stayed below that in 98.3% of trials β
self_grounded_calibration_eval.py,
PAPER.md Β§6's remark.
Runnable: examples/self_grounded_demo.py
(offline) Β· live on DeepSeek:
grounded_reasoning/experiments/self_grounded_eval.py.
grounded_reasoning/agent/)A relation-reasoning verifier for agents: check a multi-hop claim before asserting it β zero model tokens, precision guaranteed (accepts iff a grounded proof path exists).
Three integration paths (details: docs/integration.md):
GroundedReasoner.verify / filter_claims / contradictions.TOOL_SPEC (Anthropic) / openai_tool_spec() (OpenAI) + run_tool β a stateless verify_relation tool.python -m grounded_reasoning.agent.mcp_server β plugs into Claude or any MCP-compatible agent.Multi-provider (not just DeepSeek): LLMClient(provider=...) for DeepSeek / OpenAI /
Groq / OpenRouter / Together / Mistral / Ollama (local) β all OpenAI-compatible, switch
providers without changing code. Multilingual: entities/relations are opaque
Unicode strings βΉ works with any language (cha, ηΆ, ΩΨ§ΩΨ―β¦) with zero configuration.
A real function-calling demo (agent verifies itself, blocks hallucination):
python -m grounded_reasoning.experiments.agent_demo. When the graph is noisy (relations
extracted by an LLM from text), use ConformalReasoner for a coverage β₯1βΞ±
guarantee instead of hard precision.
| Path | Content |
|---|---|
grounded_reasoning/ | Public package β GroundedReasoner, verify_relation, TOOL_SPEC, ConformalReasoner, AdaptiveConformalReasoner, LLMClient |
grounded_reasoning/agent/{verifier,tool,mcp_server}.py | Public API implementation β HallucinationGuard, function-calling tool, MCP server |
grounded_reasoning/reasoning/abstract_inference.py | FuzzyInferenceEngine, TypedInferenceEngine, HallucinationGuard (Theorem F) |
grounded_reasoning/reasoning/operator_algebra.py | Relation operator algebra (Theorem G) |
grounded_reasoning/reasoning/relation_spectrum.py | Spectrum, nilpotency, Katz resolvent (Theorem H) |
grounded_reasoning/reasoning/conformal_reasoning.py | Conformal β coverage guarantee under noise (Theorem K) |
grounded_reasoning/reasoning/composition_algebra.py | Composition-table learning, validated on CLUTRR (Theorem J) |
grounded_reasoning/reasoning/horn.py | Horn forward-chaining, least-model semantics (Theorem L) |
grounded_reasoning/reasoning/transitivity_calibration.py | Clopper-Pearson calibration β reused for both the transitivity assumption (Theorem M) and the normalization over-merge risk (Theorem N) |
grounded_reasoning/reasoning/edge_pruning.py | Held-out-evidence edge pruning β a heuristic decision rule, not a Theorem, with its own measured tradeoffs |
grounded_reasoning/reasoning/llm_client.py | Provider-agnostic LLM client (key read from an env var) |
grounded_reasoning/theory/theorems.py | Nine theorems (FβN) with numerical verification |
grounded_reasoning/experiments/{guard_llm,guard_llm_stress,self_grounded,self_grounded_calibration,nl_ontology,guard_cost,clutrr,conformal_llm,redundancy_conformal,drift_conformal,inference,transitivity_calibration,normalization_calibration,heterogeneous_path_calibration,edge_pruning,edge_pruning_llm}_eval.py | Real-LLM and benchmark experiments backing every claim above |
examples/hallucination_demo.py | End-to-end function-calling demo (real LLM, needs a key) |
examples/self_grounded_demo.py | SGDC (Theorem I): self-verify a model's own multi-hop claim with NO external KB (offline) |
examples/rag_pipeline_demo.py | filter_claims as a RAG/agent post-processing guard, heterogeneous claims (offline) |
examples/calibration_demo.py | Theorem M + N side by side: measuring transitivity and normalization trust instead of assuming it (offline) |
examples/conformal_demo.py | Coverage guarantee vs. noise tradeoff, clean vs. noisy graph side by side (offline) |
examples/quickstart.ipynb | Runnable tour of the library (offline, Colab-ready) |
This project began as an attempt to invent an embedding-free retrieval algorithm that could compete with dense/RAG retrieval. That research question reached a rigorous, fully honest negative conclusion (ties BM25, loses significantly to dense embeddings β with a proof of why). The same mathematical toolkit β operator algebra, spectral analysis β turned out to have real, measurable value on a different problem: guaranteeing multi-hop relational reasoning. This repository ships only that validated, tested reasoning system; the full retrieval research trail (including every failed attempt, honestly recorded) lives in a separate research repository and is not part of this package. See PAPER.md Β§1 for the full framing.
Principle: proof before code, formal definitions, falsifiability, and honest reporting of negative results β see CONTRIBUTING.md.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/grounded-reasoning)<a href="https://allmcps.com/mcp/grounded-reasoning"><img src="https://allmcps.com/api/badge/grounded-reasoning?style=directory" alt="Grounded Reasoning on AllMCPs" /></a>