v2.0 — Hybrid Search + Two-Stage Reranking

Hybrid SearchOver Internal Docs

Production-grade RAG pipeline with dense vector search, BM25 sparse retrieval, reciprocal rank fusion, cross-encoder reranking with an LLM-judge fallback, and grounded generation with verified inline citations.

Refuses to guess
Verified citations
Every number benchmarked
vector space · projected embeddings
densesparsereranked

real embeddings, not an illustration

explore the full map ↓
Core Capabilities

Built for Production RAG

Every component designed for reliability, observability, and scale. No vendor lock-in — runs on your infrastructure.

Hybrid Search

Dense vector embeddings (Qdrant) + sparse BM25 keyword search combined via Reciprocal Rank Fusion. Configurable weights for dense/sparse balance per query.

  • Qdrant embedded or remote
  • BM25 with rank_bm25
  • RRF k=60 default
  • Per-query weight override

Two-Stage Reranking

A local cross-encoder (ms-marco MiniLM) rescores the fused candidates in one batch — ~100 ms on CPU, no API cost. RERANK_MODE=llm swaps in an LLM judge for corpora far from its training distribution.

  • Cross-encoder default
  • LLM-judge fallback mode
  • Fusion-order fallback on failure
  • Top-N configurable

Verified Citations

Extract claims → verify against sources → compute composite confidence (retrieval × citation coverage × completeness). Refuses below threshold.

  • Batched verification
  • Composite confidence score
  • Refusal on low confidence
  • Inline citation markers

Smart Chunking

Three strategies: fixed sliding window, recursive markdown-aware, semantic embedding-drift. Switchable via config — no code changes.

  • Fixed / Recursive / Semantic
  • Markdown heading aware
  • Cosine dedup (0.95)
  • Corpus-aware index

REST API + CLI

FastAPI service with Bearer auth. Two endpoints: /v1/ask and /v1/ingest. CLI for local development. OpenAPI docs at /docs.

  • FastAPI + Uvicorn
  • Bearer token auth
  • CLI ingest/ask
  • Auto OpenAPI spec

Eval Ready

Smoke tests with mocked LLM. Golden Q&A dataset structure defined. Chunking strategy comparison via config sweep. Phase 4 extensible.

  • Mocked LLM tests
  • Deterministic embeddings
  • Config sweep ready
  • Golden dataset schema
System Design

Architecture Overview

Modular 7-stage pipeline with clear boundaries. Each stage is independently configurable, swappable, and observable — no hard dependencies between stages.

01

Ingestion

Document loading, parsing, and chunking with configurable strategies

  • Multi-format support (PDF, MD, HTML, DOCX)
  • Configurable chunking: fixed, recursive, semantic
  • Metadata extraction & enrichment
  • Incremental updates with content hashing
ingestion.py
def ingest(path: str, strategy: ChunkStrategy = "recursive"):
    docs = load_documents(path)
    chunks = chunk_documents(docs, strategy)
    enriched = enrich_metadata(chunks)
    return vector_store.upsert(enriched)
02

Embedding

Generate dense and sparse vector representations for hybrid search

  • Dense embeddings (OpenAI, Cohere, local)
  • Sparse embeddings (BM25, SPLADE)
  • Batch processing with rate limiting
  • Cache embeddings for cost efficiency
embedding.py
async def embed(chunks: List[Chunk]) -> EmbeddedChunks:
    dense = await dense_embedder.embed_batch(chunks)
    sparse = sparse_encoder.encode(chunks)
    return EmbeddedChunks(dense=dense, sparse=sparse)
03

Retrieval

Hybrid vector + keyword search with reciprocal rank fusion

  • Dense vector similarity (HNSW index)
  • Sparse lexical search (BM25)
  • Reciprocal Rank Fusion (RRF)
  • Configurable dense/sparse weights
retrieval.py
def retrieve(query: str, k: int = 20) -> List[Result]:
    dense_results = vector_search(query, k * 2)
    sparse_results = bm25_search(query, k * 2)
    fused = rrf_fuse(dense_results, sparse_results)
    return fused[:k]
04

Reranking

Cross-encoder reranking for precision improvement

  • Cross-encoder models (Cohere, BGE, local)
  • Top-k reranking (default: 50 → 10)
  • Score calibration for thresholding
  • Async execution for latency hiding
reranking.py
async def rerank(query: str, results: List[Result]) -> List[Result]:
    pairs = [(query, r.text) for r in results]
    scores = await cross_encoder.score(pairs)
    return sorted(zip(results, scores), key=lambda x: x[1], reverse=True)
05

Generation

Grounded answer synthesis with citation tracking

  • RAG prompt templates with context windowing
  • Streaming responses with citations
  • Confidence scoring per claim
  • Hallucination detection heuristics
generation.py
async def generate(query: str, context: List[Result]) -> Answer:
    prompt = build_rag_prompt(query, context)
    stream = llm.stream(prompt)
    answer = await collect_with_citations(stream)
    confidence = score_confidence(answer, context)
    return Answer(text=answer, confidence=confidence, citations=context)
06

Observability

End-to-end tracing, metrics, and real-time monitoring

  • Distributed tracing (OpenTelemetry)
  • Latency percentiles per pipeline stage
  • Token usage & cost tracking
  • Error rates & alerting thresholds
  • Live dashboard with drill-down
observability.py
from opentelemetry import trace

tracer = trace.get_tracer("rag-pipeline")

@tracer.start_as_current_span("rag_query")
async def query_with_tracing(question: str):
    with tracer.start_as_current_span("retrieval") as span:
        results = retrieve(question)
        span.set_attribute("results.count", len(results))

    with tracer.start_as_current_span("generation") as span:
        answer = await generate(question, results)
        span.set_attribute("tokens.used", answer.token_count)

    return answer
07

Evaluation

Automated quality assessment with golden datasets

  • Golden dataset management (Q&A pairs)
  • RAGAS metrics: faithfulness, answer_relevancy, context_precision
  • Regression detection on pipeline changes
  • A/B testing framework for prompt/model variants
  • CI/CD integration for quality gates
evaluation.py
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
)

def evaluate_pipeline(dataset: Dataset) -> EvaluationReport:
    results = []
    for sample in dataset:
        answer = pipeline.ask(sample.question)
        results.append({
            "question": sample.question,
            "answer": answer.text,
            "contexts": [c.text for c in answer.citations],
            "ground_truth": sample.expected_answer,
        })

    scores = evaluate(results, metrics=[
        faithfulness,
        answer_relevancy,
        context_precision,
        context_recall,
    ])
    return EvaluationReport(scores=scores, passed=all_passed(scores))

Data Flow

Ingestion
Embedding
Retrieval
Reranking
Generation
Observability
Evaluation
Interactive

Live Query Demo

Ask questions against the sample documentation. Requests are proxied to the live pipeline from this page — no API key needed. Answers are grounded, cited and confidence-scored; the console below each answer shows the real execution.

Ready to query

Enter a question above or click a sample to see the grounded answer with citations

projecting stored embeddings…

real embeddings

Every point is an indexed chunk, positioned from its actual stored embedding — same vectors the dense retrieval leg searches. Nearby points are semantically near; that is the search.

legend

reranked finalists

dense matches

sparse matches

indexed, unretrieved

Drag to orbit, scroll to zoom, hover a point for its passage. Ask a question below and the retrieved set lights up against this map.

Try it on your own documents

Ingest, ask, and get a grounded answer with verified citations — in three commands.

View the quickstart
Get Started

Quickstart

From zero to running RAG pipeline in three commands.

1

Install Dependencies

Install the Python package dependencies and configure your environment.

terminal
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your OPENAI_API_KEY
2

Ingest Documents

Load your documentation into the vector store using the CLI or REST API.

terminal
# CLI
python cli.py ingest ./sample_docs

# Or via API
curl -X POST localhost:8000/v1/ingest \
  -H "Authorization: Bearer dev-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"path": "./sample_docs"}'
3

Ask Questions

Query the RAG pipeline and get grounded answers with citations.

terminal
# CLI
python cli.py ask "How do I authenticate?"

# Or via API
curl -X POST localhost:8000/v1/ask \
  -H "Authorization: Bearer dev-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"question": "How do I authenticate?"}'

Key Configuration

VariableDefaultDescription
CHUNK_STRATEGYrecursivefixed | recursive | semantic
DENSE_WEIGHT0.7Vector search weight
SPARSE_WEIGHT0.3BM25 search weight
RRF_K60Reciprocal rank fusion constant
RERANK_CANDIDATE_POOL20Candidates rescored by the reranker
FINAL_TOP_K5Final chunks for generation
MIN_RETRIEVAL_CONFIDENCE0.35Refusal threshold
DEDUP_SIMILARITY_THRESHOLD0.95Cosine similarity for dedup