A graph-enhanced RAG system is only useful if retrieval actually follows the graph, and the latest installment in Marko Čekić’s persistent knowledge-layer project makes that change explicit: it removes a question-wording router, runs Azure AI Search and bounded graph traversal on every request, then reranks the combined evidence before an LLM writes an answer.

The design, published by Towards Data Science and backed by Čekić’s public knowledge-graph-fusion implementation, is aimed at a failure common to enterprise RAG deployments. Teams extract entities and relationships, place them in a graph database, and then still retrieve context as isolated vector-search chunks. The graph becomes an audit visualization or a manually browsed wiki rather than part of the answer path.

Čekić’s change is material for Azure developers because it separates three concerns that are frequently bundled into an opaque “agentic RAG” layer: retrieval, temporal applicability, and safety policy. The project uses Azure AI Search for hybrid lexical and vector retrieval, Cosmos DB for Apache Gremlin for relationship expansion, Cosmos DB for NoSQL as the authoritative record, and a Cosmos change-feed-driven Azure Function to update derived search and graph projections.

The important finding is more restrained than the architecture’s ambition. On the author’s initial eight-question evaluation set, always-fused retrieval did not improve recall over search alone: both reached 0.75 recall for the expected items. What traversal added was an average of 10 typed, time-valid relationship paths per grounded bundle, where the search-only baseline returned none. That makes this a useful implementation report, not proof that a graph database improves retrieval quality at production scale.

A dark blue infographic illustrates a hybrid search index, knowledge graph, data sources, and ranked results.Replacing routing with a fixed retrieval path​

Part 1 of the project used a router that selected “wiki,” “evidence,” or hybrid retrieval modes based on markers in the user’s wording. A request containing terms such as “compare” or “why” might trigger synthesis-oriented retrieval, while a request asking for an “exact” quotation might prioritize source evidence.

That approach saves calls when it works, but it also makes system behavior depend on the phrasing of a request. Two users seeking the same policy fact can enter different retrieval paths and receive context with different grounding characteristics. The author’s answer is to eliminate the caller-visible choice: hybrid search and graph traversal run for every request, their objects are merged, and one reranker evaluates the union.

Microsoft’s current Azure AI Search documentation supports part of that reasoning. Hybrid search already executes lexical and vector queries in parallel and uses Reciprocal Rank Fusion, or RRF, to combine ranked lists whose scores are produced by different algorithms. Raw BM25, vector-similarity, and RRF values should not be treated as interchangeable confidence scores.

But Azure AI Search’s built-in semantic ranker can only rerank documents returned through its own search operation. It cannot natively evaluate a hand-assembled list containing objects pulled exclusively from a Gremlin traversal. That forces the design to use an external reranking stage, implemented with a Microsoft Foundry-hosted model, after search hits and graph-connected objects have been combined.

That is a sensible technical boundary, although it adds a model-token cost and another observable point of failure. A production implementation needs to log which candidates entered from search, which arrived from traversal, how many the reranker discarded, and whether graph-derived objects actually change the final evidence bundle. Without those traces, “fusion” risks becoming a more expensive black box than the keyword router it replaces.


The graph earns its place only through paths​

The project’s core claim is not that every knowledge base needs a graph database. It is that a graph engine earns its operational cost when questions need relational expansion that a search service cannot perform.

A hybrid search hit includes canonical entity identifiers for entities mentioned in the returned chunk. Those identifiers become graph anchors. The system then follows a bounded set of typed edges—one or two hops rather than unconstrained exploration—to collect connected objects and the paths that join them.

For an enterprise policy or operational corpus, this can make a real difference. A query about why a claim received a particular triage status may require a claim note, a triage rule, an exception, and a policy clause. Vector search can retrieve some or all of those documents, but it cannot prove that the retrieved pieces form a documented chain. A graph traversal can return the claim’s relationship to the triage category and the category’s relationship to the governing rule.

The author’s implementation intentionally caps traversal depth and filters eligible edge types. That is not a minor optimization. Cosmos DB for Apache Gremlin charges request units based on the work a traversal performs, and high-fan-out traversals can become unpredictable quickly. A “find everything connected to X” feature is operationally safe only if the graph schema and query API prevent every broad edge from becoming an accidental expansion path.

The current public repository also shows an important evolution beyond the published Part 2 account: it now includes a bounded agent interface alongside the fixed /query pipeline. The repository describes the agent as an escalation path rather than a default, reporting that it performs better on multi-hop tasks and worse on single-hop tasks. That is a more credible position than treating an agent loop as an automatic upgrade over deterministic retrieval.

Time belongs on relationships, not only documents​

The strongest design change is the move from document-level effective dates to bitemporal graph edges. Every relationship stores when it was valid in the represented domain and when the system learned about it.

In practical terms, this means an older relationship is expired instead of overwritten when a policy rule changes. The graph can then answer three different questions that are routinely conflated:

  • An as-of question asks which relationship or rule was valid on a particular date.
  • A timeline query asks how a relationship evolved across several effective dates.
  • A diff query asks which edges opened, closed, or changed during a specified period.

This matters for IT documentation just as much as the synthetic insurance corpus used in the demonstration. A system managing Windows support standards, endpoint configuration baselines, software approval policies, or incident procedures needs to distinguish between “what is current now” and “what was approved when this device was enrolled or this incident occurred.”

Čekić identifies a flaw in his own migration logic that is worth more attention than it receives. When converting pre-existing curated edges into bitemporal edges, the system initially used the earliest supporting-source date as an edge’s valid_from value. That produces the wrong date for some scoped supersession relationships: a document can establish the background rule long before a later update actually narrows its scope.

The lesson is straightforward: validity reconstruction cannot use one generic date heuristic. Relationship types need their own temporal semantics. A supersedes edge, a narrows_rule_for edge, and an applies_to edge do not derive their effective dates from source history in the same way.


Contradiction detection is only as good as entity resolution​

The project also moves contradiction detection to ingestion. When a new, currently valid edge is extracted, the pipeline checks for incompatible current edges attached to the same subject. A clear replacement from the same authority can expire an older edge automatically. A conflict between active statements from different authorities becomes an unresolved contradiction record, with both claims preserved and a responsible owner identified.

The public implementation and the Towards Data Science report describe an automatically detected conflict over whether trace-and-access costs were standard coverage or an optional paid endorsement. The author says the live ingest pipeline created a contradiction object without manual curation.

That result is encouraging, but it also exposes the harder problem. The detected contradiction landed on a model-generated provisional entity, trace-and-access-cost-recovery, instead of the manually curated canonical concept. The conflict was detected because both statements resolved to the same provisional node, but the automatically detected record and the original curated conflict still require a later merge.

This is why entity resolution is the actual load-bearing subsystem. If “ACV,” “actual cash value,” “cash settlement basis,” and “depreciated value” become separate entities, a graph does not recover missing links—it formalizes the fragmentation. If unrelated concepts are merged, paths and contradiction checks become misleading.

The author reports that alias-only resolution created 149 extracted concepts from a corpus containing 19 curated concepts. Embedding-based blocking, thresholds for automatic match or rejection, and LLM adjudication for ambiguous cases reduced that result to 120 concepts. That is a 20 percent improvement, but still more than six times the curated canonical total.

Readers should interpret that number correctly. It demonstrates that the revised resolver changes the outcome. It does not show that entity resolution is solved, nor that the resulting graph is accurate enough to support automated policy decisions. Before applying this model to a real enterprise corpus, teams need a labeled entity-resolution benchmark with precision and recall measures, sampled false merges, and explicit remediation procedures for reversible same_as links.

Cosmos DB’s change feed avoids a dual-write trap​

The Azure implementation makes Cosmos DB for NoSQL the durable system of record. Search indexes and the Gremlin graph are derived projections, updated by an Azure Functions worker consuming the NoSQL change feed.

That arrangement prevents a familiar multi-store failure: application code independently writes to a source database, a search index, and a graph, then spends the rest of its life repairing inconsistencies among them. Microsoft documents Azure Functions’ Cosmos DB trigger as a way to react to changes in a NoSQL container without managing change-feed worker infrastructure directly.

There is a limitation the design must retain in its operational documentation: the Azure Functions Cosmos DB trigger applies to the API for NoSQL. It does not turn a Gremlin account into the event source. In this architecture, that is appropriate because NoSQL is the authoritative write store and Gremlin is a projection target. It also means the projection worker must be idempotent, must tolerate repeated deliveries, and must have a tested rebuild procedure for both derived stores.

The project has the right high-level shape: write once to the record, project outward, and rebuild projections when necessary. Its next credibility test is larger-scale measurement. The published recall tie—0.75 with fusion and 0.75 without it—means the graph has not yet demonstrated improved retrieval coverage on the 21-document corpus.

For now, the graph’s measurable value is grounding structure: it gives the model documented, time-aware paths instead of asking it to infer connections among similarly ranked chunks. That is worthwhile for audits, incident review, and policy explanation. Whether it improves answer retrieval at enterprise corpus scale remains an open engineering result, not a conclusion the initial demo has earned.