Problem
Standard vector RAG fails on questions that need global understanding or multi-hop logic: “which vendors mentioned in Q3 contracts are affected by the compliance update in Doc B?” No single chunk contains the answer. The answer is a path through entities across documents.
System
An LLM extractor (Claude, with schema-validated structured outputs) turns documents into entities and typed relations; a heuristic extractor stands in for offline use. Entities and relations land in Neo4j, chunks and embeddings in Qdrant. Retrieval runs dense cosine search over chunks and k-hop expansion from query-matched entities, then fuses both lists with weighted reciprocal rank fusion. Seeded hierarchical Louvain detects communities over the entity graph, stored as nodes for summary-level questions.
Worth knowing
- Injection-safe Cypher. All values are parameterized, identifiers are whitelisted, and LLM-extracted relation types are stored as properties on a fixed relationship type rather than interpolated into queries. An extractor that can be steered by document content should never get to write query syntax.
- Every backend has an in-memory implementation. Graph store, vector store, embedder, extractor. The whole test suite and the end-to-end multi-hop demo run with no Docker and no API key, which is also what makes the system cheap to evaluate before committing to the infrastructure.