grounded-reasoning 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ """
2
+ grounded-reasoning — a relation-reasoning verifier for LLMs and agents.
3
+
4
+ Public API (clean import surface):
5
+
6
+ from grounded_reasoning import GroundedReasoner, verify_relation, TOOL_SPEC
7
+
8
+ - GroundedReasoner : load facts, then verify / filter_claims / contradictions.
9
+ - verify_relation : a stateless function-calling tool (0 tokens, returns a proof).
10
+ - TOOL_SPEC / openai_tool_spec : tool schemas (Anthropic / OpenAI).
11
+ - ConformalReasoner : distribution-free coverage guarantee >=1-alpha under a noisy graph.
12
+ - LLMClient : a multi-provider (OpenAI-compatible) client for demos/experiments.
13
+ """
14
+ from src.agent import (
15
+ TOOL_SPEC,
16
+ GroundedReasoner,
17
+ Verdict,
18
+ openai_tool_spec,
19
+ run_tool,
20
+ verify_relation,
21
+ )
22
+ from src.reasoning.conformal_reasoning import ConformalReasoner, conformal_threshold
23
+ from src.reasoning.llm_client import LLMClient
24
+
25
+ __version__ = "0.1.0"
26
+
27
+ __all__ = [
28
+ "GroundedReasoner",
29
+ "Verdict",
30
+ "verify_relation",
31
+ "run_tool",
32
+ "TOOL_SPEC",
33
+ "openai_tool_spec",
34
+ "ConformalReasoner",
35
+ "conformal_threshold",
36
+ "LLMClient",
37
+ "__version__",
38
+ ]
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: grounded-reasoning
3
+ Version: 0.1.0
4
+ Summary: Zero-token, precision-guaranteed verifier for LLM/agent multi-hop relational reasoning
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/ALEXaquarius/grounded-reasoning
7
+ Project-URL: Repository, https://github.com/ALEXaquarius/grounded-reasoning
8
+ Project-URL: Paper, https://github.com/ALEXaquarius/grounded-reasoning/blob/main/PAPER.md
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: numpy>=1.24
13
+ Provides-Extra: mcp
14
+ Requires-Dist: mcp>=1.0; extra == "mcp"
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest>=8; extra == "dev"
17
+ Requires-Dist: pytest-cov>=6; extra == "dev"
18
+ Requires-Dist: ruff>=0.6; extra == "dev"
19
+ Dynamic: license-file
20
+
21
+ # grounded-reasoning — Grounded, Guaranteed Reasoning for LLMs & Agents
22
+
23
+ [![CI](https://github.com/ALEXaquarius/grounded-reasoning/actions/workflows/ci.yml/badge.svg)](https://github.com/ALEXaquarius/grounded-reasoning/actions/workflows/ci.yml)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
25
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
26
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ALEXaquarius/grounded-reasoning/blob/main/examples/quickstart.ipynb)
27
+
28
+ > **TL;DR.** LLMs hallucinate on multi-hop relational reasoning. This is a
29
+ > **relation-algebra verifier** an agent calls to check a claim *before* asserting it:
30
+ > **zero model tokens**, **precision-guaranteed** (accepts a claim iff a grounded proof
31
+ > path exists), language-agnostic, and provider-agnostic. Plugs in as a **library**, a
32
+ > **function-calling tool**, or an **MCP server**. Validated on **real LLMs** (DeepSeek
33
+ > et al.) and the public **CLUTRR** benchmark. See [docs/integration.md](docs/integration.md).
34
+
35
+ 📄 Full paper: **[PAPER.md](PAPER.md)** · Integration guide: **[docs/integration.md](docs/integration.md)** · Try it in 30 seconds: **[quickstart notebook](https://colab.research.google.com/github/ALEXaquarius/grounded-reasoning/blob/main/examples/quickstart.ipynb)**
36
+
37
+ Đọc bằng tiếng Việt: **[README.vi.md](README.vi.md)**
38
+
39
+ ---
40
+
41
+ ## Why this exists
42
+
43
+ LLMs are solid on one-hop facts but **collapse on composition** — chaining several
44
+ correct facts into a multi-step conclusion. On CLUTRR (kinship reasoning), DeepSeek's
45
+ accuracy **falls off with depth**, while a grounded operator-composition solver holds
46
+ **~100% flat — at zero tokens**:
47
+
48
+ ```
49
+ acc
50
+ 100% ●─────●─────●─────●─────●─────●─────● ● Grounded solver (algebra, 0 tokens)
51
+ 90% |
52
+ 80% ○
53
+ 70% | ╲
54
+ 60% | ╲
55
+ 50% | ╲
56
+ 40% | ○ ○ ○ DeepSeek (LLM)
57
+ 30% | ╲ ╱ ╲
58
+ 20% | ○─────○ ╲
59
+ 10% | ○─────○
60
+ 0% +──┴─────┴─────┴─────┴─────┴─────┴─────┴─
61
+ hop 2 3 4 5 6 7 8 (composition steps)
62
+
63
+ hop: 2 3 4 5 6 7 8
64
+ DeepSeek: 83% 42% 25% 25% 42% 17% 8%
65
+ Solver: 100% 100% 100% 100% 100% 100% 100%
66
+ ```
67
+
68
+ *(CLUTRR/v1 gen_train234_test2to10, clean-chain, n=12/hop; full test set n=635: solver
69
+ covers 99.5%, accuracy 99.2%. `src/experiments/clutrr_eval.py`.)*
70
+
71
+ ---
72
+
73
+ ## What it is / is NOT (honestly)
74
+
75
+ **Is:** a guaranteed reasoning-verification layer built on relation operator algebra.
76
+ - **Precision = 1.0, guaranteed** (Theorem G) — accepts a claim only if a grounded proof path exists.
77
+ - **Zero extra tokens** — local matrix multiplication, no LLM call. Compare to
78
+ "have the LLM self-verify," which costs +110% tokens for 34% precision.
79
+ - **Two-sided guarantee** (Theorem I) — precision *and* recall both have tight bounds.
80
+ - **No external KB required** (SGDC) — uses the LLM's own internal consistency.
81
+
82
+ **Is not:** an "unprecedented breakthrough." The Katz index, the Neumann series,
83
+ graph reachability, and neuro-symbolic grounding are all classical math and
84
+ technique. The contribution here is unification, a measured guarantee, and
85
+ benchmark numbers — not a new primitive. The guard needs a relation graph
86
+ (supplied, or extracted from LLM facts); flexibility is bounded (see
87
+ [PAPER §5](PAPER.md)).
88
+
89
+ ### How this differs from the usual fixes
90
+
91
+ | Approach | Extra tokens | Guarantee | Needs an external KB |
92
+ |---|---|---|---|
93
+ | LLM self-verification (2nd call) | +110% | none (measured 34% precision) | no |
94
+ | Self-consistency / majority vote | multiplies with sample count | none, statistical only | no |
95
+ | RAG / external KG grounding | varies | only as good as retrieval | yes |
96
+ | **This guard** | **+0** | **precision = 1.0** (Theorem G) | no |
97
+ | **This guard, self-grounded (SGDC)** | **+0** | precision = 1.0 given sound atomic facts (Theorem I) | no |
98
+ | **This guard, conformal** | **+0** | coverage ≥ 1−α, distribution-free (Theorem K) | no |
99
+
100
+ ---
101
+
102
+ ## Three theorems, one operator (F = G = H)
103
+
104
+ The reasoning core rests on a single unification (numerically verified, zero error):
105
+
106
+ | View | Theorem | Content |
107
+ |------|---------|---------|
108
+ | Fuzzy diffusion inference | **F** | conf(a→b) = Σ αᵏ(Pᵏ)[a,b], calibrated + grounded |
109
+ | Relation operator algebra | **G** | composition = operator product, transitive closure = Σ powers |
110
+ | Spectral analysis (Katz) | **H** | `engine.infer` = resolvent (I−αP)⁻¹−I (matches **0.0** error) |
111
+
112
+ ⟹ fuzzy inference **is** spectral analysis of the relation operator. `src/reasoning/`.
113
+
114
+ Four further theorems extend this core: **I** (two-sided precision/recall guarantee
115
+ for a self-grounded, no-external-KB variant), **J** (closure-learning completeness,
116
+ validated on CLUTRR), **K** (conformal reasoning — distribution-free coverage under a
117
+ *noisy* relation graph, including one extracted by an LLM from raw text), and **L**
118
+ (Horn forward-chaining, generalizing transitive closure to conjunctive rules). All
119
+ seven are stated, proved, and numerically verified in [PAPER.md](PAPER.md).
120
+
121
+ ---
122
+
123
+ ## Evidence on real LLMs (DeepSeek)
124
+
125
+ | Experiment | Result |
126
+ |------------|--------|
127
+ | Hallucination guard (kinship) | precision **33% → 100%**, catches 94/94, 0 false rejects |
128
+ | Guard token cost | **+0 tokens** (vs. LLM self-verify: +110% tokens, 34% precision) |
129
+ | SGDC (self-grounded, no external KB) | precision **78% → 100%** from internal consistency alone |
130
+ | CLUTRR (public benchmark) | solver **~100% at every hop** vs. DeepSeek 83%→8% |
131
+ | Hard passage (9-step chain) | DeepSeek **fabricates 2/10** (wrong direction); grounded system **10/10**, with proofs — [`examples/hallucination_demo.py`](examples/hallucination_demo.py) |
132
+
133
+ ---
134
+
135
+ ## Guaranteed reasoning over a graph an LLM extracted from raw text
136
+
137
+ The guard/solver needs a **clean** graph. But if you let an **LLM extract** relations
138
+ from natural-language text, the graph is **noisy** (missing/spurious edges).
139
+ **Conformal Reasoning** (Theorem K) fixes exactly that: use operator confidence as a
140
+ score, calibrate a threshold ⟹ **distribution-free coverage ≥ 1−α**, even on a noisy
141
+ graph.
142
+
143
+ End-to-end demo: **DeepSeek extracts an "is a" graph from text** → conformal runs on
144
+ that extracted graph (ground truth is used only for scoring):
145
+
146
+ | Text | LLM extraction (P / R) | Coverage (target ≥90%) | Efficiency (FPR) |
147
+ |------|------------------------:|----------------------------:|------------------:|
148
+ | Easy | 100% / 99.7% | **91.3%** | 0.0 |
149
+ | Hard (nested clauses + near-miss distractors) | 99.5% / **68.5%** | **93.0%** | 0.77 |
150
+
151
+ > The LLM's extraction **drops 31% of the edges** (a genuinely noisy graph) →
152
+ > **the coverage guarantee still holds** (93% ≥ 90%), only efficiency degrades.
153
+ > *Validity always holds; efficiency scales with graph quality.*
154
+
155
+ ⟹ A path to guaranteed reasoning over **natural-language relations** — where the hard
156
+ guard can't reach. `src/experiments/conformal_llm_eval.py`.
157
+
158
+ ---
159
+
160
+ ## Quickstart
161
+
162
+ ```bash
163
+ git clone https://github.com/ALEXaquarius/grounded-reasoning
164
+ cd grounded-reasoning && pip install -e ".[dev]" # not yet on PyPI — install from source
165
+ pytest tests/ # every theorem + offline-locked logic, no network needed
166
+
167
+ # Use it right now (no LLM/network needed):
168
+ python -c "from grounded_reasoning import GroundedReasoner as G; r=G(); r.add_facts([('a','p','b'),('b','p','c')]); print(r.verify('a','c',via='p'))"
169
+
170
+ # Real-LLM experiments (need a key — read from an env var, NEVER hardcoded):
171
+ export DEEPSEEK_API_KEY=sk-... # bring your own; .env is gitignored
172
+ python -m src.experiments.guard_llm_eval # hallucination guard
173
+ python -m src.experiments.self_grounded_eval # SGDC
174
+ python -m src.experiments.clutrr_eval # public CLUTRR benchmark
175
+ python -m src.experiments.conformal_llm_eval # end-to-end conformal (LLM-extracted graph)
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Integrating with an Agent / LLM (`src/agent/`)
181
+
182
+ A **relation-reasoning verifier** for agents: check a multi-hop claim **before
183
+ asserting it** — zero model tokens, precision guaranteed (accepts iff a grounded proof
184
+ path exists).
185
+
186
+ ```python
187
+ from grounded_reasoning import GroundedReasoner
188
+ gr = GroundedReasoner()
189
+ gr.add_facts([("alice","parent","bob"),("bob","parent","carol")])
190
+ gr.verify("alice","carol", via="parent") # Verdict(grounded=True, proof=['alice','bob','carol'])
191
+ gr.verify("alice","zed", via="parent") # Verdict(grounded=False, proof=None) ← hallucination blocked
192
+ ```
193
+
194
+ Three integration paths (details: [docs/integration.md](docs/integration.md)):
195
+ - **Library**: `GroundedReasoner.verify / filter_claims / contradictions`.
196
+ - **Function-calling**: `TOOL_SPEC` (Anthropic) / `openai_tool_spec()` (OpenAI) + `run_tool` — a stateless `verify_relation` tool.
197
+ - **MCP server**: `python -m src.agent.mcp_server` — plugs into Claude or any MCP-compatible agent.
198
+
199
+ **Multi-provider** (not just DeepSeek): `LLMClient(provider=...)` for DeepSeek / OpenAI /
200
+ Groq / OpenRouter / Together / Mistral / Ollama (local) — all OpenAI-compatible, switch
201
+ providers without changing code. **Multilingual**: entities/relations are opaque
202
+ Unicode strings ⟹ works with any language (`cha`, `父`, `والد`…) with zero configuration.
203
+
204
+ A real function-calling demo (agent verifies itself, blocks hallucination):
205
+ `python -m src.experiments.agent_demo`. When the graph is **noisy** (relations
206
+ extracted by an LLM from text), use `ConformalReasoner` for a **coverage ≥1−α**
207
+ guarantee instead of hard precision.
208
+
209
+ ---
210
+
211
+ ## Source map
212
+
213
+ | Path | Content |
214
+ |------|---------|
215
+ | `grounded_reasoning/` | Public package — `GroundedReasoner`, `verify_relation`, `TOOL_SPEC`, `ConformalReasoner`, `LLMClient` |
216
+ | `src/agent/{verifier,tool,mcp_server}.py` | Public API implementation — HallucinationGuard, function-calling tool, MCP server |
217
+ | `src/reasoning/abstract_inference.py` | FuzzyInferenceEngine, TypedInferenceEngine, HallucinationGuard (Theorem F) |
218
+ | `src/reasoning/operator_algebra.py` | Relation operator algebra (Theorem G) |
219
+ | `src/reasoning/relation_spectrum.py` | Spectrum, nilpotency, Katz resolvent (Theorem H) |
220
+ | `src/reasoning/conformal_reasoning.py` | Conformal — coverage guarantee under noise (Theorem K) |
221
+ | `src/reasoning/composition_algebra.py` | Composition-table learning, validated on CLUTRR (Theorem J) |
222
+ | `src/reasoning/horn.py` | Horn forward-chaining, least-model semantics (Theorem L) |
223
+ | `src/reasoning/llm_client.py` | Provider-agnostic LLM client (key read from an env var) |
224
+ | `src/theory/theorems.py` | **Seven theorems (F–L)** with numerical verification |
225
+ | `src/experiments/{guard_llm,self_grounded,nl_ontology,guard_cost,clutrr,conformal_llm,inference}_eval.py` | Real-LLM and benchmark experiments backing every claim above |
226
+ | `examples/hallucination_demo.py` | End-to-end function-calling demo |
227
+ | `examples/quickstart.ipynb` | Runnable tour of the library (offline, Colab-ready) |
228
+
229
+ ---
230
+
231
+ ## Origin story
232
+
233
+ This project began as an attempt to invent an embedding-free retrieval algorithm that
234
+ could compete with dense/RAG retrieval. That research question reached a rigorous,
235
+ fully honest **negative** conclusion (ties BM25, loses significantly to dense
236
+ embeddings — with a proof of why). The same mathematical toolkit — operator algebra,
237
+ spectral analysis — turned out to have real, measurable value on a different problem:
238
+ **guaranteeing** multi-hop relational reasoning. This repository ships only that
239
+ validated, tested reasoning system; the full retrieval research trail (including every
240
+ failed attempt, honestly recorded) lives in a separate research repository and is not
241
+ part of this package. See [PAPER.md §1](PAPER.md) for the full framing.
242
+
243
+ ---
244
+
245
+ ## Contributing & Community
246
+
247
+ - How to contribute + research principles: [CONTRIBUTING.md](CONTRIBUTING.md)
248
+ - Code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) · Security: [SECURITY.md](SECURITY.md)
249
+ - Version history: [CHANGELOG.md](CHANGELOG.md) · Citation: [CITATION.cff](CITATION.cff)
250
+ - License: **MIT** ([LICENSE](LICENSE))
251
+
252
+ ---
253
+
254
+ *Principle: proof before code, formal definitions, falsifiability, and honest
255
+ reporting of negative results — see [CONTRIBUTING.md](CONTRIBUTING.md).*
@@ -0,0 +1,31 @@
1
+ grounded_reasoning/__init__.py,sha256=XFxfon70jvzj8bPAL6fzYdnfAxAEolQeLdB1n4ufKBo,1112
2
+ grounded_reasoning-0.1.0.dist-info/licenses/LICENSE,sha256=MqhOEz7X42LVfaxFNmWGqTHENDxbC64xidGpANVud8k,1088
3
+ src/__init__.py,sha256=btdnrY03zgQ1GeBCUyB7jZnkLvz6UhGkMf7wwUdnEvs,94
4
+ src/agent/__init__.py,sha256=yxo279ck2ud0bOn00cFZRHdTN215N-OGDFgd_0mpE5k,580
5
+ src/agent/mcp_server.py,sha256=dvIcm7pGzEhe2JopnBQ70OMgbS_36X0bAGIz90bhU0o,1135
6
+ src/agent/tool.py,sha256=AKT5OvHacnJI8GY3gMt12gI4trc0SZWOk3hQJY3Msds,3431
7
+ src/agent/verifier.py,sha256=YukMbHo15Q6P9siwYHZ1XUhMYVFJ5AOxp-ZZiqltEu4,7701
8
+ src/experiments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ src/experiments/agent_demo.py,sha256=mVF_6UAgNuYg8rpqobQWPxe1RZDqoysbOcrD9KtRPoU,4215
10
+ src/experiments/clutrr_eval.py,sha256=oGjBbBNhDEzUP-IBWEyo-oXtwHYcfP7yGX5rom6od7M,10603
11
+ src/experiments/conformal_llm_eval.py,sha256=a4Jz76OlUlLkZg5qE4iNee4Wz8l_n42_Qkub30tYc74,7397
12
+ src/experiments/guard_cost_eval.py,sha256=v68m1DUa3cu7AEi5VrEefPhsFQk8KeIgSeiZjlXp4Qw,4000
13
+ src/experiments/guard_llm_eval.py,sha256=Dpf100Xikkwutx8kO4vMsh1kzKy6uHcujlBEbWy85Jg,5614
14
+ src/experiments/inference_eval.py,sha256=FMB-mejGduzrvaHvACPYF6oRE9zWn0k6pEZLLsqI6GY,4165
15
+ src/experiments/nl_ontology_eval.py,sha256=oRMGD2bLFWej-dapAdvx3aewwsRjPIrz3H6_Y4LbXQU,9334
16
+ src/experiments/self_grounded_eval.py,sha256=Xi6vFFXQuChyke6E7VB7P5X-5CCjQ4WaCpLskBcCqoo,6438
17
+ src/reasoning/__init__.py,sha256=x8cuGTx7m_yqXj5wM-lQ4ZXRg6qqXFEBIGKqqm99d-4,610
18
+ src/reasoning/abstract_inference.py,sha256=Lchg_rnGkvSMy5LcJhb_GZmjwSu_K9Q_FKa0hnP7L_Q,7045
19
+ src/reasoning/composition_algebra.py,sha256=eK4jI4cBmRXDm5vHB9rWhLfFpjcasQB3bKq-I6PZDFg,2491
20
+ src/reasoning/conformal_reasoning.py,sha256=PyT2n_Zeb1OHOlHAIx-YMJD7Cj_9JYy1gll7JRcI6PU,3115
21
+ src/reasoning/horn.py,sha256=lVGWcZPQFS_BPcoAJpLEg6Nk9UYnG35_zKfE5Fn-8w4,2124
22
+ src/reasoning/llm_client.py,sha256=QME0RW2X7Gm9z9IqoHZCce1qinKlcKV1KD-7Wepg92w,5852
23
+ src/reasoning/operator_algebra.py,sha256=6Ix0k8sg9v3a-g7R3RwUYsahCSXLuxC6axZJWiiCXeo,5094
24
+ src/reasoning/relation_spectrum.py,sha256=pFe0LSQx4krFhseaQom-QGn2kwn0GGnXwjtHFu_kVpI,4428
25
+ src/theory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ src/theory/theorems.py,sha256=5j4B8eypv2zN1jAkjRfdJn0a2I2zdgWjj4QwfPQ3fxw,27740
27
+ grounded_reasoning-0.1.0.dist-info/METADATA,sha256=s9vwvbBcwpc6rAYnsgnzK7rEDtcp0J1NNlnxZ3NpV38,13317
28
+ grounded_reasoning-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
29
+ grounded_reasoning-0.1.0.dist-info/entry_points.txt,sha256=LeiyVmu4Bw3Y8lVpNsTsiTd8Zg_GyVrokAtv3Q91p5I,69
30
+ grounded_reasoning-0.1.0.dist-info/top_level.txt,sha256=nq6246FMNze-gecMmsMSXlX1pvQpMc_GecIcV0RTMMA,23
31
+ grounded_reasoning-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ grounded-reasoning-mcp = src.agent.mcp_server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 grounded-reasoning contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ grounded_reasoning
2
+ src
src/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """RAG-Laclac: Research framework for beyond-RAG retrieval methods."""
2
+
3
+ __version__ = "0.1.0"
src/agent/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ """
2
+ Agent/LLM integration layer for grounded reasoning.
3
+
4
+ - GroundedReasoner : facade — load facts, verify multi-hop relational claims.
5
+ - verify_relation : a stateless function-calling tool (0 tokens, returns a proof).
6
+ - TOOL_SPEC : tool schema (Anthropic/OpenAI) for registering with a model.
7
+ """
8
+ from src.agent.tool import TOOL_SPEC, openai_tool_spec, run_tool, verify_relation
9
+ from src.agent.verifier import GroundedReasoner, Verdict
10
+
11
+ __all__ = [
12
+ "GroundedReasoner",
13
+ "Verdict",
14
+ "verify_relation",
15
+ "run_tool",
16
+ "TOOL_SPEC",
17
+ "openai_tool_spec",
18
+ ]
@@ -0,0 +1,37 @@
1
+ """
2
+ MCP server — exposes `verify_relation` as an MCP tool so ANY MCP-compatible agent
3
+ (Claude, etc.) can use it directly: the agent verifies a relational claim before
4
+ asserting it, at 0 tokens, with a proof.
5
+
6
+ Run (requires `pip install mcp`):
7
+ python -m src.agent.mcp_server # stdio server
8
+
9
+ The `mcp` import is LAZY-loaded inside build_server() so this module still imports
10
+ cleanly even without `mcp` installed (tests/CI don't need the dependency).
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from src.agent.tool import TOOL_SPEC, verify_relation
15
+
16
+
17
+ def build_server():
18
+ """Build a FastMCP server exposing the verify_relation tool (lazy `mcp` import)."""
19
+ from mcp.server.fastmcp import FastMCP
20
+
21
+ server = FastMCP("grounded-reasoning")
22
+
23
+ @server.tool(name=TOOL_SPEC["name"], description=TOOL_SPEC["description"])
24
+ def verify_relation_tool(
25
+ facts: list[list[str]], subject: str, relation: str, object: str # noqa: A002
26
+ ) -> dict:
27
+ return verify_relation(facts, subject, relation, object)
28
+
29
+ return server
30
+
31
+
32
+ def main() -> None:
33
+ build_server().run()
34
+
35
+
36
+ if __name__ == "__main__":
37
+ main()
src/agent/tool.py ADDED
@@ -0,0 +1,93 @@
1
+ """
2
+ A tool for AGENTS (function-calling) — verifies a relational claim BEFORE it is
3
+ asserted.
4
+
5
+ Stateless & JSON-friendly: the agent passes `facts` + a claim, and gets back a
6
+ verdict + proof path. Catches multi-hop relational hallucination at 0 LLM tokens,
7
+ with a precision guarantee (Theorem G).
8
+
9
+ Use with Anthropic/OpenAI function-calling:
10
+ tools=[TOOL_SPEC]; ... when the model calls the tool -> run_tool(tool_input)
11
+ Or call directly: verify_relation(facts, subject, relation, object).
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from src.agent.verifier import GroundedReasoner
16
+
17
+ TOOL_SPEC = {
18
+ "name": "verify_relation",
19
+ "description": (
20
+ "Verify whether a relational claim is grounded (provable by chaining the "
21
+ "given atomic facts) BEFORE asserting it. Use this to catch hallucinated "
22
+ "multi-hop relations (e.g. ancestor/prerequisite/cause chains). Returns "
23
+ "grounded=false with proof=null when the claim cannot be derived, and a "
24
+ "proof path when it can. Costs zero model tokens and never accepts an "
25
+ "ungrounded claim."
26
+ ),
27
+ "input_schema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "facts": {
31
+ "type": "array",
32
+ "description": "Known atomic (one-hop) facts as [subject, relation, object] triples.",
33
+ "items": {
34
+ "type": "array",
35
+ "items": {"type": "string"},
36
+ "minItems": 3,
37
+ "maxItems": 3,
38
+ },
39
+ },
40
+ "subject": {"type": "string", "description": "Start entity of the claim."},
41
+ "relation": {
42
+ "type": "string",
43
+ "description": "Relation to verify transitively (e.g. 'parent' to test ancestor).",
44
+ },
45
+ "object": {"type": "string", "description": "End entity of the claim."},
46
+ },
47
+ "required": ["facts", "subject", "relation", "object"],
48
+ },
49
+ }
50
+
51
+
52
+ def openai_tool_spec() -> dict:
53
+ """TOOL_SPEC in OpenAI/DeepSeek/Groq (function-calling) format."""
54
+ return {
55
+ "type": "function",
56
+ "function": {
57
+ "name": TOOL_SPEC["name"],
58
+ "description": TOOL_SPEC["description"],
59
+ "parameters": TOOL_SPEC["input_schema"],
60
+ },
61
+ }
62
+
63
+
64
+ def verify_relation(facts, subject: str, relation: str, object: str) -> dict: # noqa: A002
65
+ """
66
+ Verify `subject --relation*--> object` from `facts`. Returns a JSON dict:
67
+ {grounded, proof, confidence, relation}.
68
+
69
+ Tolerant of LLM input: SKIPS any fact not shaped like [s, r, o] (instead of
70
+ crashing the agent), and reports the number skipped under `skipped_facts`.
71
+ """
72
+ gr = GroundedReasoner()
73
+ clean, skipped = [], 0
74
+ for t in facts or []:
75
+ if isinstance(t, (list, tuple)) and len(t) == 3 and all(x is not None for x in t):
76
+ clean.append([str(t[0]), str(t[1]), str(t[2])])
77
+ else:
78
+ skipped += 1
79
+ gr.add_facts(clean)
80
+ out = gr.verify(str(subject), str(object), via=str(relation)).as_dict()
81
+ if skipped:
82
+ out["skipped_facts"] = skipped
83
+ return out
84
+
85
+
86
+ def run_tool(tool_input: dict) -> dict:
87
+ """Dispatcher for function-calling: takes the tool's JSON input, returns a verdict dict."""
88
+ return verify_relation(
89
+ tool_input["facts"],
90
+ tool_input["subject"],
91
+ tool_input["relation"],
92
+ tool_input["object"],
93
+ )