MAMATHA EPILI / ENGINEERING & AI
Selected work

RAG system2026Prototype

Gita Guidance

Retrieval-only by design, so nothing can invent scripture

Stack

Frontend
  • Angular 22 standalone components
  • TypeScript
  • Devanagari, IAST, Hindi, English
  • Speaker-separated results
  • /lab transformer visualiser
  • Per-head attention and causal mask
Backend & Data
  • Python, FastAPI, Pydantic v2
  • BAAI/bge-m3 via sentence-transformers
  • One vector per language, max-pooled
  • 700-verse corpus, licence per field
  • Canonical chapter:verse addressing
  • Character-level GPT in PyTorch
  • BM25 baseline evaluation harness

Try it here

Recorded run — no key, no backend

Retrieval needs bge-m3 — a 2.2 GB model — so it cannot run on a static page. This is the project’s own demo: real questions and the verses the pipeline returned for them, replayed with no network calls and no key.

Open the demo in its own tab Served from mamta-epili.github.io, sandboxed.

Problem

Someone typing “I am stressed, guide me” wants the verse that speaks to it, and two obvious approaches both fail. Keyword search cannot help: the word “stressed” appears nowhere in the 700 verses. A generative model can, and that is worse — the one failure a product like this must not have is inventing scripture and attributing it to a text people hold sacred.

What I built

  • A 700-verse corpus in Sanskrit (Devanagari and IAST), Hindi and English, addressed by canonical chapter:verse, carrying licence metadata on every field.
  • Multilingual dense retrieval with bge-m3: one vector per language per verse, max-pooled, so a Hindi question matches the Hindi rendering without the reader having to declare which language they are asking in.
  • Speaker-aware ranking. Every verse is labelled by who speaks it, so the answer comes from Krishna and Arjuna’s lament is shown separately rather than competing with it.
  • Exchange detection on top of those labels: where a Krishna verse directly follows an Arjuna one, both are rendered as a question and its answer — something the retriever cannot know, because it scores verses one at a time.
  • A rule-based router that runs before embedding: 47 patterns across English, romanised Hindi and Devanagari, answering questions that express despair from a hand-chosen verse plan instead of from similarity search.
  • An evaluation harness comparing dense retrieval against a BM25 baseline over in-corpus and out-of-corpus questions, and a routing test suite of 33 cases — 18 that must route, 15 that must not.
  • A character-level transformer built from scratch in PyTorch, and a `/lab` page that draws it working: next-character probabilities, attention per head, and the causal mask as a picture.

Technical decisions & trade-offs

  • Nothing on the answer path generates text. The shloka, its translations and its citation all come verbatim from the corpus, so the failure mode of a fabricated verse is structurally absent rather than prompt-mitigated.
  • The router runs before the embedding call, not after retrieval. Similarity search matches despair beautifully to verses that are actively wrong to show someone in that state — 2:22 likens dying to changing clothes — so that query is never embedded at all.
  • Routing replaces the ranking; it does not refuse the question. Refusing was a deliberate non-goal — the Gita exists to answer exactly this — so the curated path substitutes a hand-chosen plan for a ranked one rather than closing the door.
  • The router’s tests assert both directions. Fifteen cases exist to prove ordinary distress does not over-route: a product that treats “my husband and I argue constantly” as a crisis is useless to the person asking.
  • Exchange pairing only claims what the labels support. It stops at the first Krishna verse it crosses, so of 573 Krishna verses just 18 qualify — and 18:58, which sits 57 verses into an unbroken monologue, is never captioned as a reply to a question nobody asked.
  • Hindi translations are copyrighted and therefore not in the repository. What is committed is the public-domain subset — the same 700 verses, enough for the app to run — so the licence boundary is enforced by what exists in git rather than by a note asking people to be careful.
  • The transformer is kept in the repository and documented as unused. Its context window is 256 characters, which one verse plus a short question fills completely, leaving no room to answer in — a negative result worth writing down, since the alternative is a reader assuming a model sits on the answer path.

Challenges solved

  • Embedding similarity matched questions to text that resembles a question — which in a dialogue is Arjuna asking, not Krishna answering. The fix was to label the corpus by speaker and rank on it, not to tune the query.
  • There is no refusal path, and measurement says a score threshold will not supply one: the best out-of-corpus question scores 0.56 against a median answerable 0.52, so ask about cryptocurrency and the same openness returns five verses with a straight face.
  • The evaluation set is 16 questions — 9 answerable, 7 not — against a target of 60, and the retrieval figures are over those 9, so one verse moves them 11 points. Indicative, and recorded that way rather than quoted as a result.
  • A working prototype rather than an MVP: two local terminals and a 2.2 GB model download. Written down as the gap it is.

Outcome

A prototype that answers the question it was built for, and a finding I would not have got from a diagram: attention is retrieval — query against keys, weighted sum of values — and the RAG is that same operation with an external corpus and a top-k in place of the softmax.

Permalink: https://www.mamathaepili.com/projects/gita-guidance