Spaces:
Sleeping
Sleeping
Srini P
refactor: remove Azure OpenTelemetry integration and implement core RAG pipeline orchestration with backend setup
6bc9349 | # FinBot Backend Architecture - Complete Guide | |
| ## Overview | |
| The FinBot RAG backend is organized around a **layered architecture** with clear separation of concerns. Each module handles a specific domain of the system, allowing for maintainability, testability, and scalability. | |
| ``` | |
| REQUEST | |
| β | |
| [main.py] - FastAPI endpoints | |
| β | |
| [pipeline/rag_pipeline.py] - Orchestration | |
| ββ [guardrails/input_guards.py] - Validate queries | |
| ββ [routing/router.py] - Route query to collection | |
| ββ [retrieval/rbac_retriever.py] - RBAC-enforced retrieval | |
| ββ [Groq API] - Generate answer | |
| ββ [SentenceTransformer] - Generate embeddings | |
| ββ [guardrails/output_guards.py] - Validate response | |
| β | |
| RESPONSE | |
| ``` | |
| --- | |
| ## Directory Structure & File Organization | |
| ### 1. **Root Level Files** | |
| #### `main.py` (FastAPI Application) | |
| - **Purpose**: HTTP API entry point | |
| - **Responsibility**: | |
| - Define endpoints (routes) | |
| - Request/response validation | |
| - CORS setup | |
| - Lifecycle management | |
| - **Key Endpoints**: | |
| - `POST /api/chat` - Main chat endpoint | |
| - `GET /api/health` - System health check | |
| - `GET /api/users/{username}` - Get user info | |
| - `POST /admin/create-user` - Admin user creation | |
| - `POST /admin/ingest` - Document ingestion trigger | |
| **Key Pattern**: Controllers/Handlers that delegate to services | |
| --- | |
| #### `config.py` (Configuration & Constants) | |
| - **Purpose**: Centralize all configuration | |
| - **Contains**: | |
| - User roles enum (EMPLOYEE, FINANCE, ENGINEERING, MARKETING, C_LEVEL) | |
| - Document collections enum (GENERAL, FINANCE, ENGINEERING, MARKETING, HR) | |
| - **CRITICAL**: `ROLE_COLLECTION_ACCESS` mapping (defines RBAC rules) | |
| - Demo users for testing | |
| - LLM config (model, temperature, tokens) | |
| - Retrieval config (top_k, score_threshold) | |
| **Key Pattern**: Single source of truth for all constants | |
| **Example RBAC Rule**: | |
| ```python | |
| ROLE_COLLECTION_ACCESS = { | |
| "employee": ["general"], | |
| "finance": ["general", "finance"], | |
| "engineering": ["general", "engineering"], | |
| "c_level": ["general", "finance", "engineering", "marketing", "hr"], | |
| } | |
| ``` | |
| --- | |
| #### `vector_store.py` (Qdrant Vector Database) | |
| - **Purpose**: Interface to Qdrant vector database (Cloud or Local) | |
| - **Responsibility**: | |
| - Connect to Qdrant Cloud for persistent, shared storage | |
| - Fallback to local persistent storage for disconnected development | |
| - Create/manage vector collections and enforce RBAC filters | |
| - **Key Metadata Fields**: | |
| - `access_roles`: Which roles can access this chunk | |
| - `collection_name`: Which collection (finance, engineering, etc.) | |
| - `source_file`: Original document | |
| - `chunk_position`: Position in hierarchical structure | |
| **Key Pattern**: Singleton pattern (single instance per app) | |
| --- | |
| #### `metadata_schema.py` (Data Models) | |
| - **Purpose**: Pydantic models for data validation | |
| - **Key Classes**: | |
| - `Chunk` - Represents a searchable document chunk | |
| - `RAGResponse` - Full pipeline response | |
| - `QueryMetadata` - Metadata about the query | |
| - `RetrievalResult` - Retrieval layer output | |
| **Key Pattern**: Schema validation & type safety | |
| --- | |
| ### 2. **Pipeline Module** (`pipeline/`) | |
| #### `rag_pipeline.py` (Orchestration Engine) | |
| - **Purpose**: Orchestrate the entire RAG flow | |
| - **Thought Process**: | |
| - A query goes through 5 distinct stages | |
| - Each stage has a specific responsibility | |
| - Each stage can fail independently and is logged | |
| **5-Stage Pipeline**: | |
| **Stage 1: Input Validation (Guardrails)** | |
| ``` | |
| Query β rate_limit check β injection detection β OffTopic check β PII check | |
| β If fails, return error immediately | |
| ``` | |
| **Stage 2: Semantic Routing** | |
| ``` | |
| Query β Router (semantic-router) β Select collection | |
| "Show me sales data" β Route to FINANCE collection | |
| "How does the API work" β Route to ENGINEERING collection | |
| ``` | |
| **Stage 3: RBAC-Enforced Retrieval** | |
| ``` | |
| User Role + Selected Collection β Check access β Query vector store | |
| Employee wants FINANCE β DENIED | |
| Finance user wants FINANCE β ALLOWED β Retrieve chunks | |
| ``` | |
| **Stage 4: LLM Generation** | |
| ``` | |
| Question + Retrieved Chunks β Groq (Llama 3.3 70B) β Generate answer | |
| Uses retrieved chunks as context (RAG) | |
| ``` | |
| **Stage 5: Output Validation (Guardrails)** | |
| ``` | |
| Generated Answer β Check for hallucinations β Check for missing citations | |
| β If issues detected, flag in response | |
| ``` | |
| **Return Complete Response**: | |
| - `answer`: The generated response | |
| - `sources`: Which chunks were used | |
| - `route`: Which collection was queried | |
| - `accessible_collections`: What user can access | |
| - `guardrail_flags`: Any warnings/issues detected | |
| - `rbac_denied`: Was access denied? | |
| **Key Pattern**: Pipeline Pattern (chain of processors) | |
| --- | |
| ### 3. **Routing Module** (`routing/`) | |
| #### `router.py` (Semantic Query Routing) | |
| - **Purpose**: Determine which collection a query should search | |
| - **Technology**: SemanticRouter (ML-based routing) | |
| - **Examples**: | |
| ``` | |
| "What are Q4 financials?" β FINANCE | |
| "How do I set up the API?" β ENGINEERING | |
| "What's our market strategy?" β MARKETING | |
| "What are company policies?" β GENERAL | |
| ``` | |
| #### `semantic_router_config.py` (Router Training Data) | |
| - **Purpose**: Define routes and training examples | |
| - **Contents**: Route definitions with example queries for each route | |
| - **How It Works**: Semantic router learns from examples to categorize new queries | |
| **Key Pattern**: Configuration-driven machine learning | |
| --- | |
| ### 4. **Retrieval Module** (`retrieval/`) | |
| #### `rbac_retriever.py` (RBAC-Enforced Vector Search) | |
| - **Purpose**: Retrieve chunks while enforcing access control | |
| - **Critical Logic**: | |
| ``` | |
| 1. Get user's accessible collections (from config) | |
| 2. Validate requested collections against user's access | |
| 3. Search vector store ONLY in allowed collections | |
| 4. Return chunks user is authorized to see | |
| ``` | |
| **Key Principle**: RBAC filter is applied at vector store level, not post-processing | |
| **Examples**: | |
| - Employee asks for FINANCE data β Denied, no chunks returned | |
| - Finance user asks for FINANCE data β Allowed, chunks returned with access roles verified | |
| **Key Pattern**: Authorization layer (middleware pattern) | |
| --- | |
| #### `user_auth.py` (User Management) | |
| - **Purpose**: User authentication & authorization | |
| - **Responsibility**: | |
| - Store user profiles (role, department, etc.) | |
| - Map roles to accessible collections (using config.py) | |
| - Validate user roles | |
| - **Demo Users**: Pre-defined users for testing | |
| **Key Pattern**: Identity & Permissions service | |
| --- | |
| ### 5. **Guardrails Module** (`guardrails/`) | |
| #### `input_guards.py` (Input Validation) | |
| - **Purpose**: Validate and sanitize user input BEFORE processing | |
| - **Checks**: | |
| - **Rate Limiting**: Max queries per user per time period | |
| - **Injection Detection**: SQL/prompt injection attempts | |
| - **Off-Topic Detection**: Is query relevant to knowledge base? | |
| - **PII Detection**: Does query ask for sensitive data? | |
| **Examples**: | |
| ``` | |
| Query: "; DROP TABLE users; --" | |
| β Detected as injection β Rejected | |
| Query: "What's my credit card number?" | |
| β Detected as PII request β Rejected | |
| Query: "Tell me a joke" | |
| β Detected as off-topic β Rejected | |
| Query: "Show me Q4 sales" | |
| β Passes all checks β Continue to routing | |
| ``` | |
| **Key Pattern**: Defense-in-depth (multiple checks) | |
| --- | |
| #### `output_guards.py` (Output Validation) | |
| - **Purpose**: Validate LLM response BEFORE returning to user | |
| - **Checks**: | |
| - **Hallucination Detection**: Is answer grounded in source documents? | |
| - **Citation Quality**: Are sources properly cited? | |
| - **Completeness**: Does answer address the query? | |
| **Examples**: | |
| ``` | |
| Answer contains facts not in source docs | |
| β Flag as potential hallucination β Warn user | |
| Answer references sources that weren't used | |
| β Flag as citation error β Warn user | |
| ``` | |
| **Key Pattern**: Quality assurance layer | |
| --- | |
| ### 6. **Ingestion Module** (`ingestion/`) | |
| #### `docling_parser.py` (Document Parsing) | |
| - **Purpose**: Parse complex documents (PDF, DOCX, Markdown) | |
| - **Responsibility**: | |
| - Convert documents to structured text | |
| - Preserve document hierarchy (sections, subsections, etc.) | |
| - Extract metadata (titles, headings, structure) | |
| - **Output**: Parsed document with hierarchical structure | |
| **Key Pattern**: Standard parser pattern | |
| --- | |
| #### `hierarchical_chunker.py` (Smart Chunking) | |
| - **Purpose**: Break documents into optimal chunks | |
| - **Thought Process Behind Chunking**: | |
| ``` | |
| Raw Document (10+ pages) | |
| β | |
| Split by sections (respects hierarchy) | |
| β | |
| Split by semantic meaning (paragraphs, lists) | |
| β | |
| Create recursive chunks (overlap for context) | |
| β | |
| Tag chunks with metadata (section, source, role access) | |
| β | |
| Final Chunks (good context, minimal overlap) | |
| ``` | |
| **Why Hierarchical?** | |
| - Maintains document structure | |
| - Preserves context (related info together) | |
| - Enables collection-level access control | |
| - Improves retrieval relevance | |
| **Key Pattern**: Recursive chunking | |
| --- | |
| #### `document_ingester.py` (Orchestration of Ingestion) | |
| - **Purpose**: Coordinate parsing β chunking β embedding β storage | |
| - **Pipeline**: | |
| ``` | |
| Document β Parse (docling_parser) | |
| β Chunk (hierarchical_chunker) | |
| β Generate embeddings (SentenceTransformer locally) | |
| β Tag with access roles (from config) | |
| β Store in vector DB (Qdrant) | |
| ``` | |
| **Key Pattern**: Pipeline pattern applied to ingestion | |
| --- | |
| ## Design Patterns Used | |
| ### 1. **Layered Architecture** | |
| Each layer has a specific responsibility and depends on layers below, but not above: | |
| ``` | |
| API Layer (main.py) | |
| β | |
| Business Logic Layer (pipeline/) | |
| β | |
| Data Access Layer (retrieval/, vector_store/) | |
| β | |
| External Services (Groq, Qdrant) | |
| ``` | |
| ### 2. **Singleton Pattern** | |
| Single instances of expensive resources: | |
| - Vector store (`get_vector_store()`) | |
| - RAG pipeline (`get_rag_pipeline()`) | |
| - User manager (`get_user_manager()`) | |
| ### 3. **Pipeline Pattern** | |
| Processes flow through stages: | |
| - RAG pipeline (input β routing β retrieval β LLM β output) | |
| - Ingestion pipeline (parse β chunk β embed β store) | |
| ### 4. **Factory Pattern** | |
| Create instances via factory functions: | |
| ```python | |
| pipeline = get_rag_pipeline() | |
| retriever = get_rbac_retriever() | |
| router = get_router() | |
| ``` | |
| ### 5. **Configuration-Driven Design** | |
| Behavior controlled by `config.py`: | |
| - Collection access rules | |
| - User roles | |
| - LLM settings | |
| - No hardcoded values | |
| ### 6. **Authorization Layer** | |
| RBAC enforced at retrieval layer: | |
| - Not post-filtering | |
| - Vetted at vector store level | |
| - Cannot bypass | |
| --- | |
| ## Data Flow Example: User Query | |
| ``` | |
| User: "Show me the Q4 sales report" | |
| Role: finance | |
| 1. REQUEST | |
| POST /api/chat | |
| { "query": "Show me the Q4 sales report", "user_role": "finance" } | |
| 2. MAIN.PY (FastAPI) | |
| Validates request format, calls pipeline.answer_query() | |
| 3. PIPELINE - STAGE 1: INPUT GUARDS | |
| β Not a rate limit violation | |
| β Not an injection attack | |
| β Not off-topic | |
| β No PII request | |
| 4. PIPELINE - STAGE 2: ROUTING | |
| Query β Router β "This is about SALES/FINANCE" | |
| Route: FINANCE collection | |
| 5. PIPELINE - STAGE 3: RBAC RETRIEVAL | |
| User role: finance | |
| Requested collection: FINANCE | |
| β finance role CAN access FINANCE collection | |
| Query Qdrant ONLY in FINANCE collection | |
| Returns: [chunk1, chunk2, chunk3] (Q4 sales data) | |
| 6. PIPELINE - STAGE 4: LLM GENERATION | |
| Prompt: context + query + instructions | |
| "Based on the Q4 sales data below, answer: Show me the Q4 sales report" | |
| Groq generates comprehensive answer | |
| 7. PIPELINE - STAGE 5: OUTPUT GUARDS | |
| β Answer is grounded in source documents | |
| β Sources are properly cited | |
| β No hallucinations detected | |
| 8. RESPONSE | |
| { | |
| "answer": "Q4 sales totaled $4.2M...", | |
| "sources": [chunk1, chunk2, chunk3], | |
| "route": "finance", | |
| "user_role": "finance", | |
| "accessible_collections": ["general", "finance"], | |
| "guardrail_flags": [], | |
| "rbac_denied": false | |
| } | |
| ``` | |
| --- | |
| ## RBAC Enforcement Example | |
| ### Scenario 1: Authorized Access | |
| ``` | |
| User: emp_john | |
| Role: employee | |
| Query: "Company policies" | |
| RBAC Check: | |
| - Employee can access: [general] | |
| - Query routed to: general β | |
| - Allowed collections: general β | |
| β Retrieval succeeds | |
| ``` | |
| ### Scenario 2: Unauthorized Access | |
| ``` | |
| User: emp_john | |
| Role: employee | |
| Query: "What are company financials?" | |
| RBAC Check: | |
| - Employee can access: [general] | |
| - Query routed to: finance β | |
| - Allowed collections: general β | |
| β RBAC DENIED | |
| β No chunks retrieved | |
| β Response: "You don't have access to financial data" | |
| ``` | |
| --- | |
| ## Key Design Decisions | |
| ### 1. **Why Semantic Routing?** | |
| - Automatically routes queries to right collection | |
| - No manual labeling needed | |
| - Scales with new collections | |
| ### 2. **Why Hierarchical Chunking?** | |
| - Preserves document context | |
| - Enables collection-level access control | |
| - Improves relevance | |
| ### 3. **Why RBAC at Vector Store Level?** | |
| - Cannot be bypassed | |
| - **Retrieval Engine**: RBAC-aware vector search | |
| - **Generation Engine**: Groq (Llama 3.3 70B) | |
| - **Security Layer**: Input/Output Guardrails | |
| - Defense in depth | |
| - Prevents malicious input | |
| - Ensures answer quality | |
| - Auditable (logged) | |
| ### 4. **Why Separate Input/Output Guards?** | |
| - Defense in depth | |
| - Prevents malicious input | |
| - Ensures answer quality | |
| - Auditable (logged) | |
| ### 5. **Why Singleton Pattern?** | |
| - Vector store connections are expensive | |
| - LLM client setup is expensive | |
| - Router models take time to load | |
| - Reuse same instance across requests | |
| --- | |
| ## Summary | |
| The backend is architected as a **layered pipeline** where: | |
| 1. **Configuration** (`config.py`) is the single source of truth for RBAC rules | |
| 2. **API** (`main.py`) is the thin HTTP layer | |
| 3. **Pipeline** (`pipeline/`) orchestrates the flow | |
| 4. **Guardrails** protect against bad input and bad output | |
| 5. **Routing** directs to correct collection | |
| 6. **Retrieval** enforces access control | |
| 7. **Ingestion** prepares documents for search | |
| Each component is **focused**, **testable**, and **replaceable**. This design enables building a robust, secure RAG system that demonstrates enterprise-grade RBAC and quality assurance patterns. | |