knowlytix-knowledge 0.0.1__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,818 @@
1
+ # DocGMS: Geometric Expert System with LLM-Augmented Learning
2
+
3
+ ## 1. Vision
4
+
5
+ DocGMS is a **persistent geometric expert system** that combines:
6
+ - **GMS** (Geometric Memory Systems) as the structured knowledge back-end: exact recall, contradiction detection, consistency checking, relational reasoning
7
+ - **LLM** (Claude or local models) as the reasoning/language front-end: open-ended reasoning, natural language understanding, zero-shot generalization
8
+
9
+ The system ingests documents, builds a geometric knowledge store on the hypersphere, answers queries with geometric confidence, and **grows smarter over time** by learning verified knowledge from LLM interactions.
10
+
11
+ **Key principle:** The LLM proposes. GMS verifies. Verified knowledge grows GMS. GMS gets smarter.
12
+
13
+ ---
14
+
15
+ ## 2. Architecture
16
+
17
+ ```
18
+ ┌──────────────────────────────┐
19
+ │ LLM (Claude) │
20
+ │ - Open-ended reasoning │
21
+ │ - NLU / ambiguous questions │
22
+ │ - Zero-shot generalization │
23
+ └──────────┬───────────────────┘
24
+ │ answer text
25
+
26
+ ┌──────────────────────────────┐
27
+ │ Triple Extraction (LLM) │
28
+ │ answer → (h, r, t) triples │
29
+ │ + numeric values │
30
+ └──────────┬───────────────────┘
31
+ │ extracted triples
32
+
33
+ ┌────────────────────────────────────────┐
34
+ │ GMS Verification Layer │
35
+ │ │
36
+ │ 1. score_triple(h, r, t) │
37
+ │ → geodesic distance on S^{m-1} │
38
+ │ → lower = more plausible │
39
+ │ │
40
+ │ 2. tension_energy(a, b) │
41
+ │ → Clifford algebra: E = 2sin(θ/2) │
42
+ │ → 0=agree, √2=irrelevant, 2=contra │
43
+ │ │
44
+ │ 3. path_holonomy(path, direct) │
45
+ │ → ‖R_path R_direct^{-1} - I‖_F │
46
+ │ → 0=consistent, high=inconsistent │
47
+ └──────┬─────────────────┬───────────────┘
48
+ │ │
49
+ contradicts? consistent?
50
+ │ │
51
+ ▼ ▼
52
+ ┌──────────┐ ┌────────────────────┐
53
+ │ REJECT │ │ LEARN (grow GMS) │
54
+ │ + report │ │ 1. Expand embeddings│
55
+ │ why │ │ 2. Sentence-xformer │
56
+ └──────────┘ │ init │
57
+ │ 3. Freeze existing │
58
+ │ 4. Riemannian GD on │
59
+ │ new embeddings │
60
+ │ 5. ENM write for │
61
+ │ numeric facts │
62
+ │ 6. Post-verify │
63
+ └────────────────────┘
64
+ ```
65
+
66
+ ### 2.1 The Query Loop
67
+
68
+ Every query follows this flow:
69
+
70
+ 1. **GMS first**: Attempt structured answer via ENM lookup, triple scoring, phase check, or relational transport. If confident answer found, return immediately.
71
+
72
+ 2. **LLM augmented**: If GMS cannot answer (open-ended, ambiguous, or novel domain), query the LLM with GMS context prepended for grounding:
73
+ - Relevant triples from the store
74
+ - Exact numeric values from ENM
75
+ - Phase-encoded threshold results
76
+ - Known contradictions and tensions
77
+
78
+ 3. **Extract**: Decompose LLM answer into (head, relation, tail) triples + numeric values.
79
+
80
+ 4. **Verify**: Score each extracted triple against GMS:
81
+ - `score_triple()`: geodesic distance — is this fact plausible given the manifold?
82
+ - `tension_energy()`: does this contradict existing entities?
83
+ - `path_holonomy()`: is this consistent with existing relational paths?
84
+
85
+ 5. **Learn or Reject**:
86
+ - If verified (no contradictions, consistent paths) and contains novel facts:
87
+ - Expand GMS embedding tables for new entities
88
+ - Initialize via sentence-transformers
89
+ - Freeze existing embeddings
90
+ - Riemannian gradient descent on new embeddings only
91
+ - Write numeric values to ENM
92
+ - The store grows — future queries benefit
93
+ - If contradictions detected:
94
+ - Reject the write
95
+ - Report which facts contradicted and why (tension energy values, holonomy defects)
96
+
97
+ 6. **Return**: Answer with full provenance — source, confidence, verification report, whether new facts were learned.
98
+
99
+ ### 2.2 GMS vs LLM: Complementary Strengths
100
+
101
+ | Capability | GMS Handles | LLM Handles |
102
+ |---|---|---|
103
+ | Exact numeric recall | ENM register (100%, lossless, SHA-256) | - |
104
+ | Contradiction detection | Tension energy E(a,b) in u-space | - |
105
+ | Path consistency | Holonomy defect via rotor composition | - |
106
+ | Threshold/inequality | Phase encoding on S^1 | - |
107
+ | Multi-hop reasoning | Cayley rotor transport: R_L...R_1 @ v_h | - |
108
+ | Link prediction | score_all_tails(): geodesic ranking | - |
109
+ | Open-ended reasoning | - | Natural language generation |
110
+ | Ambiguous questions | - | NLU + intent classification |
111
+ | Novel domains (zero-shot) | - | Generalization from pretraining |
112
+ | Explanation generation | - | Articulate reasoning in text |
113
+ | Answer verification | Scores + checks LLM outputs | - |
114
+ | Knowledge growth | Riemannian GD on verified facts | Proposes new facts for verification |
115
+
116
+ ### 2.3 Why GMS + LLM Beats LLM Alone
117
+
118
+ 1. **Exact Numerical Recall**: GMS 100% vs LLM ~2%. ENM stores IEEE 754 float64 with SHA-256 integrity.
119
+
120
+ 2. **Contradiction Detection**: Tension energy (Clifford algebra) is a continuous geometric signal, not pattern matching. F1 = 0.71 vs 0.57 in paper experiments.
121
+
122
+ 3. **Every LLM Answer is Audited**: Extracted triples are scored on the manifold. The system quantifies how plausible each claim is and flags contradictions — the LLM never gets the final word unverified.
123
+
124
+ 4. **Persistent Memory**: The store persists to disk and grows over sessions. An LLM's context window is ephemeral.
125
+
126
+ 5. **Integrity Protection**: Contradiction gates prevent bad knowledge from entering the store. RAG has no such mechanism.
127
+
128
+ 6. **Speed**: Structured queries answered in <1ms (manifold ops) vs ~2s (API call).
129
+
130
+ 7. **The Store Gets Smarter**: Every verified interaction that introduces novel, consistent facts expands the manifold. Link prediction improves, contradiction detection refines, multi-hop paths become richer.
131
+
132
+ ---
133
+
134
+ ## 3. File Structure
135
+
136
+ ```
137
+ docgms/
138
+ __init__.py # Package + public API exports
139
+ __main__.py # Entry: python -m docgms
140
+ cli.py # CLI: ingest, query, compare, status
141
+ config.py # Configuration dataclasses
142
+ convert.py # PDF/TeX/MD -> markdown conversion
143
+ llm_backend.py # Unified LLM interface (Anthropic + local)
144
+ store.py # GMSExpertStore: persistent geometric knowledge store
145
+ ingest.py # Document ingestion into store
146
+ extract.py # LLM answer -> triple extraction + GMS scoring
147
+ verify.py # Contradiction (tension) + consistency (holonomy)
148
+ learn.py # Runtime memory growth via Riemannian GD
149
+ query.py # Query engine: the full GMS + LLM + verify + learn loop
150
+ compare.py # GMS+LLM vs LLM-only evaluation
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 4. Module Specifications
156
+
157
+ ### 4.1 `config.py` — Configuration
158
+
159
+ ```python
160
+ @dataclass
161
+ class ConvertConfig:
162
+ llm_model: str = "claude-sonnet-4-20250514"
163
+ llm_backend: str = "anthropic" # "anthropic" or "local"
164
+ local_model_name: str = ""
165
+ max_pages: int = 200
166
+
167
+ @dataclass
168
+ class VerifyConfig:
169
+ tau_contra: float = 1.7 # tension energy contradiction threshold
170
+ tau_ent: float = 0.8 # entailment threshold
171
+ tau_path: float = 0.5 # holonomy consistency threshold
172
+ holonomy_alpha: float = 0.1 # decay for effective holonomy
173
+ min_plausibility: float = 1.0 # max geodesic dist for plausible triple
174
+
175
+ @dataclass
176
+ class LearnConfig:
177
+ n_steps: int = 50 # Riemannian GD steps for new entities
178
+ lr_write: float = 5e-3
179
+ freeze_existing: bool = True
180
+ contradiction_gate: bool = True # reject writes that introduce contradictions
181
+ auto_learn: bool = True # auto-write verified novel facts from Q&A
182
+
183
+ @dataclass
184
+ class DocGMSConfig:
185
+ convert: ConvertConfig
186
+ geometry: GeometryConfig # from knowlytix.core.config (d_v=128, d_u=128, m=64)
187
+ loss: LossConfig # from knowlytix.core.config
188
+ train: TrainConfig # from knowlytix.core.config
189
+ memory: MemoryConfig # from knowlytix.core.config
190
+ verify: VerifyConfig
191
+ learn: LearnConfig
192
+ store_path: str = "docgms_store/" # persistent store directory
193
+ ```
194
+
195
+ Imports from: `knowlytix.core.config.GeometryConfig`, `LossConfig`, `TrainConfig`, `MemoryConfig`
196
+
197
+ ### 4.2 `llm_backend.py` — LLM Interface
198
+
199
+ ```python
200
+ class LLMBackend(ABC):
201
+ def call(self, system: str, user: str, max_tokens: int = 2048) -> str: ...
202
+
203
+ class AnthropicBackend(LLMBackend):
204
+ """Anthropic API. Pattern from knowlytix/benchmark/llm_caller.py."""
205
+ def __init__(self, model: str = "claude-sonnet-4-20250514"): ...
206
+
207
+ class LocalTransformersBackend(LLMBackend):
208
+ """HuggingFace model on GPU via transformers."""
209
+ def __init__(self, model_name: str, device: str = "cuda"): ...
210
+
211
+ def create_backend(config: ConvertConfig) -> LLMBackend: ...
212
+ ```
213
+
214
+ ### 4.3 `convert.py` — Document Conversion
215
+
216
+ ```python
217
+ def detect_format(path: str) -> str:
218
+ """Returns 'pdf', 'tex', 'md', 'txt', 'docx'."""
219
+
220
+ def convert_pdf_to_markdown(path: str, config: ConvertConfig, llm_fn: Callable) -> str:
221
+ """PyMuPDF for text extraction. LLM for complex table reconstruction."""
222
+
223
+ def convert_tex_to_markdown(path: str, config: ConvertConfig, llm_fn: Callable) -> str:
224
+ """pandoc subprocess for structure. Post-process LaTeX remnants.
225
+ LLM fallback for tabular environments."""
226
+
227
+ def convert_document(path: str, config: ConvertConfig, llm_fn: Callable) -> str:
228
+ """Top-level dispatcher. .md files read directly."""
229
+ ```
230
+
231
+ Dependencies: `pymupdf`, system `pandoc`
232
+
233
+ ### 4.4 `store.py` — GMSExpertStore (the core)
234
+
235
+ ```python
236
+ class GMSExpertStore:
237
+ """Persistent geometric expert system store.
238
+
239
+ Manages: trained GKG model, ENM register, compression memory,
240
+ transport layer, adapter mappings. Supports ingestion of new
241
+ documents and runtime growth from verified Q&A.
242
+
243
+ Disk layout (store_path/):
244
+ model.pt — GKG weights
245
+ enm.json — ENM key-value pairs
246
+ adapter.json — entity/relation vocabularies
247
+ metadata.json — ingestion history, stats
248
+ documents/ — ingested markdown copies
249
+ """
250
+
251
+ def __init__(self, config: DocGMSConfig, device: torch.device): ...
252
+
253
+ # --- Core state ---
254
+ model: GeometricKnowledgeGraph
255
+ adapter: GraphToGMS
256
+ enm: ExactNumericalMemory
257
+ doc_graph: DocumentGraph
258
+ transport: RelationalTransport
259
+ compression: CompressionMemory
260
+ router: MemoryRouter
261
+
262
+ # --- Persistence ---
263
+ def save(self): ...
264
+ def load(self) -> bool: ...
265
+ def exists(self) -> bool: ...
266
+
267
+ # --- GMS Operations (thin wrappers around model/transport/enm) ---
268
+ def score_triple(self, head: str, rel: str, tail: str) -> float:
269
+ """Geodesic distance on S^{m-1}. Lower = more plausible."""
270
+
271
+ def tension_energy(self, entity_a: str, entity_b: str) -> float:
272
+ """Clifford tension energy. 0=agree, sqrt(2)=irrelevant, 2=contradict.
273
+ Uses: knowlytix.core.graph.gkg.tension_energy_pairs() -> knowlytix.core.geometry.clifford.tension_energy()"""
274
+
275
+ def check_holonomy(self, relation_path: list[str], direct_relation: str) -> float:
276
+ """Holonomy defect. Uses: transport.path_holonomy()"""
277
+
278
+ def is_path_consistent(self, relation_path: list[str], direct: str) -> bool:
279
+ """True if holonomy defect <= tau_path. Uses: transport.is_path_consistent()"""
280
+
281
+ def lookup_enm(self, category: str, entity_id: str) -> float | None:
282
+ """Exact numeric lookup with SHA-256 integrity."""
283
+
284
+ def link_predict(self, head: str, relation: str, top_k: int = 10) -> list[tuple[str, float]]:
285
+ """Top-k tail predictions using model.score_all_tails()."""
286
+
287
+ def query_triples(self, head=None, relation=None, tail=None) -> list[tuple]:
288
+ """Pattern match on doc_graph.triples."""
289
+
290
+ def find_contradictions(self) -> list[tuple]:
291
+ """doc_graph.find_contradictions()."""
292
+
293
+ def stats(self) -> dict:
294
+ """entities, relations, triples, enm_entries, documents ingested."""
295
+ ```
296
+
297
+ Imports:
298
+ - `knowlytix.core.graph.gkg.GeometricKnowledgeGraph`
299
+ - `knowlytix.core.graph.transport.RelationalTransport`
300
+ - `knowlytix.core.memory.enm.ExactNumericalMemory`, `ENMKey`
301
+ - `knowlytix.core.memory.compression.CompressionMemory`
302
+ - `knowlytix.core.memory.router.MemoryRouter`
303
+ - `knowlytix.core.train_finstructbench.GraphToGMS`
304
+ - `knowlytix.benchmark.graph.DocumentGraph`
305
+
306
+ ### 4.5 `ingest.py` — Document Ingestion
307
+
308
+ ```python
309
+ def ingest_document(store: GMSExpertStore, document_path: str,
310
+ llm_backend: LLMBackend, config: DocGMSConfig,
311
+ device: torch.device) -> IngestResult:
312
+ """Ingest a document into the expert store.
313
+
314
+ First document: full pipeline
315
+ 1. convert_document(path) -> markdown
316
+ 2. ingest_markdown(path) -> DocumentGraph [knowlytix.benchmark.ingest]
317
+ 3. GraphToGMS(doc_graph) -> adapter [knowlytix.core.train_finstructbench]
318
+ 4. train_gms(adapter, device) -> model [knowlytix.core.train_finstructbench]
319
+ 5. populate_enm(doc_graph, adapter) -> enm [knowlytix.core.train_finstructbench]
320
+ 6. Create transport, compression, router
321
+ 7. Save store
322
+
323
+ Subsequent documents: incremental growth
324
+ 1. convert_document(path) -> markdown
325
+ 2. ingest_markdown(path) -> new DocumentGraph
326
+ 3. Identify new entities and triples not in store
327
+ 4. learn.expand_and_optimize(new_triples, store) [learn.py]
328
+ 5. Add new ENM entries
329
+ 6. Merge doc_graph triples into store.doc_graph
330
+ 7. Save store
331
+
332
+ Returns IngestResult(new_entities, new_triples, new_enm, stats).
333
+ """
334
+
335
+ def ingest_markdown_text(store, markdown_text, config, device) -> IngestResult:
336
+ """Ingest raw markdown string (for programmatic use)."""
337
+ ```
338
+
339
+ ### 4.6 `extract.py` — Triple Extraction + GMS Scoring
340
+
341
+ ```python
342
+ @dataclass
343
+ class ExtractedTriple:
344
+ head: str
345
+ relation: str
346
+ tail: str
347
+ numeric_value: float | None = None # if triple contains a number
348
+
349
+ @dataclass
350
+ class ScoredTriple:
351
+ triple: ExtractedTriple
352
+ gms_score: float # geodesic distance (lower = better)
353
+ head_match: str | None # nearest entity in store, or None
354
+ tail_match: str | None
355
+ is_novel: bool # True if entity not in store
356
+
357
+ @dataclass
358
+ class PlausibilityReport:
359
+ plausible: bool
360
+ mean_score: float
361
+ worst_score: float
362
+ novel_count: int
363
+ scored_triples: list[ScoredTriple]
364
+
365
+ def extract_triples(answer_text: str, question: str,
366
+ llm_backend: LLMBackend) -> list[ExtractedTriple]:
367
+ """Prompt LLM to decompose answer into structured triples.
368
+
369
+ Prompt template:
370
+ Given this question and answer, extract all factual claims as
371
+ (subject, relationship, object) triples. For numeric facts,
372
+ include the exact value. Format: one triple per line as
373
+ TRIPLE: subject | relationship | object [| numeric_value]
374
+ """
375
+
376
+ def score_triples(triples: list[ExtractedTriple],
377
+ store: GMSExpertStore,
378
+ device: torch.device) -> list[ScoredTriple]:
379
+ """Score each triple against GMS manifold.
380
+
381
+ For each triple:
382
+ 1. Fuzzy-match head/tail to nearest entity in store.adapter vocab
383
+ (normalized string matching + optional semantic matching via
384
+ encode_texts + cosine similarity against store embeddings)
385
+ 2. If matched: store.score_triple(h, r, t) -> geodesic distance
386
+ 3. If not matched: mark is_novel=True (candidate for learning)
387
+ 4. For numeric values: cross-check against ENM if entity exists
388
+ """
389
+
390
+ def assess_plausibility(scored: list[ScoredTriple],
391
+ threshold: float = 1.0) -> PlausibilityReport:
392
+ """Aggregate triple scores into overall plausibility verdict."""
393
+ ```
394
+
395
+ ### 4.7 `verify.py` — Contradiction + Consistency Checking
396
+
397
+ ```python
398
+ @dataclass
399
+ class ContradictionResult:
400
+ entity_a: str
401
+ entity_b: str
402
+ tension_energy: float # 0=agree, sqrt(2)=irrelevant, 2=contradict
403
+ is_contradiction: bool # E > tau_contra
404
+ is_entailment: bool # E < tau_ent
405
+
406
+ @dataclass
407
+ class HolonomyResult:
408
+ path: list[str] # relation path [r1, r2, ...]
409
+ direct: str # direct relation
410
+ defect: float # holonomy defect value
411
+ is_consistent: bool # defect <= tau_path
412
+
413
+ @dataclass
414
+ class VerificationReport:
415
+ contradictions: list[ContradictionResult]
416
+ inconsistencies: list[HolonomyResult]
417
+ overall_consistent: bool
418
+ confidence: float # 1.0=fully verified, 0.0=many issues
419
+
420
+ def check_contradictions(entity_pairs: list[tuple[str, str]],
421
+ store: GMSExpertStore,
422
+ config: VerifyConfig) -> list[ContradictionResult]:
423
+ """Tension energy check for each entity pair.
424
+
425
+ Uses store.tension_energy(a, b) which calls:
426
+ knowlytix.core.graph.gkg.tension_energy_pairs(idx_a, idx_b)
427
+ -> knowlytix.core.geometry.clifford.tension_energy(u_a, u_b)
428
+ -> E = 2 * sin(theta/2)
429
+ """
430
+
431
+ def check_holonomy(triples: list[ExtractedTriple],
432
+ store: GMSExpertStore,
433
+ config: VerifyConfig) -> list[HolonomyResult]:
434
+ """Find relation triangles formed by new triples + existing graph.
435
+
436
+ For each new triple (h, r_new, t):
437
+ - Find existing paths from h to t through intermediate entities
438
+ - Compute path_holonomy(existing_path, r_new)
439
+ - Flag if defect > tau_path
440
+
441
+ Uses store.check_holonomy() which calls:
442
+ transport.path_holonomy(relation_path, direct_relation)
443
+ -> compose_rotors(path) @ R_direct^{-1} - I -> Frobenius norm
444
+ -> effective_holonomy(raw_defect, path_length, alpha)
445
+ """
446
+
447
+ def verify_answer(scored_triples: list[ScoredTriple],
448
+ store: GMSExpertStore,
449
+ config: VerifyConfig) -> VerificationReport:
450
+ """Combined verification: contradictions + holonomy + confidence.
451
+
452
+ 1. For all pairs of (new entity, existing entity): check_contradictions
453
+ 2. For all new triples forming paths: check_holonomy
454
+ 3. Compute confidence: 1.0 - (n_contradictions + n_inconsistencies) / n_triples
455
+ """
456
+ ```
457
+
458
+ ### 4.8 `learn.py` — Runtime Memory Growth
459
+
460
+ ```python
461
+ @dataclass
462
+ class LearnResult:
463
+ accepted: bool
464
+ reason: str # "consistent" or "contradiction: ..."
465
+ new_entities: list[str]
466
+ scores_before: list[float] # geodesic scores pre-optimization
467
+ scores_after: list[float] # geodesic scores post-optimization
468
+ tension_before: list[float]
469
+ tension_after: list[float]
470
+ n_steps_run: int
471
+
472
+ class RuntimeLearner:
473
+ """Grow GMS by writing verified knowledge via Riemannian gradient descent.
474
+
475
+ Follows the protocol from scripts/experiment_llm_knowlytix.core.py Experiment 7:
476
+ 1. Freeze trained GKG (rotors, projections, existing embeddings)
477
+ 2. Expand embedding tables with new entities
478
+ 3. Init new entity embeddings via sentence-transformers (encode_texts)
479
+ 4. Riemannian GD on new embeddings only, minimizing:
480
+ - Triple scoring loss: d_geo(R_r @ v_h, v_t) for new triples
481
+ - Tension energy targets: E=0 for consistent pairs, E=2 for contradictory
482
+ 5. Post-write verification
483
+ 6. If contradiction gate enabled and post-write contradictions found -> rollback
484
+ """
485
+
486
+ def __init__(self, store: GMSExpertStore, config: LearnConfig): ...
487
+
488
+ def learn_triples(self, new_triples: list[ExtractedTriple],
489
+ device: torch.device) -> LearnResult:
490
+ """Full learning pipeline with contradiction guard.
491
+
492
+ Steps:
493
+ a. Identify new entities (not in store.adapter)
494
+ b. Pre-check: verify_answer() on new triples against store
495
+ c. If contradiction_gate and contradictions -> return rejected
496
+ d. Expand embedding tables
497
+ (pattern: experiment_llm_knowlytix.core.py:1889-1939)
498
+ e. Encode new entities: encode_texts() for v-space and u-space
499
+ (using sentence-transformers/all-MiniLM-L6-v2 and nli-mpnet-base-v2)
500
+ f. Freeze existing params
501
+ g. Create new-entity-only params (v, u_proj, specificity, confidence)
502
+ h. Riemannian GD loop for n_steps
503
+ (pattern: experiment_llm_knowlytix.core.py:1982-2138)
504
+ i. Post-verification: re-check tension + holonomy
505
+ j. If still contradictory -> rollback (restore original weights)
506
+ k. Update adapter mappings (entity_to_idx, idx_to_entity)
507
+ l. Return LearnResult with before/after metrics
508
+ """
509
+
510
+ def learn_enm(self, category: str, entity_id: str, value: float) -> bool:
511
+ """Write exact numeric value to ENM. Always accepts (no contradiction
512
+ possible for exact storage)."""
513
+
514
+ def _expand_embeddings(self, new_names: list[str], device): ...
515
+ def _riemannian_optimize(self, triple_tensors, tension_targets, device): ...
516
+ def _rollback(self): ...
517
+ ```
518
+
519
+ Imports:
520
+ - `knowlytix.core.graph.encoders.encode_texts` — sentence-transformer encoding
521
+ - `knowlytix.core.geometry.sphere.normalize` — sphere projection
522
+ - `knowlytix.core.memory.enm.ENMKey` — ENM key construction
523
+ - Pattern from `scripts/experiment_llm_knowlytix.core.py:1848-2238`
524
+
525
+ ### 4.9 `query.py` — The Expert System Query Engine
526
+
527
+ ```python
528
+ @dataclass
529
+ class QueryResult:
530
+ answer: Any # the answer
531
+ source: str # "enm"|"triple"|"phase"|"transport"|"llm"|"gms+llm"
532
+ confidence: float # GMS-derived confidence
533
+ extracted_triples: list[ScoredTriple] # triples from LLM answer (if LLM was used)
534
+ verification: VerificationReport | None # contradiction + holonomy (if LLM used)
535
+ learned: bool # True if novel facts were written to store
536
+ learn_result: LearnResult | None # details of learning (if it happened)
537
+ gms_context: str # GMS context provided to LLM (if augmented)
538
+
539
+ class QueryEngine:
540
+ """Expert system query interface.
541
+
542
+ The full query loop:
543
+ 1. GMS attempts structured answer
544
+ 2. If insufficient: LLM answers (with GMS context)
545
+ 3. Extract triples from LLM answer
546
+ 4. Score + verify (contradiction + holonomy)
547
+ 5. If verified + novel + auto_learn: grow GMS
548
+ 6. Return answer with full provenance
549
+ """
550
+
551
+ def __init__(self, store: GMSExpertStore, llm_backend: LLMBackend,
552
+ config: DocGMSConfig): ...
553
+
554
+ def query(self, question: str, mode: str = "gms_llm") -> QueryResult:
555
+ """Query the expert system.
556
+
557
+ Modes:
558
+ "gms_only" — structured GMS answer only
559
+ "llm_only" — LLM answer only (still verified by GMS)
560
+ "gms_llm" — GMS attempts first, LLM augments, verified, learned
561
+ """
562
+
563
+ def query_batch(self, questions: list[str], mode: str = "gms_llm") -> list[QueryResult]:
564
+ """Batch query."""
565
+
566
+ # --- Internal routing ---
567
+
568
+ def _classify_question(self, question: str) -> str:
569
+ """Heuristic classification:
570
+ - 'exact value', 'what is the', 'how much' -> exact_recall
571
+ - 'above', 'below', 'exceeds', 'threshold' -> threshold
572
+ - 'contradict', 'conflict', 'inconsistent' -> contradiction
573
+ - 'path', 'chain', 'leads to', 'through' -> multi_hop
574
+ - 'both', 'intersection', 'and also' -> cross_reference
575
+ - 'how many', 'count' -> counting
576
+ - default -> open_ended
577
+ """
578
+
579
+ def _gms_answer(self, question: str, qtype: str) -> tuple[Any, float] | None:
580
+ """Attempt GMS-only answer based on question type.
581
+
582
+ exact_recall: ENM lookup -> store.lookup_enm()
583
+ threshold: Phase check -> doc_graph phase encoders
584
+ contradiction: Tension energy -> store.tension_energy()
585
+ multi_hop: Transport -> transport.path_score()
586
+ cross_reference: Triple intersection -> store.query_triples()
587
+ counting: Triple count -> len(store.query_triples())
588
+
589
+ Returns (answer, confidence) or None if GMS can't answer.
590
+ """
591
+
592
+ def _gather_gms_context(self, question: str) -> str:
593
+ """Build GMS context for LLM augmentation.
594
+
595
+ 1. Keyword-match entities in store.adapter vocab
596
+ 2. For matched entities: gather related triples
597
+ 3. For matched entities: lookup ENM values
598
+ 4. For matched entities: check phase thresholds
599
+ 5. Format as structured context string
600
+ """
601
+
602
+ def _llm_answer(self, question: str, gms_context: str | None) -> str:
603
+ """Call LLM. If gms_context provided, prepend to prompt.
604
+
605
+ System prompt:
606
+ You are an expert analyst backed by a geometric knowledge store.
607
+ Use the provided structured context as ground truth.
608
+ For numeric values, prefer the exact values from the knowledge store.
609
+ If the context contradicts your knowledge, flag the discrepancy.
610
+
611
+ User prompt:
612
+ <context>{gms_context}</context>
613
+ <question>{question}</question>
614
+ """
615
+
616
+ def _attempt_learn(self, scored_triples: list[ScoredTriple],
617
+ verification: VerificationReport,
618
+ device: torch.device) -> LearnResult | None:
619
+ """If auto_learn=True and verification.overall_consistent and novel triples exist:
620
+ call RuntimeLearner.learn_triples() to grow GMS."""
621
+ ```
622
+
623
+ ### 4.10 `compare.py` — GMS+LLM vs LLM-Only Evaluation
624
+
625
+ ```python
626
+ @dataclass
627
+ class ComparisonRow:
628
+ qid: str
629
+ category: str
630
+ question: str
631
+ ground_truth: Any
632
+ # GMS-only
633
+ gms_answer: Any
634
+ gms_correct: bool
635
+ # LLM-only (verified by GMS)
636
+ llm_answer: Any
637
+ llm_correct: bool
638
+ llm_plausibility: float
639
+ llm_contradictions: int
640
+ llm_holonomy_violations: int
641
+ # GMS+LLM (augmented, verified, learned)
642
+ gms_llm_answer: Any
643
+ gms_llm_correct: bool
644
+ gms_llm_plausibility: float
645
+ gms_llm_contradictions: int
646
+ gms_llm_learned: bool
647
+
648
+ @dataclass
649
+ class ComparisonReport:
650
+ document: str
651
+ total_questions: int
652
+ scores: dict[str, int] # {mode: n_correct}
653
+ by_category: dict[str, dict]
654
+ rows: list[ComparisonRow]
655
+ plausibility_summary: dict[str, float] # {mode: mean_plausibility}
656
+ contradiction_summary: dict[str, int] # {mode: total_contradictions}
657
+ facts_learned: int # novel facts added to GMS
658
+ timestamp: str
659
+
660
+ class ComparisonRunner:
661
+ """Three-way comparison: gms_only vs llm_only vs gms_llm.
662
+
663
+ Uses knowlytix.benchmark.generators.default_generators() to auto-generate
664
+ questions from store.doc_graph topology.
665
+ """
666
+
667
+ def __init__(self, store: GMSExpertStore, query_engine: QueryEngine,
668
+ config: DocGMSConfig): ...
669
+
670
+ def run(self) -> ComparisonReport:
671
+ """
672
+ 1. Generate questions: default_generators().generate(store.doc_graph)
673
+ 2. For each question, run query_engine.query(q, mode) for all 3 modes
674
+ 3. Score: knowlytix.benchmark.scorers.score_answer(answer, ground_truth)
675
+ 4. Aggregate: accuracy + plausibility + contradictions + facts learned
676
+ """
677
+
678
+ def print_report(self, report: ComparisonReport): ...
679
+ def save_report(self, report: ComparisonReport, path: str): ...
680
+ ```
681
+
682
+ Imports:
683
+ - `knowlytix.benchmark.generators.default_generators` — question generation
684
+ - `knowlytix.benchmark.scorers.score_answer`, `PARSERS` — scoring
685
+
686
+ ### 4.11 `cli.py` + `__main__.py`
687
+
688
+ ```
689
+ Usage:
690
+ docgms ingest <document> # Ingest PDF/TeX/MD into expert store
691
+ docgms query "question" # Query the expert system
692
+ docgms query --interactive # Interactive Q&A session
693
+ docgms compare <document> # Three-way comparison
694
+ docgms status # Show store statistics
695
+ docgms export <path> # Export store state
696
+
697
+ Global flags:
698
+ --store-path PATH # Store directory (default: docgms_store/)
699
+ --device cuda|cpu
700
+ --mode gms_only|llm_only|gms_llm
701
+ --auto-learn / --no-auto-learn
702
+ --model MODEL # LLM model name
703
+ --epochs N # Training epochs (first ingest only)
704
+ ```
705
+
706
+ ---
707
+
708
+ ## 5. Reuse Map
709
+
710
+ | Component | Source | Usage |
711
+ |---|---|---|
712
+ | `ingest_markdown()` | `knowlytix.benchmark.ingest` | Markdown → DocumentGraph |
713
+ | `GraphToGMS` | `knowlytix.core.train_finstructbench` | DocumentGraph → training tensors |
714
+ | `train_gms()` | `knowlytix.core.train_finstructbench` | Training loop with Riemannian SGD |
715
+ | `populate_enm()` | `knowlytix.core.train_finstructbench` | DocumentGraph ENM → GMS ENM |
716
+ | `default_generators()` | `knowlytix.benchmark.generators` | Auto-generate evaluation questions |
717
+ | `score_answer()`, `PARSERS` | `knowlytix.benchmark.scorers` | Score answers + parse LLM output |
718
+ | `GeometricKnowledgeGraph` | `knowlytix.core.graph.gkg` | Triple scoring, tension energy |
719
+ | `RelationalTransport` | `knowlytix.core.graph.transport` | Multi-hop, holonomy |
720
+ | `ExactNumericalMemory` | `knowlytix.core.memory.enm` | Exact numeric storage |
721
+ | `CompressionMemory` | `knowlytix.core.memory.compression` | Approximate memory |
722
+ | `MemoryRouter` | `knowlytix.core.memory.router` | ENM vs compression dispatch |
723
+ | `encode_texts()` | `knowlytix.core.graph.encoders` | Sentence-transformer encoding |
724
+ | `RiemannianSGD` | `knowlytix.core.optim.riemannian` | Manifold-aware optimization |
725
+ | `GMSLoss` | `knowlytix.core.losses.combined` | Multi-component loss |
726
+ | `PhaseEncoder` | `knowlytix.core.geometry.phase` | Numeric threshold queries |
727
+ | `tension_energy()` | `knowlytix.core.geometry.clifford` | Clifford algebra contradiction |
728
+ | `holonomy_defect()` | `knowlytix.core.geometry.cayley` | Rotor composition consistency |
729
+ | Test-time write pattern | `scripts/experiment_llm_knowlytix.core.py:1848-2238` | Adapted in learn.py |
730
+ | LLM call pattern | `knowlytix/benchmark/llm_caller.py` | Adapted in llm_backend.py |
731
+
732
+ ---
733
+
734
+ ## 6. Data Flow Summary
735
+
736
+ ```
737
+ Document (PDF/TeX/MD)
738
+
739
+
740
+ [convert.py] ──────────────────────────► Markdown text
741
+
742
+
743
+ [knowlytix.benchmark.ingest] ───────────────► DocumentGraph
744
+ │ (ENM + triples + phase)
745
+
746
+ [knowlytix.core.train_finstructbench] ────────────► Trained GKG + ENM + Transport
747
+
748
+
749
+ [store.py: GMSExpertStore] ────────────► Persistent on disk
750
+
751
+
752
+ [query.py: QueryEngine]
753
+
754
+ ├── GMS answers structured queries (ENM, triples, phase, transport)
755
+
756
+ ├── LLM answers open-ended/ambiguous/zero-shot
757
+ │ │
758
+ │ ▼
759
+ │ [extract.py] → (h, r, t) triples
760
+ │ │
761
+ │ ▼
762
+ │ [verify.py] → tension energy + holonomy
763
+ │ │
764
+ │ ├── Contradicts → REJECT + report
765
+ │ │
766
+ │ └── Consistent → [learn.py] → GROW GMS
767
+ │ │
768
+ │ ├── Expand embeddings
769
+ │ ├── Riemannian GD
770
+ │ ├── Post-verify
771
+ │ └── Save store
772
+
773
+
774
+ QueryResult (answer + provenance + verification + learning)
775
+ ```
776
+
777
+ ---
778
+
779
+ ## 7. Implementation Order
780
+
781
+ | Phase | Files | Dependency | Estimated Lines |
782
+ |---|---|---|---|
783
+ | 1 | `config.py`, `__init__.py` | None | ~70 |
784
+ | 2 | `llm_backend.py` | config | ~100 |
785
+ | 3 | `convert.py` | llm_backend | ~250 |
786
+ | 4 | `store.py` | config, gms.*, knowlytix.benchmark.* | ~300 |
787
+ | 5 | `ingest.py` | store, convert | ~150 |
788
+ | 6 | `extract.py` | store, llm_backend | ~200 |
789
+ | 7 | `verify.py` | store | ~180 |
790
+ | 8 | `learn.py` | store, verify | ~250 |
791
+ | 9 | `query.py` | store, extract, verify, learn, llm_backend | ~300 |
792
+ | 10 | `compare.py` | query, knowlytix.benchmark.generators/scorers | ~150 |
793
+ | 11 | `cli.py`, `__main__.py` | all | ~120 |
794
+ | **Total** | | | **~2070** |
795
+
796
+ ---
797
+
798
+ ## 8. Testing Strategy
799
+
800
+ | Test | Validates | Expected |
801
+ |---|---|---|
802
+ | Ingest model_validation.md | Full pipeline | ~5000 triples, ~260 ENM |
803
+ | ENM exact recall | store.lookup_enm() | 100% accuracy |
804
+ | Score known triple | store.score_triple() | Low geodesic distance |
805
+ | Score random triple | store.score_triple() | High geodesic distance |
806
+ | Tension: contradictory pair | store.tension_energy() | E > 1.7 |
807
+ | Tension: consistent pair | store.tension_energy() | E < 0.8 |
808
+ | Holonomy: valid path | store.check_holonomy() | defect < 0.5 |
809
+ | Extract triples from LLM | extract_triples() | Parseable triples |
810
+ | Score extracted triples | score_triples() | Scores + entity matches |
811
+ | Verify consistent answer | verify_answer() | overall_consistent=True |
812
+ | Verify contradictory answer | verify_answer() | overall_consistent=False |
813
+ | Learn consistent triples | learn_triples() | accepted=True, store grows |
814
+ | Learn contradictory triples | learn_triples() | accepted=False, rollback |
815
+ | Multi-document ingest | Second ingest | Store expands |
816
+ | Store save/load roundtrip | save() + load() | Identical state |
817
+ | Compare on auto-questions | ComparisonRunner.run() | 3-column report |
818
+ | End-to-end: ingest PDF + query | Full pipeline | Answer with provenance |