Built for HH Goa 2026 Open Trials — Task #2: Voice-Enabled RAG Model.
Speak a question → get a grounded, sourced answer. Full pipeline: transcription → engineered multi-strategy chunking (already applied at index time) → hybrid retrieval → rerank → guarded generation.
Mic/audio file
│
▼
┌─────────┐ ┌──────────────┐ ┌───────────┐ ┌────────────┐ ┌──────────┐
│ STT │ ──▶ │ Hybrid │ ──▶ │ Rerank │ ──▶ │ Confidence │ ──▶ │ Generate │
│ Whisper │ │ Retrieval │ │ cross-enc │ │ Gate │ │ (LLM) │
└─────────┘ │ (BM25+dense) │ └───────────┘ │(guardrail) │ └──────────┘
└──────────────┘ │ fail → "I don't know"
▼
grounded JSON answer
{answer, sources, confidence}
Chunking happens once, offline, at ingest time (ingest/) — not per-query.
Each source document is routed to the chunker suited to its shape:
| Content type | Chunker | Why |
|---|---|---|
| Prose / narrative docs | chunk_semantic.py |
Splits on meaning boundaries, not fixed size |
| Tables / structured | chunk_table.py |
Keeps rows + header context intact |
| Step-by-step / procedural | chunk_procedural.py |
Sliding window with overlap so steps aren't cut |
reef/
├── ingest/
│ ├── chunk_semantic.py
│ ├── chunk_table.py
│ ├── chunk_procedural.py
│ └── ingest.py # routes docs → chunker, writes data/chunks.jsonl
├── index/
│ └── build_index.py # builds dense (Chroma) + sparse (BM25) indexes
├── stt/
│ └── transcribe.py # faster-whisper wrapper
├── retrieve/
│ └── hybrid_retrieve.py # BM25 + dense fusion, cross-encoder rerank, confidence gate
├── generate/
│ └── generate.py # LLM call with retry + structured JSON output
├── benchmarks/
│ └── run_benchmark.py # end-to-end latency benchmark, P50/P70/P100
├── frontend/
│ └── index.html # minimal mic-capture UI, hits /ask
├── data/ # put your raw corpus here
├── app.py # FastAPI server tying it all together
└── requirements.txt
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# 1. Drop your corpus (pdf/txt/md/csv) into data/raw/
# 2. Ingest + chunk
python ingest/ingest.py --input data/raw --output data/chunks.jsonl
# 3. Build indexes
python index/build_index.py --chunks data/chunks.jsonl
# 4. Run the server
uvicorn app:app --reload
# 5. Open frontend/index.html (or serve it) and talk to Reefpython benchmarks/run_benchmark.py --queries benchmarks/queries.txtOutputs benchmarks/results.csv and benchmarks/latency_report.png with
per-stage P50/P70/P100 latency — drop straight into your submission.
If the top reranked chunk score falls below CONFIDENCE_THRESHOLD
(retrieve/hybrid_retrieve.py), Reef returns a "not enough information"
response instead of generating — this is logged and counted in the
benchmark report as a guardrail trigger rate.
- India GST/tax rule PDFs (mixed prose + tables + amendments)
- A large OSS project's docs + closed issues (inconsistent formatting)
- Drug interaction / dosage leaflets (safety-critical → guardrail matters)
- Day 1: corpus picked, all 3 chunkers running on real data
- Day 2: hybrid retrieval + rerank + guardrail on text queries
- Day 3: STT wired in, full voice loop, benchmark harness running
- Day 4: README polish, architecture diagram image, demo video,
#RAGInGoa