An Advanced Retrieval-Augmented Generation (RAG) system built over video course SRT and VTT subtitle files.
Ask a natural-language question about the course and get an answer that cites the exact lesson name and timestamp — so students can jump straight to the right moment in the video.
Based on the "GenAI with JavaScript 2026" course. The links in the task (Eraser design board & the course lesson) are login-protected, so this repo implements the standard Advanced RAG architecture that those lessons describe.
┌──────────────────────────────────────────────────────────────┐
│ INGESTION PIPELINE │
│ │
.srt │ parse cues ─► clean/dedupe ─► chunk (w/ overlap) ─► embed │
.vtt │ (parser.js) (chunker.js) (llm.js) │
│ │ │
│ ▼ │
│ Qdrant vector store (vectorStore.js) │
│ payload = { lesson, source, startTs, endTs, text } │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ QUERY PIPELINE │
│ question │
│ │ │
│ ├─► Parallel Query Fan-Out ─┐ │
│ ├─► HyDE ├─► Reciprocal Rank Fusion ─► │
│ └─► Step-Back Prompting ───┘ (patterns.js) │
│ │ │
│ ▼ │
│ LLM answer w/ lesson + timestamp cites │
│ (answer.js) │
└──────────────────────────────────────────────────────────────┘
| Pattern | What it does |
|---|---|
| Parallel Query Retrieval (Fan-Out) | LLM rewrites the question into several diverse queries, searches each, then fuses. |
| Reciprocal Rank Fusion (RRF) | Merges multiple ranked result lists into one robust ranking. |
| HyDE | Generates a hypothetical answer and searches with it for better semantic recall. |
| Step-Back Prompting | Asks a broader conceptual question to pull in foundational context. |
.
├─ data/ # put your .srt / .vtt course files here
├─ src/
│ ├─ config.js # env + settings
│ ├─ llm.js # OpenAI embeddings + chat helpers
│ ├─ parser.js # SRT/VTT -> timestamped cues
│ ├─ chunker.js # cues -> chunks (lesson + timestamp metadata)
│ ├─ vectorStore.js # Qdrant collection + upsert + search
│ ├─ ingest.js # ingestion CLI
│ ├─ patterns.js # advanced RAG retrieval strategies
│ ├─ answer.js # grounded answer generation w/ citations
│ └─ chat.js # query CLI / REPL
├─ docker-compose.yml # local Qdrant
├─ .env.example
└─ package.json
- Node.js 18+
- Docker (for Qdrant) — or a hosted Qdrant Cloud URL
- An OpenAI API key
npm install
Copy-Item .env.example .env
# then edit .env and set OPENAI_API_KEYnpm run qdrant:upDownload the SRT/VTT attachments from the course lesson and drop them into data/.
File names become lesson titles, e.g. 01 - Intro to RAG.en.vtt → lesson "Intro to RAG".
(Two sample files are already included so you can test immediately.)
npm run ingest # add/update
npm run ingest -- --reset # wipe collection and re-index# interactive
npm run chat
# one-off
node src/chat.js "How does reciprocal rank fusion work?"
# choose a strategy: all | parallel | hyde | stepback | simple
node src/chat.js --strategy hyde "What is HyDE?"Reciprocal Rank Fusion merges the ranked results of several search queries into a
single list, giving robustly relevant chunks (Lesson: "Chunking Subtitles", 00:00:42).
References:
- "Chunking Subtitles" @ 00:00:42–00:00:51 (02 - Chunking Subtitles.srt)
- "Introduction to RAG" @ 00:00:55–00:01:04 (01 - Introduction to RAG.en.vtt)
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
— | Required. |
CHAT_MODEL |
gpt-4o-mini |
Chat model. |
EMBEDDING_MODEL |
text-embedding-3-small |
Embedding model. |
EMBEDDING_DIM |
1536 |
Must match the embedding model. |
QDRANT_URL |
http://localhost:6333 |
Qdrant endpoint. |
QDRANT_COLLECTION |
course_subtitles |
Collection name. |
DATA_DIR |
./data |
Where subtitle files live. |
CHUNK_CUES |
12 |
Cues merged per chunk. |
CHUNK_OVERLAP |
2 |
Overlapping cues between chunks. |
- Works with auto-generated and manual subtitles; rolling-caption duplicates in auto VTT are de-duplicated automatically.
- Any OpenAI-compatible endpoint works by pointing the SDK at a different base URL.
- Swap Qdrant for another vector DB by re-implementing
src/vectorStore.js.
MIT