Portfolio RAG Pipelines Real-Time Streaming Graph-RAG
Dynamic Data & Knowledge Graphs

Real-Time Streaming Graph-RAG

AI Pipeline Architecture Model

Back to Portfolio
Real-Time Streaming Graph-RAG Icon

A continuous ingestion RAG system connected to real-time data streams (e.g., live news feeds or system logs) that instantly updates an interconnected knowledge graph and vector space with sub-second latency.

🏗️
Engineering Value & Purpose
Addresses the 'data obsolescence' problem by synchronizing live event streaming with knowledge graph updates and real-time semantic retrieval.
Technology Stack
Apache Kafka Redpanda Neo4j Bytewax Dynamic Embeddings

System Architecture Blueprint

RAG-004: Real-Time Streaming Graph-RAG — Engineering Blueprint

This document details the system design, mathematical retrieval equations, and stream orchestration codes for RAG-004 (Real-Time Streaming Graph-RAG). It defines how to ingest, parse, embed, and query streaming signals with sub-second latencies using an integrated graph-vector space.

  1. System Specifications & Performance SLA

Target Ingestion Latency: $< 150\text{ms}$ end-to-end (from Redpanda partition publish to Neo4j Graph + Vector commit).

Retrieval Query SLA: $< 45\text{ms}$ total latency executing hybrid Graph + Vector traversals.

Stream Throughput Target: Stable execution up to $2,500$ messages/second per processor node.

Accuracy Target: Zero duplicate node/edge creations under concurrent streams (guaranteed via Neo4j transactional locks and unique schema constraints).

  1. Ingestion & Graph-Vector Topology

The ingestion path operates as a continuous, asynchronous dataflow. Instead of batch indexing files, raw streams of unstructured text (e.g., live RSS feeds, operational syslogs, system trace logs) are piped into a distributed commit log.

                       [ Raw Real-Time Event Stream ]
                                     │
                                     ▼
                        [ Redpanda / Apache Kafka ]
                                     │
                         (Continuous Event Pull)
                                     ▼
                 [ Bytewax Real-Time Dataflow Engine ]
                 • Non-blocking parallel parsing (NLP)
                 • Entity & Relation Extraction
                 • Async Vector Generation (cohere-embed-v3)
                                     │
                 ┌───────────────────┴───────────────────┐
                 ▼                                       ▼
       [ Write Entity Nodes ]                 [ Link Semantic Edges ]
                 │                                       │
                 └───────────────────┬───────────────────┘
                                     ▼
                       [ Neo4j Graph & Vector Store ]
  1. Mathematical Retrieval Formulation

Standard vector databases calculate similarity based purely on static semantic distance. In a real-time environment, retrieve relevance must incorporate both structural significance (graph connection density) and temporal freshness (decay factor).

3.1 Dynamic Temporal Decay

Let $n$ represent a node in the graph, and $t_n$ represent the epoch timestamp when the node was last updated. Given a query $Q$ at current time $t$, the time-decayed semantic similarity $S_{\text{semantic}}(Q, n, t)$ is modeled as:

$$S_{\text{semantic}}(Q, n, t) = \cos(\mathbf{e}_Q, \mathbf{e}_n) \cdot e^{-\gamma (t - t_n)}$$

Where:

$\mathbf{e}_Q$ and $\mathbf{e}_n$ are the normalized vector representations of the query and the node, respectively.

$\cos(\cdot)$ denotes the cosine similarity operator.

$\gamma > 0$ is the temporal decay constant. A larger $\gamma$ penalizes older documents faster.

3.2 Dynamic Centrality Weighting

To prevent isolated nodes from clouding the context space, we multiply the similarity score with the normalized PageRank metric or Degree Centrality metric $C(n)$ extracted from the graph topology:

$$C(n) = \frac{\text{deg}(n)}{\max_{v \in V} \text{deg}(v)}$$

The final hybrid routing score $S_{\text{hybrid}}(n, t)$ is defined as:

$$S_{\text{hybrid}}(n, t) = \lambda \cdot S_{\text{semantic}}(Q, n, t) + (1 - \lambda) \cdot C(n)$$

Where $\lambda \in [0.0, 1.0]$ is the hybrid balance coefficient:

$\lambda \to 1.0$ favors semantic textual matches.

$\lambda \to 0.0$ favors highly-connected topological hubs.

  1. Stream Processor Ingestion Pipeline (Bytewax)

The streaming worker is built with Bytewax, a stream processing framework that uses a highly efficient Rust backend to manage python generator states concurrently.

import os import datetime from typing import Dict, Any, Tuple, List import spacy from bytewax.dataflow import Dataflow import bytewax.operators as op from bytewax.connectors.kafka import KafkaSource from neo4j import GraphDatabase from openai import OpenAI

1. Initialize Clients and Local NLP parser

neo4j_uri = os.getenv("NEO4J_URI", "bolt://localhost:7687") neo4j_user = os.getenv("NEO4J_USER", "neo4j") neo4j_pass = os.getenv("NEO4J_PASSWORD", "password")

driver = GraphDatabase.driver(neo4j_uri, auth=(neo4j_user, neo4j_pass)) nlp = spacy.load("en_core_web_sm") openai_client = OpenAI()

2. Bytewax Stream Pipeline Configuration

flow = Dataflow("realtime-graphrag-pipeline")

Read continuous incoming events from Redpanda

source = KafkaSource( brokers=["localhost:9092"], topics=["live_news_feed"], starting_offset="latest" ) stream = op.input("kafka-in", flow, source)

def parse_and_extract_triples(event: Tuple[str, bytes]) -> List[Dict[str, Any]]: """ Parses unstructured text stream and extracts named entities and structural verbs. """ key, payload_bytes = event payload_str = payload_bytes.decode("utf-8")

# Process with local light-NLP parser
doc = nlp(payload_str)
triples = []

# Simple dependency parser heuristic for extraction
for token in doc:
    if token.dep_ == "ROOT":
        subject = [w for w in token.lefts if w.dep_ in ["nsubj", "nsubjpass"]]
        obj = [w for w in token.rights if w.dep_ in ["dobj", "pobj", "attr"]]

        if subject and obj:
            triples.append({
                "subject": subject[0].text,
                "predicate": token.text.upper(),
                "object": obj[0].text,
                "raw_text": payload_str,
                "timestamp": datetime.datetime.utcnow().timestamp()
            })
return triples

Map stream through the extractor step

parsed_triples = op.flat_map("nlp_triple_extractor", stream, parse_and_extract_triples)

def generate_embeddings_and_write_neo4j(triple: Dict[str, Any]): """ Asynchronously generates vector representations and performs transactional upserts. """ subject = triple["subject"] predicate = triple["predicate"] obj = triple["object"] raw_text = triple["raw_text"] ts = triple["timestamp"]

# Generate vector embedding for semantic matching
response = openai_client.embeddings.create(
    model="text-embedding-3-small",
    input=raw_text
)
vector = response.data[0].embedding

# Commit atomic upsert transactions to Neo4j
cypher_query = """
MERGE (s:Entity {name: $sub})
ON CREATE SET s.created_at = $ts, s.updated_at = $ts
ON MATCH SET s.updated_at = $ts

MERGE (o:Entity {name: $obj})
ON CREATE SET o.created_at = $ts, o.updated_at = $ts
ON MATCH SET o.updated_at = $ts

MERGE (s)-[r:RELATION {type: $pred}]->(o)
SET r.raw_text = $text, 
    r.embedding = $vec, 
    r.timestamp = $ts
"""

with driver.session() as session:
    session.run(
        cypher_query, 
        sub=subject, 
        obj=obj, 
        pred=predicate, 
        text=raw_text, 
        vec=vector, 
        ts=ts
    )

Execute the sink operation concurrently

op.inspect("write_sink", parsed_triples, generate_embeddings_and_write_neo4j)

  1. Sub-Second Hybrid Query Execution (Cypher)

To retrieve real-time context, queries execute a vector search inside Neo4j's GenAI vector index first, then traverse the graph space to pull adjacent nodes, calculating the time decay factor dynamically.

Cypher Vector Search + Graph Traversal Query

// Query the integrated vector index for semantic starting points CALL db.index.vector.queryNodes('entity_vector_index', 5, $query_vector) YIELD node AS anchor, score AS vector_score

// Traverse adjacent relationships up to 2 hops away to pull structural context MATCH (anchor)-[r:RELATION]-(neighbor:Entity) WITH anchor, r, neighbor, vector_score, datetime().epochSeconds AS current_time

// Calculate Temporal Decay Score: Vector_score * e^(-gamma * delta_t) WITH anchor, r, neighbor, vector_score, (current_time - r.timestamp) AS delta_t, 0.0001 AS gamma // Decay penalty factor

WITH anchor, r, neighbor, vector_score * exp(-gamma * delta_t) AS decayed_score, neighbor.name AS neighbor_name

// Calculate simple node degree centrality dynamically WITH anchor, r, neighbor_name, decayed_score, count( (anchor)-[]-() ) AS node_degree

// Combine metrics using the hybrid balance coefficient (lambda = 0.70) WITH neighbor_name, decayed_score, node_degree, (0.70 * decayed_score) + (0.30 * (node_degree / 100.0)) AS hybrid_relevance

RETURN neighbor_name, hybrid_relevance ORDER BY hybrid_relevance DESC LIMIT 5;

  1. Verification & Validation Protocols

6.1 Transactional Integrity & Concurrency Test

We write testing sequences to assert that rapid concurrent publishes of duplicate records are handled gracefully using transactional locks, avoiding duplicate edge creation.

import threading import pytest

def test_concurrent_write_integrity(): # Simulate high concurrent stream updates pointing to identical nodes def run_worker(): test_payload = { "subject": "Bitcoin", "predicate": "HIT", "object": "AllTimeHigh", "raw_text": "Bitcoin hit an all time high today.", "timestamp": datetime.datetime.utcnow().timestamp() } generate_embeddings_and_write_neo4j(test_payload)

threads = [threading.Thread(target=run_worker) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

# Validate that only 2 distinct Entity nodes and 1 relation edge exist
with driver.session() as session:
    nodes = session.run("MATCH (e:Entity) RETURN count(e) AS count").single()["count"]
    edges = session.run("MATCH ()-[r:RELATION]->() RETURN count(r) AS count").single()["count"]

assert nodes == 2
assert edges == 1

6.2 Stream Processing Performance Benchmark

To verify sub-second performance limits, an automated benchmark test parses and measures processing timestamps:

def test_pipeline_ingestion_latency(): import time start_time = time.time()

# Inject 100 test event strings into Redpanda mockup
inject_mock_records(count=100)

# Wait for the database indexes to sync
time.sleep(1.0)

# Fetch the latest committed timestamp in Neo4j and compute delta
with driver.session() as session:
    last_commit_ts = session.run("MATCH ()-[r:RELATION]->() RETURN max(r.timestamp) AS ts").single()["ts"]

end_time = time.time()
total_delta = end_time - last_commit_ts

# Assert end-to-end commit latency is below 150ms per batch
assert total_delta < 0.150

Required Technical Skills

Advanced Engineering Skills Inventory: Real-Time Streaming Graph-RAG (RAG-004)

This matrix details the advanced systems engineering, stream data processing, database topology, caching logic, and frontend visual rendering competencies required to construct and deploy low-latency, dynamic Graph-RAG pipelines.

  1. High-Throughput Event Streaming & Broker Topologies

Ingesting transactional information in real-time requires structuring resilient, multi-tenant distributed message queues that prevent pipeline bottlenecks.

                [ Real-Time Stream Event ]
                            │
                [ Kafka / Redpanda Broker ]
                 • Partition Keying: `tenant_id`
                 • Binary Record Headers
                 • Dynamic Multi-Topic Routing

Redpanda / Apache Kafka Architecture:

Setting up multi-node Redpanda clusters using containerized orchestrations, configuring retention policies, partition allocations, and replica limits.

Constructing clean partitioning strategies (e.g., using tenant_id as the message key to guarantee ordered record offsets for each tenant).

Structuring binary message payloads with custom record metadata headers (such as timestamp, correlation_id, and tenant_uuid) to isolate stream processing context at the ingestion boundary.

Stream Security Protocols:

Implementing SASL/SSL authentication mechanics to secure message transport interfaces.

Writing Kafka Interceptors to dynamically sanitize or validate tokens inside record headers before payload consumption.

  1. Python-Rust Stream Orchestration & Flow Control

Continuous streaming pipelines require backpressure handling and smart micro-batching to protect downstream database locks.

Bytewax Streaming Engine:

Understanding the core execution of the Timely Dataflow engine (Bytewax's Rust-based backend framework) for high-concurrency stateful processing.

Implementing stateful stream transformations using operators like map, flat_map, fold_window, and stateful_map to extract entity-relation triples from unstructured text.

Reactive Backpressure & Flow Tuning:

Monitoring active streaming parameters (such as partition commit lag, CPU utilization, and worker thread blocks).

Writing custom flow controllers to dynamically slow down consumer offset commits if downstream database transactions (e.g., heavy write operations in Neo4j) exceed latency thresholds.

Micro-Batch Transaction Optimization:

Aggregating streaming records into temporal slides (e.g., 50ms windows) or volume batches (e.g., 100 rows) to minimize Neo4j transaction write overhead:

$$\text{Batching Ratio} = \frac{\text{Transactions}{\text{naive}}}{\text{Transactions}{\text{batched}}} \ge 100\times$$

  1. Dynamic Hybrid Graph-Vector Database Design

Representing real-time streaming data requires updating a unified graph-vector space with sub-second commit latency without corrupting structural indices.

                            [ Write Worker ]
                                   │
                ┌──────────────────┴──────────────────┐
                ▼                                     ▼
    [ Dense Vector Index ]                 [ Directed Graph Schema ]
     • Neo4j Vector Indexes                 • Relational Node Linking
     • Cohere Embeddings                    • Strict Unique Constraints

Neo4j Cypher Optimization & Constraints:

Defining strict multi-tenant schema constraints (e.g., CREATE CONSTRAINT FOR (e:Entity) REQUIRE e.tenant_id IS NOT NULL) to ensure logical data boundary enforcement.

Writing transactional writes utilizing APOC procedures and MERGE clauses to prevent graph duplication or race conditions under high concurrency.

Hybrid Dynamic Retrieval Queries:

Fusing dense vector searches (using Neo4j's native GenAI indices) with real-time graph traversals (using Cypher) in a single unified execution thread.

Computing temporal decay calculations inside Cypher directly using node properties to dynamically prioritize fresh connections:

$$S_{\text{temporal}} = S_{\text{vector}} \cdot e^{-\gamma(t - t_n)}$$

Cluster Infrastructure (Causal Clustering):

Deploying highly available Neo4j Enterprise clusters with isolated physical databases mapped to different client accounts.

Writing database session routers that decouple write batches (processed exclusively on Primary nodes) from client query workloads (served via Read Replicas).

  1. L2 Semantic Caching & Token Optimization

Generating vector embeddings on raw stream feeds ($2,500\text{ messages/sec}$) is financially unsustainable. You must know how to construct high-speed local filtering networks.

Exact Deduplication (SHA-256):

Implementing lightweight, sub-millisecond key-value lookups (using Redis Hashes) to bypass semantic operations completely if the cryptographic footprint of an incoming payload matches an already processed string.

Vector Cosine Similarity Filtering:

Implementing in-memory vector collections (using Redis Vector Space or HNSW search) to evaluate fresh inputs against recently generated vectors.

Setting semantic similarity thresholds ($\ge 0.95$ similarity) to recycle pre-computed embeddings for incoming text streams, decreasing API cost overheads by over 80%.

  1. Next.js SSE & Force-Directed Canvas Rendering

Displaying real-time database updates requires consuming event streams efficiently and animating graph updates without freezing the browser interface.

[ Server-Sent Events (SSE) ] ──► [ Next.js Event Hook ] ──► [ HTML5 Canvas 2D ] • Render nodes dynamically • Recalculate forces smoothly

Server-Sent Events (SSE) Pipelines:

Building asynchronous generators in Python/FastAPI using chunk-by-chunk serialization protocols to push new node-edge relations directly to browser endpoints.

Consuming HTTP data streams in React using native browser EventSource interfaces, implementing memory-safe cleanup hooks to prevent event thread leaks on component unmount.

High-Performance Canvas Execution:

Using react-force-graph or the native WebGL/Canvas 2D context API to render dynamic graphs containing hundreds of active nodes.

Customizing node layout updates (using physics solvers) to limit calculation costs during live render ticks, maintaining a smooth $60\text{ FPS}$ UI experience.

🚀 Quick Installation & Setup Guide

To run this project locally, follow these simple steps designed for both developers and users with no prior programming experience.

  1. Download Python: Make sure you have Python 3.10+ installed on your computer. Download it from the official Python Website. During installation, make sure to check the box "Add Python to PATH".
  2. Extract / Setup Code: Open the terminal (Command Prompt on Windows) and navigate to the project directory, or download/extract the project files.
  3. Install Dependencies (pip): In Command Prompt, run the following command to install all necessary packages automatically:
    pip install -r requirements.txt
  4. Setup API Keys / Environment: Open the .env file in a text editor (like Notepad) and paste your API keys:
    GEMINI_API_KEY=your_gemini_api_key_here
  5. Launch the Application: Double-click the run_project.bat or run_pito.bat file, or run the following command:
    python app.py
    (or app_gui.py)