raggit

raggit

A plug-and-play production-grade RAG system that connects to local and remote object storage, automatically indexes documents, and answers questions using hybrid retrieval with reranking and LLM augmentation.

What raggit does

raggit is built for teams that need reliable retrieval-augmented generation without managing a patchwork of vector databases, parsers, and rerankers. It handles the full document lifecycle: watching storage, parsing common formats, chunking intelligently, embedding, searching, and generating cited answers.

  • Hybrid retrieval combines BM25 keyword search with dense semantic search, fused with weighted Reciprocal Rank Fusion.
  • Format-aware chunking preserves document structure for Markdown, code, PDFs, and plain text.
  • Safety and observability with optional PII redaction, prompt-injection hardening, and structured audit logs.
  • Multi-tenant filtering by source URI, filename, tenant, tags, document IDs, and date range.
  • Multiple storage backends: local filesystem, S3, Google Cloud Storage, and Azure Blob Storage.
Project status

raggit is in early development. The core ingestion, retrieval, and CLI flows are functional and tested. API server mode and analytics are on the roadmap.

Quick Start

You need Docker Desktop (or Docker Engine + Compose) and uv installed.

  1. Start PostgreSQL and Qdrant

    docker compose up -d postgres qdrant

    PostgreSQL is mapped to host port 5433 to avoid conflicts with a local postgres on 5432.

  2. Install dependencies

    uv sync
  3. Run database migrations

    uv run alembic upgrade head
  4. Configure raggit

    uv run raggit setup \
      --database-url postgresql+asyncpg://raggit:raggit@localhost:5433/raggit \
      --qdrant-url http://localhost:6333 \
      --storage-source-type local \
      --storage-uri ./data/documents \
      --llm-provider openai \
      --llm-model gpt-4o-mini \
      --llm-api-key $OPENAI_API_KEY
  5. Add documents and ingest

    mkdir -p data/documents
    cp my-docs/*.pdf data/documents/
    uv run raggit ingest ./data/documents
  6. Ask questions

    uv run raggit query "What is raggit?"
  7. Run the watcher for continuous indexing

    uv run raggit watch

See the storage backends section for S3, GCS, and Azure setup.

Architecture

raggit is organized into clear layers: storage, ingestion, data, retrieval, LLM, audit, and interface. All I/O is async, and every significant input and output is logged to PostgreSQL.

flowchart TB subgraph Storage LS[Local Filesystem] RS[S3 / GCS / Azure Blob] end subgraph Ingestion W[Watcher Service] I[Indexing Service] P[Parser Registry] FA[Format-Aware Chunker] DED[Dedup] CL[Chunk Cleaner] PI[PII Redaction] IH[Injection Hardening] E[Embedder] end subgraph PostgreSQL D[documents] CH[chunks] EC[embedding_collections] LG[logs] end VS[Qdrant Vector Store] LS --> W RS --> W W --> I I --> P P --> FA FA --> DED DED --> CL CL --> PI PI --> IH IH --> E E --> VS I --> D IH --> CH E --> CH I --> EC IH --> LG E --> LG subgraph Retrieval Q[User Query] QR[Query Rewriter] MQ[Multi-Query] HY[HyDE] BM25[Postgres FTS / BM25] SEM[Qdrant Semantic Search] RRF[Weighted RRF] RR[Cross-Encoder Reranker] TH[Score Threshold] PW[Parent-Window Expansion] AUG[Augmenter] LLM[LLM Provider] GRD[Groundedness Check] OUT[Answer + Citations] end Q --> QR QR --> BM25 QR --> SEM QR --> MQ QR --> HY BM25 --> RRF SEM --> RRF RRF --> RR RR --> TH TH --> PW PW --> AUG AUG --> LLM LLM --> GRD GRD --> OUT RRF --> LG AUG --> LG LLM --> LG

Design goals

  • Accuracy through hybrid retrieval, cross-encoder reranking, query rewriting, and parent-document expansion.
  • Safety by isolating untrusted document text from system instructions and optionally redacting PII.
  • Observability with lifecycle statuses, model-scoped collections, and structured audit logs.
  • Multi-tenancy via tenant_id and tags applied consistently across BM25 and vector search.
  • Evolvability via model-scoped Qdrant collections so changing embedding models does not corrupt existing indexes.

Ingestion Pipeline

When a file is added or changed, the watcher triggers the indexer, which runs the document through parsing, format-aware chunking, cleaning, optional safety steps, embedding, and persistence.

1
Watch local filesystem or remote object storage for add, modify, and delete events.
2
Parse PDF, DOCX, HTML, Markdown, and plain text. PDF pages are marked explicitly so page numbers survive chunking.
3
Chunk in a format-aware way: Markdown by headers, code by top-level definitions, PDF by page markers, and fallback recursive character splitting.
4
Size chunks by tokens using tiktoken cl100k_base when available, with configurable overlap.
5
Dedup near-identical chunks using content hash plus Jaccard word-set similarity.
6
Clean chunks by normalizing Unicode, collapsing whitespace, and fixing hyphenation.
7
Redact PII (optional) and harden against prompt-injection patterns before embedding.
8
Embed chunks using local sentence-transformers or an OpenAI-compatible API.
9
Store vectors in a model-scoped Qdrant collection and metadata/links in PostgreSQL.
10
Audit every ingestion input and output to the PostgreSQL logs table.

Retrieval Pipeline

Queries are sanitized, optionally rewritten, run through BM25 and semantic search in parallel, fused, reranked, expanded, and then passed to the LLM with citations.

1
Sanitize the query to extract keywords.
2
Rewrite the query when configured: multi_query generates alternative phrasings; hyde generates a hypothetical answer passage to embed.
3
Filter BM25 and semantic searches by metadata: source URI prefix, filename prefix, tenant, tags, document IDs, date range.
4
BM25 keyword search via PostgreSQL full-text search with a tsvector GIN index.
5
Semantic similarity search via Qdrant cosine distance.
6
Fuse ranked lists with weighted Reciprocal Rank Fusion with tunable BM25/semantic weights.
7
Rerank top-N candidates with an optional cross-encoder such as BAAI/bge-reranker-base.
8
Threshold low-confidence chunks with min_score and refuse empty or low-score retrieval.
9
Expand parent-document windows around each hit when parent_window > 0.
10
Cite every chunk with ID, source URI, filename, page, section, offsets, score, and excerpt.
11
Augment the prompt with isolated untrusted-context wrappers and system instructions.
12
Generate an answer via an OpenAI-compatible API or Ollama.
13
Check groundedness and warn if the answer is not supported by retrieved context.
14
Audit the query, retrieval result, and final answer to the PostgreSQL logs table.

CLI Reference

The raggit CLI is the primary interface for setup, ingestion, watching, and querying.

Command Description
raggit setup Interactive configuration for local, S3, GCS, and Azure backends.
raggit ingest <path> One-time ingestion with a progress bar. Path is optional for cloud storage.
raggit watch <path> Continuously watch and index with live event indicators.
raggit query "<question>" Ask a question; shows status spinners, chunk table, answer panel, and citation tree.
raggit status Show indexed document status and active embedding collections.

Query options

raggit query supports --top-k, --source-prefix, --filename-prefix, --tenant, --tag (repeatable), --min-score, and --rewrite.

Docker Deployment

Build and run the entire stack with one command:

docker compose up -d

This starts:

  • raggit-postgres on port 5433
  • raggit-qdrant on ports 6333 and 6334
  • raggit-app running the watcher

Mount your documents into ./data/documents.

Configuration

Configuration is loaded from environment variables and a ~/.config/raggit/raggit.env file generated by raggit setup. The setup command writes the file with 0600 permissions.

Environment variables

Variable Default Description
DATABASE_URLpostgresql+asyncpg://raggit:raggit@localhost:5433/raggitPostgreSQL connection string.
QDRANT_URLhttp://localhost:6333Qdrant URL.
QDRANT_COLLECTIONraggit_chunksQdrant collection name.
STORAGE_SOURCE_TYPElocalStorage backend: local, s3, gcs, azure_blob.
STORAGE_URI./data/documentsStorage URI or local path.
STORAGE_BUCKETNoneS3/GCS bucket name.
STORAGE_CONTAINERNoneAzure container name.
STORAGE_PREFIXNoneObject prefix or folder.
STORAGE_REGIONNoneS3 region.
STORAGE_AWS_ACCESS_KEY_IDNoneAWS access key ID.
STORAGE_AWS_SECRET_ACCESS_KEYNoneAWS secret access key.
STORAGE_GCS_SERVICE_ACCOUNT_PATHNoneGCS service account JSON path.
STORAGE_AZURE_CONNECTION_STRINGNoneAzure Blob connection string.
LLM_PROVIDERopenaiLLM provider.
LLM_MODELgpt-4o-miniModel name.
LLM_API_KEYNoneAPI key.
EMBEDDING_PROVIDERsentence-transformersEmbedding provider.
EMBEDDING_MODELBAAI/bge-small-en-v1.5Embedding model.

Storage Backends

raggit supports local filesystem, S3, Google Cloud Storage, and Azure Blob Storage. Remote backends are installed as optional extras to keep the base install small.

AWS S3

Install the S3 extra:

uv pip install 'raggit[s3]'

Then run setup with S3 options:

uv run raggit setup \
  --database-url postgresql+asyncpg://raggit:raggit@localhost:5433/raggit \
  --qdrant-url http://localhost:6333 \
  --storage-source-type s3 \
  --storage-uri s3://my-bucket/documents \
  --storage-bucket my-bucket \
  --storage-prefix documents \
  --storage-region us-east-1 \
  --aws-access-key-id $AWS_ACCESS_KEY_ID \
  --aws-secret-access-key $AWS_SECRET_ACCESS_KEY \
  --llm-provider openai \
  --llm-model gpt-4o-mini \
  --llm-api-key $OPENAI_API_KEY

Google Cloud Storage

Install the GCS extra:

uv pip install 'raggit[gcs]'
uv run raggit setup \
  --database-url postgresql+asyncpg://raggit:raggit@localhost:5433/raggit \
  --qdrant-url http://localhost:6333 \
  --storage-source-type gcs \
  --storage-uri gs://my-bucket/documents \
  --storage-bucket my-bucket \
  --storage-prefix documents \
  --gcs-service-account-path /path/to/service-account.json \
  --llm-provider openai \
  --llm-model gpt-4o-mini \
  --llm-api-key $OPENAI_API_KEY

Azure Blob Storage

Install the Azure extra:

uv pip install 'raggit[azure]'
uv run raggit setup \
  --database-url postgresql+asyncpg://raggit:raggit@localhost:5433/raggit \
  --qdrant-url http://localhost:6333 \
  --storage-source-type azure_blob \
  --storage-uri azure://my-container/documents \
  --storage-container my-container \
  --storage-prefix documents \
  --azure-connection-string $AZURE_STORAGE_CONNECTION_STRING \
  --llm-provider openai \
  --llm-model gpt-4o-mini \
  --llm-api-key $OPENAI_API_KEY
Cloud watcher

Cloud storage watchers use periodic polling and snapshot diffing instead of native push notifications. This keeps the implementation portable across providers without requiring SQS, Pub/Sub, or Event Grid.

Development

# Run linting
uv run ruff check .

# Run type checking
uv run mypy src

# Run tests
uv run pytest

License

raggit is released under the MIT License.

Back to top