Technical Reference Manual & Interview Preparation Guide
Version 1.0.0VigilantRAG is an advanced, production-grade Self-Correcting Retrieval-Augmented Generation (RAG) engine. Traditional RAG setups suffer from critical vulnerabilities when deployed in production:
VigilantRAG addresses these challenges by introducing an active, self-correcting feedback loop:
This project uses a fully local, resource-efficient stack designed to run on low-tier CPUs (such as free Hugging Face instances) while maintaining high precision. Below is the technical breakdown and design tradeoffs:
| Component | Selected Tech | Why We Used It | Why Not Alternatives? |
|---|---|---|---|
| Programming Language | Python 3.11 | Standard for AI/ML engineering. Native libraries for tensor operations, vector spaces, and transformers. | Node.js / Go: Immature tooling for local model weights and execution. |
| Web Server | FastAPI | Asynchronous, highly performant, handles concurrent loops, native Pydantic validation, auto-generated OpenAPI docs. | Flask: Synchronous blockages, requires manual plugins. Django: Heavyweight, bloated for a single-page engine. |
| Dense Index | FAISS | High-performance local vector similarity search. In-memory, runs on CPU without background services. | Pinecone/Milvus: Costly, require internet APIs or complex docker containers. |
| Sparse Index | Rank-BM25 | Term-frequency matching. Essential for exact names, specific serial numbers, and technical jargon. | SQL LIKE: Inefficient, does not compute statistical term frequency. |
| Bi-Encoder | all-MiniLM-L6-v2 | 384-dimensional dense embedding model. Extremely lightweight (90MB), runs in milliseconds on CPU. | OpenAI embeddings: Introduces network dependency, API costs, and privacy concerns. |
| Re-ranker Model | ms-marco-MiniLM-L-6-v2 | Cross-Encoder model. Scores query-document pairs jointly to capture deep semantic relevance. | Cosine Similarity alone: Fails to capture subtle word interactions. |
| Factual Auditor | nli-deberta-v3-xsmall | Natural Language Inference classifier. Detects logical conflicts between answer and source context. | LLM-as-a-Judge: Slow, non-deterministic, expensive, and can hallucinate the audit. |
| Generative LLM | Qwen2.5-0.5B-Instruct | Lightweight, 0.5B instruction-tuned model. Runs locally on CPU, handles system prompts cleanly. | Llama-3-8B: Too heavy for basic servers (requires dedicated GPU). |
The following text diagram details how data flows through the multi-stage engine for every query:
Here is what each file in the workspace is responsible for:
src/config.py: Configures model names, directory paths (such as the data/ folder), default thresholds (relevance=0.40, NLI=0.60), and system prompt templates.src/retriever.py: Manages document ingestion. Chunks text with configurable overlap, runs the FAISS vector index, handles BM25 tokenization, and merges results.src/reranker.py: Computes joint query-context logits using the Cross-Encoder model and sorts chunks by score.src/query_expansion.py: Performs query rewriting. Contains synonym rules (e.g. mapping "wfh" → "remote", "study" → "learning") and fallback LLM rephrasing prompts.src/hallucination_guard.py: Evaluates the factual alignment of drafts against the context. Outputs entailment, neutral, and contradiction scores.src/llm_client.py: Manages the local Qwen2.5 model instance, formats instruction prompts, and adjusts temperatures.src/engine.py: Coordinates the pipeline. Collects telemetry logs (timing metrics, intermediate drafts, scores) and exposes the clean query() function.app.py: FastAPI server file. Sets up CORS, mounts static UI files, and defines API endpoints (/api/query, /api/ingest, /api/documents).The dashboard is built with a premium glassmorphic dark-mode CSS theme. Here is how each visual component works:
A visual timeline representing the stages of execution. Clicking on any step dynamically populates the details panel on the right with internal execution variables:
Q1: What happens when the RAG retriever fails to find relevant documents on the first pass?
A: VigilantRAG monitors the Cross-Encoder score of the top-ranked chunk. If it falls below the relevance threshold (e.g. 0.40), the engine flags it as irrelevant context. It stops, expands the query with domain synonyms or an LLM rewrite, and re-executes the search. This prevents the model from generating answers based on unrelated, garbage context.
Q2: Why use an NLI model for hallucination checking instead of another LLM query?
A: NLI models are specialized classifiers trained specifically to grade logical entailment. They are deterministic, fast (running in milliseconds on CPU), and output exact probability logits. Using an LLM to check another LLM is slow, expensive, prone to prompt injection, and suffers from the same hallucination issues it is trying to detect.
Q3: How does the system handle "unanswerable" questions where facts do not exist in the corpus?
A: When a user asks an unanswerable question, the initial relevance check fails, triggering query expansion. If the second retrieval pass still fails to find relevant chunks (relevance remains < 0.40), the engine instructs the LLM to output a fallback response ("I do not have sufficient information in the context to answer this question.") which passes the NLI check because it makes no active assertions.
Q4: Why run the models locally inside the container instead of calling API endpoints?
A: Local model execution ensures absolute data privacy, eliminates API costs, avoids network latency/outages, and ensures the application is completely self-contained. By using optimized, small-footprint models (like MiniLM and Qwen-0.5B), we run inference quickly on basic CPU servers.