Standard flat-vector RAG struggles when documents contain deeply interconnected entities. If you split a 50-page enterprise agreement, automotive catalog, warranty register, or CRM database into arbitrary 512-token chunks, the relational topology is broken. Vector similarity cannot easily perform a 4-hop walk: finding that a specific defective part was supplied by a Tier-1 vendor, fitted into a specific vehicle variant, sold through an authorized dealership, and replaced under a specific warranty claim.
To solve this, I designed and built an event-sourced Knowledge Compiler: a pipeline that processes raw unstructured documents, CSVs, JSON registries, and Markdown notes into a live 98,921-node context graph in Neo4j with explicit provenance tracking and deterministic graph traversal.
1. The 6-Stage Compilation Pipeline
Instead of relying on free-form prompting that produces disconnected entities, the compiler enforces a structured 6-stage lifecycle:
GraphPatch records containing asserted_at, valid_from, and supersedes references. Previous facts are marked superseded rather than destructively deleted.
UNWIND transactions backed by Neo4j schema indexes.
# Conceptual: Bitemporal Mutation & Non-Destructive Superseding
function apply_assertion(graph, subject, predicate, object, document_source):
existing_fact = graph.find_active_edge(subject, predicate)
if existing_fact and existing_fact.target != object:
# Supersede previous assertion without destructive deletion
existing_fact.status = "superseded"
existing_fact.valid_to = current_timestamp()
# Insert new assertion with immutable provenance metadata
graph.insert_edge(
source = subject,
relation = predicate,
target = object,
status = "current",
asserted_at = current_timestamp(),
source_doc = document_source
)
2. Visualizing Scale: 500 to 100,000 Nodes
Visualizing massive graphs in a client browser quickly runs into performance bottlenecks. Running real-time force-directed physics simulations on 100,000 DOM elements easily exhausts browser memory and locks up the rendering loop.
[:MANUFACTURES], [:SUPPLIES_PART], [:STOCKS_INVENTORY], [:CLAIM_ON_VEHICLE]) between OEMs, Dealerships, Parts, and Warranty records.To make the UI responsive across scales, I added a multi-density rendering mode:
- 500 Nodes (Sample): Uses live spring-physics simulation for local neighborhood inspection.
- 5,000 to 25,000 Nodes: Connected multi-hop cluster graphs with lightweight physics.
- 100,000 Nodes (Universe): Coordinates are precalculated server-side using concentric radial orbits grouped by domain label. Vis.js physics is disabled, allowing the point cloud and 43,000+ translucent cyber edges to render smoothly at 60 FPS.
3. FastRP Topological Deduplication
Enterprise text often refers to the same real-world entity with slight variations—such as "Bombay Plant" versus "Mumbai Manufacturing Unit" or "GM" versus "General Manager". Calling an LLM to compare every pair of 100k entities is prohibitively slow and expensive.
To address this, I used Fast Random Projection (FastRP) graph embeddings. FastRP embeds node neighborhoods into 128-dimensional vectors based on graph topology. Candidate duplicates are identified using cosine similarity over structural neighbors and merged via atomic graph patches:
# Conceptual: Topological Graph Deduplication
function deduplicate_graph_entities(graph, similarity_threshold = 0.92):
# 1. Project graph neighborhood into low-dimensional vector space
embeddings = compute_fast_random_projection(graph.nodes, graph.edges, dim=128)
# 2. Find candidate alias pairs via neighborhood cosine similarity
candidate_pairs = find_high_similarity_pairs(embeddings, similarity_threshold)
# 3. Validate schema compatibility and merge aliases
for (entity_a, entity_b) in candidate_pairs:
if validate_schema_compatibility(entity_a, entity_b):
graph.merge_nodes(canonical = entity_a, alias = entity_b)
4. Grounded GraphRAG Agent & Traversal
When an end user asks complex multi-hop questions, the ReAct GraphRAG Agent does not guess. It executes targeted queries against the graph and traverses relationship pointers.
Agent Execution Trace:
1.
search_nodes("LEAD-000010") → Customer Lead Nisha Verma2. Follow
[:INQUIRED_ABOUT_MODEL] → Tata Harrier Adventure+ Diesel Automatic3. Follow
[:MANUFACTURES] → Tata Motors Passenger Vehicles Limited (org_tata)4. Follow
[:ASSIGNED_TO_DEALER] → Apex Automotive Madurai East (dealer_dl_mad_04)Provenance: The response explicitly cites source assertions (
assert_org_tata...) from leads.csv, car_catalog.csv, and dealership_master_network.md.
Handling Missing Data: A common failure mode in LLM RAG is inventing plausibly-sounding answers when an entity does not exist. If a user asks about LEAD-999999 or a non-existent dealership code, the graph lookup returns empty, and the agent explicitly reports that no assertion exists rather than guessing.
5. Comparison: Vector RAG vs Naive Graph vs Bitemporal Compiler
| Dimension | Flat Vector RAG | Naive LLM Extraction | Bitemporal Knowledge Compiler |
|---|---|---|---|
| Multi-Hop Traversal | ❌ Lost across chunk boundaries | 🟡 Fragile dangling IDs | ✅ Indexed pointer hops |
| Temporal State & Auditing | ❌ No bitemporal validity | ❌ Destructive overwrites | ✅ Immutable assertion IDs |
| Entity Disambiguation | ❌ Embedding collision | ❌ Entity duplication sprawl | ✅ FastRP structural deduplication |
| Missing Entity Handling | ❌ Frequent hallucination | 🟡 Inconsistent grounding | ✅ Explicit assertion check |
| 100k Node Scale | 🟡 High vector search latency | ❌ Unindexed query bottlenecks | ✅ B-Tree indexed batch UNWIND |
Closing Takeaway
In complex domains like automotive supply chains, warranties, and multi-tier networks, relational topology matters as much as text semantics. Treating graph construction as a structured compiler problem—with deterministic AST chunking, bitemporal patches, and graph embeddings—gives LLM agents the precise, auditable ground truth they need to answer multi-hop questions reliably.