Local · Multi-Agent · Anti-Hallucination

Grounded
Intelligence,
Not Guesses.

A local-first RAG pipeline that refuses to hallucinate.
Built on LangGraph, Ollama, and pgvector.

Five Agents.
One Purpose.

Each agent has exactly one job. The graph is compiled once and reused — no rebuilds per request.

01 Query Agent

Query Analysis
& Rewriting

Generates a HyDE-style hypothetical answer, creates multiple rewritten queries for multi-query retrieval, classifies intent, and decides whether retrieval is even needed.

  • [HyDE Generation]
  • [Multi-Query Expansion]
  • [Intent Classification]
Explore →
02 Meta Agent

Conversational
Non-RAG Path

Handles greetings, app-help questions, and conversational prompts that should never pretend to cite documents. A dedicated escape valve from the retrieval pipeline.

  • [Greeting Routing]
  • [App-Help Responses]
  • [No-Citation Path]
Explore →
03 Retrieval Agent

Hybrid
Search Pipeline

Runs hybrid dense + sparse retrieval across all rewritten queries and the HyDE answer. Deduplicates while preserving all retrieval metadata, then reranks with a cross-encoder.

  • [Dense pgvector]
  • [BM25 Sparse]
  • [RRF Fusion]
  • [Cross-Encoder Rerank]
Explore →
04 Validation Agent

Faithfulness
Gatekeeper

The anti-hallucination gate. Asks whether retrieved context is sufficient, produces a faithfulness score, and routes weak evidence straight to refusal — no sneaking through.

  • [Faithfulness Scoring]
  • [Threshold Routing]
  • [phi4-mini:3.8b]
Explore →
05 Generation Agent

Cited Answer
or Refusal

Generates cited answers with inline [Source N] references. Post-checks citation validity. If grounding checks fail, the user gets an explicit refusal — never a silent empty answer.

  • [Inline Citations]
  • [Grounding Check]
  • [qwen2.5:7b]
Explore →
START
↓
query_agent
↓ needs retrieval
retrieval_agent
↓
validation_agent
↓ passed
generation_agent
↓ failed
refusal_node
→ meta/greeting
meta_agent
END

The Retrieval
Stack

True hybrid retrieval. Every returned chunk is annotated with diagnostics. Every diagnostic is surfaced in the UI.

4 Retrieval Stages
60% Faithfulness Gate
512 Token Chunk Size
01

Dense Retrieval

pgvector similarity search with nomic-embed-text (768 dims). Annotates each doc with dense_rank and dense_score.

02

Sparse BM25

Corpus-level BM25 across the full indexed collection — not just dense candidates. Cache invalidated on every ingest. Annotates sparse_rank and sparse_score.

03

RRF Fusion

Reciprocal Rank Fusion merges both ranked lists: RRF(d) = Σ 1/(k + rank_i(d)). Annotates rrf_score and retrieval_channels.

04

Cross-Encoder Reranking

cross-encoder/ms-marco-MiniLM-L-6-v2 reranks the fused top-K. Final reranker_score determines what reaches generation.

Core
Capabilities

Every feature is designed around one principle: never guess.

View Source Code →

Ingest PDFs and web URLs. PDF ingestion captures title, author, page count, and page numbers. Web ingestion is section-aware — it tracks h1/h2/h3 headings and groups content under heading paths.

[PyPDFLoader] [Section-Aware Web Scraping] [Duplicate Detection] [Token-Aware Chunking]

Chunk sizes are measured in tokens via tiktoken — not characters. Each chunk is prefixed with structural hints. Chunk IDs are collision-resistant: they encode source name, page marker, doc index, chunk index, and a content hash.

[tiktoken cl100k_base] [512 token chunks] [50 token overlap] [Stable Chunk IDs]

Every returned document carries a full diagnostic trail: dense_rank, dense_score, sparse_rank, sparse_score, rrf_score, reranker_score, and retrieval_channels. These are surfaced directly in the UI — no black box retrieval.

[Dense + Sparse Fusion] [7 Diagnostic Fields] [UI-Surfaced Diagnostics]

Two independent grounding layers. First: the Validation Agent checks faithfulness score against threshold (0.60). Second: the Generation Agent post-validates that every [Source N] reference maps to an actual retrieved chunk. Both can independently trigger refusal.

[Dual-Layer Grounding] [60% Faithfulness Gate] [Citation Verification] [Explicit Refusals]

All models run locally via Ollama. No API keys. No data leaves your machine. Generation model (qwen2.5:7b) is configured for temperature 0.1 — deliberately low for factual RAG. Validation model (phi4-mini:3.8b) is optimized for reasoning at small scale.

[qwen2.5:7b Generation] [phi4-mini:3.8b Validation] [nomic-embed-text 768d] [100% Local]

Running in
Minutes

You need Docker, Ollama, and Python 3.11+. That's it.

01

Clone & Install

git clone https://github.com/kedar49/RAG-Application.git
cd RAG-Application

python3 -m venv .venv
source .venv/bin/activate

pip install -r requirements.txt
cp .env.example .env
02

Start Infrastructure

# Start pgvector (PostgreSQL + pgvector)
docker compose up -d

# Pull required Ollama models
ollama pull qwen2.5:7b        # ~4.7 GB
ollama pull phi4-mini:3.8b    # ~2.5 GB
ollama pull nomic-embed-text  # ~274 MB
03

Launch

streamlit run app.py
# Open http://localhost:8501
The Streamlit UI provides chat, source inventory, retrieval diagnostics, and knowledge base management from a single interface.
RETRIEVAL_TOP_K 20 Dense + sparse candidates before reranking
RERANK_TOP_K 5 Final docs passed to generation
FAITHFULNESS_THRESHOLD 0.60 Below this — refuse to answer
GENERATION_TEMPERATURE 0.1 Low for factual, deterministic answers
CHUNK_SIZE 512 Token-based chunk size via tiktoken
DENSE_SEARCH_K 40 Dense candidate pool before RRF fusion