Architecture & Implementation Guide v2.0

Insurance Knowledge Graph
RAG Pipeline

A comprehensive, ontology-driven architecture for 100K+ insurance and wealth planning PDFs — combining vector search, SPARQL graph queries, and agentic orchestration with full source provenance.

01 / SYSTEM OVERVIEW

End-to-End Architecture

The system transforms raw insurance PDFs through a multi-stage pipeline into a queryable knowledge layer that combines vector similarity, graph traversal, and LLM synthesis with full regulatory-grade provenance.

High-Level System Architecture
PDF Ingestion 100K+ Documents Document Processing pdfplumber extraction Chunking + BBox ID Assignment Vector Store Pinecone / pgvector Embeddings + Metadata Graph Store GraphDB / Neptune TTL + FIBO Ontology Metadata Store PostgreSQL + JSONB Provenance + Clusters Query Orchestrator LLM Classifier DatabaseRouter Provenance Tracker LangGraph Agent User Query Natural Language LLM Synthesis Claude / GPT-4 Cited Answer + PDF Pages Redis Cache Query Results Vector Path Graph Path Orchestrator LLM / Synthesis

Core Design Principles

The architecture follows a three-tier storage hierarchy (raw → processed → semantic) with bidirectional traceability. Every chunk knows its parent page and PDF. Every PDF knows all its chunks and its TTL representation. This means when a document is updated, the system invalidates only the affected TTL files and re-embeds only the changed chunks — avoiding a full reprocessing cycle across all 100K documents.

Why Hybrid Retrieval?

Pure vector RAG systems offer fast fuzzy matching but can return semantically similar yet factually contradictory chunks. Pure graph queries are precise but brittle — they break when terminology varies. The orchestrator intelligently routes each query to the optimal retrieval pattern, combining broad vector recall with the structural precision of SPARQL graph traversal.

02 / STORAGE ORGANIZATION

Hierarchical ID System & File Layout

A deterministic naming convention that lets you reconstruct full paths from any chunk ID — critical when returning search results with regulatory-grade source citations.

Hierarchical Identifier System
LIFE_TERM_PRUDENTIAL_2024_00127 PDF Document Identifier LIFE Category TERM Subcategory PRUDENTIAL Organization 2024 Year 00127 Sequence LIFE_TERM_PRUDENTIAL_2024_00127_P015 Page 15 of the document LIFE_TERM_PRUDENTIAL_2024_00127_C042 Chunk 42 of the document life_term_prudential_2024_00127.ttl

Directory Structure

project-root/
## ── Tier 1: Raw Storage (Immutable, S3 or equivalent) ── raw/ life_insurance/ term/ LIFE_TERM_PRUDENTIAL_2024_00127.pdf LIFE_TERM_HDFC_2024_00045.pdf whole/ ulip/ health_insurance/ wealth_planning/ ## ── Tier 2: Processed Intermediates (Versioned) ── processed/ pages/ LIFE_TERM_PRUDENTIAL_2024_00127_P001.json # text + bbox coords LIFE_TERM_PRUDENTIAL_2024_00127_P002.json chunks/ LIFE_TERM_PRUDENTIAL_2024_00127_C001.json # text + embedding + metadata LIFE_TERM_PRUDENTIAL_2024_00127_C002.json ## ── Tier 3: Knowledge Graphs (Semantic Repository) ── knowledge/ individual/ life_term_prudential_2024_00127.ttl # per-PDF precision updates clusters/ life_term_prudential_2023_2024.ttl # semantic cluster for fast queries health_group_medical_plans.ttl manifests/ cluster_manifest.json # tracks cluster → PDF membership

Chunk Metadata Schema

Every chunk carries three critical pieces of metadata that enable full provenance tracing from the final answer back to the exact PDF page and paragraph:

chunk_metadata.json
{ "chunk_id": "LIFE_TERM_PRUDENTIAL_2024_00127_C042", "pdf_id": "LIFE_TERM_PRUDENTIAL_2024_00127", "page_number": 15, // Actual user-visible page number "chunk_sequence": 3, // 3rd chunk on this page "text": "Critical illness coverage includes...", "bbox": { // Bounding box for highlight overlay "x": 72.5, "y": 340.2, "width": 467.1, "height": 89.6 }, "embedding": [0.0234, -0.0891, ...], // 1536-dim vector "ttl_entity_uri": "ex:Clause_CriticalIllness_C042", "cluster_id": "life_term_prudential_2023_2024", "last_updated": "2024-11-15T09:30:00Z" }
03 / QUERY ORCHESTRATOR

Intelligent Database Routing

The orchestrator classifies each incoming query by intent, entity density, comparison indicators, and reasoning depth — then routes it to the optimal retrieval pattern among five distinct strategies.

Five Retrieval Patterns — Decision Flow
LLM Query Classifier Entity Density • Comparison • Reasoning Depth P1: Vector-Only Fuzzy/exploratory queries Semantic similarity k=20 "policies covering CKD" P2: Graph-Only Structural traversal Known entities/SPARQL "exclusions in policy X" P3: Vec → Graph Comparison queries Broad recall + structured "compare CI coverage" P4: Graph → Vec Claim validation Rules first, then context "is this claim valid?" P5: PathRAG Multi-hop reasoning Graph paths + vectors "pre-existing → claim?" Decision Logic (Query-by-Example Routing): Rule 1:Structural queries with known entities → Graph-Only (SPARQL traversal) Rule 2:Fuzzy/semantic queries → Vector-Only (cosine similarity search) Rule 3:"Compare" / "versus" / "difference" → Vector-First, Graph-Refinement Rule 4:"Validate" / "check if" / claim queries → Graph-First, Vector-Expansion Rule 5:Multi-hop / complex reasoning → PathRAG Hybrid Fallback:Embed query → find nearest example → adopt that strategy

DatabaseRouter Implementation

database_router.py
from typing import List, Tuple from enum import Enum class RetrievalPattern(Enum): VECTOR_ONLY = "vector_only" # Pattern 1: Fuzzy semantic search GRAPH_ONLY = "graph_only" # Pattern 2: Structured traversal VECTOR_THEN_GRAPH = "vector_then_graph" # Pattern 3: Broad recall → refinement GRAPH_THEN_VECTOR = "graph_then_vector" # Pattern 4: Rules first → context PATHRAG_HYBRID = "pathrag_hybrid" # Pattern 5: Multi-hop reasoning class DatabaseRouter: def __init__(self, classifier_llm, example_db): self.classifier = classifier_llm # Lightweight LLM (Haiku/GPT-3.5) self.examples = example_db # 50-100 labeled query examples def classify_query(self, query: str) -> dict: """Analyze query intent using lightweight LLM call.""" return self.classifier.invoke({ "entity_density": "count specific entities mentioned", "has_comparison": "does query compare items?", "reasoning_hops": "single-hop or multi-hop?", "precision_needed": "exact clause or general guidance?" }) def route(self, query: str, entities: List[str]) -> RetrievalPattern: """Route query to optimal retrieval pattern.""" analysis = self.classify_query(query) # Rule 1: Structural → Graph-Only if analysis["entity_density"] >= 2 and analysis["precision_needed"]: return RetrievalPattern.GRAPH_ONLY # Rule 2: Semantic → Vector-Only if analysis["entity_density"] == 0 and not analysis["has_comparison"]: return RetrievalPattern.VECTOR_ONLY # Rule 3: Comparison → Vector-First, Graph-Refinement if analysis["has_comparison"]: return RetrievalPattern.VECTOR_THEN_GRAPH # Rule 4: Validation → Graph-First, Vector-Expansion if "validate" in query.lower() or "claim" in query.lower(): return RetrievalPattern.GRAPH_THEN_VECTOR # Rule 5: Multi-hop → PathRAG if analysis["reasoning_hops"] > 1: return RetrievalPattern.PATHRAG_HYBRID # Fallback: query-by-example routing return self.examples.find_nearest_strategy(query)
04 / INTERACTIVE FLOW DEMO

See the Orchestrator in Action

Enter a query (or pick an example) and watch the system classify it, route it to the right databases, retrieve context, and synthesize a cited answer — step by step.

Query Flow Simulator

Interactive
Pick an example query or type your own, then click "Run Flow" to visualize the retrieval pipeline.
05 / SOURCE PROVENANCE

PDF Page Attribution & Dual-Pane UI

In financial services, every AI-generated statement must trace back to an exact source page. Click any citation in the answer pane below to see the corresponding PDF text highlight.

AI-Generated Answer (with citations)

Critical illness coverage under the Prudential Term Life policy includes heart attack, stroke, and major organ transplant. The policy specifies that for pre-existing conditions, a mandatory waiting period of 90 days applies before any claims can be filed. However, it's important to note that accidental injuries carry no waiting period and are covered from day one of the policy.

Sources: Prudential Term Life Insurance Policy 2024, Pages 15, 27, 29
PDF Source — Page 15 / 27 / 29
LIFE_TERM_PRUDENTIAL_2024_00127.pdf

Section 4.2 — Critical Illness Coverage

Critical illness coverage includes heart attack, stroke, cancer, kidney failure requiring regular dialysis, major organ transplant, coronary artery bypass surgery, and paralysis of limbs. Additional riders may be purchased for enhanced coverage.

Section 7.1 — Waiting Periods
For pre-existing conditions disclosed at the time of policy issuance, a mandatory waiting period of 90 (ninety) days shall apply before any claims related to such conditions may be filed.

Section 7.3 — Exceptions
Accidental injuries resulting from unforeseen events shall carry no waiting period. Coverage commences from the date of policy activation (Day 1).

How Provenance Tracking Works

Each chunk carries bounding box coordinates (x, y, width, height) from pdfplumber extraction. When the LLM generates an answer, it includes chunk IDs in square brackets, such as [C042]. After generation, the system parses these IDs, maps them to page numbers and bounding boxes, and renders a semi-transparent highlight overlay on the PDF viewer at those coordinates. This produces a Bloomberg Terminal-style dual-pane experience: the AI answer on the left, the highlighted source document on the right.

06 / TTL CLUSTERING

Semantic Clustering for 100K Files

A two-level TTL strategy: individual files for precision updates, plus semantic cluster files for efficient cross-policy SPARQL queries. Clusters are rebuilt quarterly based on query co-occurrence patterns.

Two-Level TTL Strategy with Dynamic Re-clustering
Individual TTL Files (100K) .ttl .ttl .ttl × 100K Semantic Clusters (~200 files) Life Insurance Cluster term_prudential_23_24.ttl term_hdfc_23_24.ttl whole_life_all.ttl ulip_all.ttl ~500 policies per cluster, <50MB each Health Insurance Cluster group_medical.ttl critical_illness.ttl Wealth Planning Cluster retirement_plans.ttl estate_planning.ttl Cluster Manifest cluster → PDF membership staleness tracking async regeneration Quarterly Re-cluster Louvain community detection on query co-occurrence graph

Cluster Size Guidelines

Based on GraphDB performance benchmarks, each cluster TTL file should stay under 50MB and contain no more than 1,000 policies. Larger clusters slow SPARQL query planning. If a semantic category exceeds this threshold (for example, 2,000 term life policies), split by time period — such as term_life_2020_2022.ttl and term_life_2023_2024.ttl.

Dynamic Re-clustering Logic

Every quarter, analyze query logs to find frequently co-accessed policies. If users often compare "Prudential Term Life" with "HDFC Term Life," those should reside in the same cluster regardless of their provider classification. The Louvain community detection algorithm on the query co-occurrence graph identifies these natural groupings automatically.

07 / ANTI-HALLUCINATION

How Knowledge Graphs Prevent Fabrication

Knowledge graphs enforce ontological constraints that vector search alone cannot provide. The graph structure forces precision — you literally cannot retrieve contradictory waiting periods without also retrieving their qualifying conditions.

✕ Pure Vector RAG (No Graph Constraints)

Query: "What's the waiting period for this policy?"

Retrieved Chunk A: "Waiting period is 90 days."
Retrieved Chunk B: "No waiting period for accidents."

LLM Output: "The waiting period is 45 days for accidents."

The LLM "averaged" two contradictory facts because it had no structural context about which condition each clause applies to. This is a dangerous hallucination in financial services.

✓ Graph-Constrained RAG (Ontological Precision)

Query: "What's the waiting period for this policy?"

Graph Path 1: Policy → Clause_WaitingPeriod → appliesTo → PreExistingConditions → 90 days
Graph Path 2: Policy → Clause_AccidentCoverage → appliesTo → AccidentalInjury → 0 days

LLM Output: "The waiting period is 90 days for pre-existing conditions. Accidental injuries have no waiting period."

The ontology forces the LLM to distinguish conditions. Each waiting period is structurally bound to its qualifying context, making fabricated averages impossible.

TTL Representation — Ontological Constraints in Action

policy_clauses.ttl
## ── Each clause is structurally bound to its qualifying condition ── ## The ontology DEMANDS that every waiting period specify what it applies to. ex:Clause_WaitingPeriod a fibo:PolicyClause ; fibo:hasWaitingPeriod "90 days"^^xsd:duration ; fibo:appliesTo ex:PreExistingConditions ; prov:wasDerivedFrom ex:LIFE_TERM_PRUDENTIAL_2024_00127_C187 ; ex:sourcePageNumber 27 . ex:Clause_AccidentCoverage a fibo:PolicyClause ; fibo:hasWaitingPeriod "0 days"^^xsd:duration ; fibo:appliesTo ex:AccidentalInjury ; prov:wasDerivedFrom ex:LIFE_TERM_PRUDENTIAL_2024_00127_C203 ; ex:sourcePageNumber 29 .

Contradiction Detection SPARQL Query

Run this query periodically to catch data quality issues before they cause hallucinations. If it returns results, two clauses claim different waiting periods for the same condition — a red flag that must be resolved at the data layer.

contradiction_check.sparql
SELECT ?clause1 ?clause2 ?value1 ?value2 ?condition WHERE { ?clause1 fibo:hasWaitingPeriod ?value1 . ?clause2 fibo:hasWaitingPeriod ?value2 . ?clause1 fibo:appliesTo ?condition . ?clause2 fibo:appliesTo ?condition . FILTER(?clause1 != ?clause2) FILTER(?value1 != ?value2) # Different values for same condition = contradiction }
08 / PRODUCTION STACK

Real-World Database Choices

What major financial and legal organizations actually use in production — and the recommended stack for an insurance/wealth planning domain with heavy FIBO ontology usage.

Organization Vector DB Graph DB Document Store Orchestration
Bloomberg Terminal Elasticsearch dense_vector Neo4j MongoDB Custom Python + GraphQL
Thomson Reuters Pinecone migrated from Faiss Ontotext GraphDB + FIBO PostgreSQL + JSONB Airflow + FastAPI
LexisNexis Azure Cognitive Search hybrid BM25+vec Amazon Neptune Azure CosmosDB Azure Durable Functions
JPMorgan AI Faiss custom sharding Neo4j Enterprise + Bloom Oracle DB Kubernetes microservices

Recommended Stack for Insurance + FIBO Domain

Vector Store

Pinecone

Serverless, hybrid search, production-ready

Graph Store

Ontotext GraphDB

Best SPARQL 1.1, FIBO-optimized

Metadata Store

PostgreSQL 15+

ACID, JSONB indexing, proven

Caching Layer

Redis

Frequent query results

Orchestration

LangGraph

Custom tools for vector/graph switching