Dhruvpandey1476's picture
GraphRAG Hackathon Round 2 Submission - 54.7% token reduction
cb4c15b
|
Raw History Blame Contribute Delete
11.5 kB
# Architecture Deep Dive β€” GraphRAG Inference System
## System Overview
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ GraphRAG Inference System β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ React Dashboard (Port 5173) β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ Query Input β”‚ β”‚ Token BarChart β”‚ β”‚ Accuracy RadarChart β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ Sample Q's β”‚ β”‚ (Recharts) β”‚ β”‚ (LLM-as-Judge) β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ POST /query/compare β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ FastAPI Server (Port 8000) β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ Pipeline 1 β”‚ β”‚ Pipeline 2 β”‚ β”‚ Pipeline 3 β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ LLM Only β”‚ β”‚ Basic RAG β”‚ β”‚ GraphRAG β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ sys promptβ”‚ β”‚ β€’ embed query β”‚ β”‚ β€’ extract entities β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ question β”‚ β”‚ β€’ FAISS search β”‚ β”‚ β€’ TG traversal β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β€’ LLM call β”‚ β”‚ β€’ top-K chunks β”‚ β”‚ β€’ serialize graph β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β€’ LLM call β”‚ β”‚ β€’ LLM call β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚ β”‚ β”‚ LLM-as-Judge β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ + Metrics Agg β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ FAISS Vector DB β”‚ β”‚ TigerGraph Savanna β”‚
β”‚ (in-memory/pkl) β”‚ β”‚ Knowledge Graph β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ chunks[] β”‚ β”‚ Entity vertices β”‚
β”‚ embeddings[] β”‚ β”‚ RELATED_TO edges β”‚
β”‚ IndexFlatIP β”‚ β”‚ MENTIONED_IN edges β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ OpenAI API β”‚
β”‚ (LLM + Embeddings) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
---
## Component Details
### 1. Ingestion Pipeline (`backend/graph/ingestion.py`)
Converts raw documents into the knowledge graph:
```
Raw Docs (.txt/.md)
β”‚
β–Ό chunk_text() β€” token-aware, 512 tokens + 64 overlap
Document Chunks
β”‚
β–Ό extract_entities_and_relations() β€” GPT-4o-mini JSON extraction
Entities + Relationships
β”‚
β”œβ”€β–Ά get_embeddings_batch() β€” text-embedding-3-small
β”‚ β”‚
β”‚ β–Ό
β”‚ Entity embeddings (for future vector-on-graph hybrid)
β”‚
β”œβ”€β–Ά tg.upsert_entity() β€” Entity vertex in TigerGraph
β”œβ”€β–Ά tg.upsert_document() β€” Document vertex in TigerGraph
β”œβ”€β–Ά tg.upsert_relationship() β€” RELATED_TO edge
└─▢ tg.link_entity_to_document() β€” MENTIONED_IN edge
```
Simultaneously, all raw chunks are added to FAISS for Pipeline 2.
### 2. Basic RAG Pipeline (`backend/rag/basic_rag.py`)
```
User Query
β”‚
β–Ό openai.embeddings.create() β†’ 1536-dim vector
Query Vector
β”‚
β–Ό faiss.IndexFlatIP.search() β€” cosine similarity
Top-K Chunk Indices (K=5)
β”‚
β–Ό Concatenate chunk texts
Context String (~3,000 tokens)
β”‚
β–Ό openai.chat.completions.create()
Answer + Token Usage
```
Token overhead: system prompt (~50) + context (~3,000) + question (~50) = ~3,100 prompt tokens
### 3. GraphRAG Pipeline (`backend/rag/graph_rag.py`)
```
User Query
β”‚
β–Ό extract_query_entities() β€” LLM β†’ ["Entity A", "Entity B"]
Seed Entities
β”‚
β–Ό tg.get_entity_subgraph() β€” GSQL multi-hop traversal
Subgraph {entities[], relationships[], documents[]}
β”‚
β–Ό serialize_subgraph() β€” compact structured text
Graph Context (~400-800 tokens)
β”‚
β–Ό openai.chat.completions.create()
Answer + Token Usage
```
Token overhead: system prompt (~60) + graph context (~600) + question (~50) = ~710 prompt tokens
**Token reduction: ~77%**
### 4. LLM-as-a-Judge (`backend/llm/judge.py`)
```
Question + Answer + Ground Truth
β”‚
β–Ό GPT-4o-mini with JSON response format
{
"score": 8,
"correctness": 9,
"completeness": 7,
"clarity": 8,
"relevance": 8,
"reasoning": "..."
}
```
Run for both Basic RAG and GraphRAG answers. Used for per-query comparison and aggregate pass rate.
### 5. BERTScore Evaluation (`evaluation/benchmark.py`)
```python
from bert_score import score as bert_score_fn
P, R, F1 = bert_score_fn(
predictions=[graph_answers],
references=[ground_truths],
lang="en",
model_type="distilbert-base-uncased"
)
```
Target: F1 rescaled β‰₯ 0.55 (raw β‰₯ 0.88) for bonus points.
---
## Knowledge Graph Schema
### Vertices
| Type | Key Attributes | Purpose |
|------|---------------|---------|
| Entity | name, entity_type, description, embedding | Core knowledge nodes |
| Document | title, content, chunk_index, token_count | Source text chunks |
| Concept | name, category, importance_score | High-level topics |
### Edges
| Type | Direction | Attributes | Purpose |
|------|-----------|-----------|---------|
| RELATED_TO | Entity ↔ Entity | relation_type, confidence, context | Core relationships |
| MENTIONED_IN | Entity β†’ Document | frequency, relevance_score | Entity-source links |
| HAS_CONCEPT | Document β†’ Concept | weight | Topic tagging |
| CO_OCCURS_WITH | Entity ↔ Entity | co_occurrence_count | Statistical co-occurrence |
---
## Subgraph Serialization Format
The key to token efficiency is our compact serialization format:
```
## ENTITIES
β€’ [Entity Name] [TYPE]: description text
## RELATIONSHIPS
β€’ Entity A β€”[RELATION_TYPE]β†’ Entity B (conf: 0.95) | context snippet
## SUPPORTING TEXT (top-1 doc, max 300 chars)
[Document Title]: first 300 characters of relevant chunk...
```
This format is:
- Hierarchically structured (LLM can parse with zero instruction)
- Non-redundant (each fact stated once)
- Confidence-weighted (LLM can trust high-confidence facts more)
- Bounded (caps prevent runaway token usage)
---
## API Endpoints
| Method | Path | Description |
|--------|------|-------------|
| GET | /health | Server health + TG connection status |
| POST | /query/compare | Run all 3 pipelines, return full metrics |
| GET | /stats/session | Cumulative session token savings |
| GET | /graph/stats | TigerGraph vertex/edge counts |
| POST | /ingest/text | Quick-ingest a document |
---
## Performance Characteristics
| Operation | Typical Latency |
|-----------|----------------|
| Query embedding (OpenAI) | 150–300ms |
| FAISS search (100K chunks) | <10ms |
| Entity extraction (LLM) | 300–600ms |
| TigerGraph GSQL traversal | 50–200ms |
| LLM generation (gpt-4o-mini) | 800–2000ms |
| LLM Judge evaluation | 300–600ms |
| **Total: Basic RAG** | **~1,500–2,500ms** |
| **Total: GraphRAG** | **~1,000–1,800ms** |
GraphRAG's latency advantage comes from:
1. No FAISS search needed (graph traversal replaces it)
2. Smaller context = faster LLM generation
3. No chunk deduplication step needed
---
## Scaling Considerations (Round 2)
For 50-100M token datasets:
- TigerGraph Savanna handles billions of edges natively β€” no schema changes needed
- FAISS upgrades to `IndexIVFFlat` or `HNSW` for ANN search at scale
- Ingestion parallelized with async batch processing
- Entity extraction cached β€” same entities aren't re-extracted across document runs
- Community detection (TigerGraph built-in) adds global context layer for broad queries