MAMATHA EPILI / ENGINEERING & AI
Selected work

RAG system2026Open source

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

How it works

Flowchart: A question in English, Hindi or Devanagari first meets 47 rule patterns; a match such as despair answers from a hand-chosen verse plan. Otherwise the question is embedded with bge-m3 and matched against one vector per language per verse, then ranked speaker-aware so Krishna’s answer leads. Every verse is shown with its chapter:verse address — retrieval only, nothing invented.YesNoQuestion asked80.5English · Hindi ·DevanagariMatches apattern?93.547 rulesVerse plan80.5hand-chosen, e.g.despairEmbed question208.5bge-m3 · FastAPIDense retrieval202.5one vector per language,max-pooled700-verse corpus324.5Sanskrit · Hindi ·EnglishSpeaker-aware ranking202.5Krishna leads, ArjunaseparateVerses shown208.5cited chapter:verse
A question in English, Hindi or Devanagari first meets 47 rule patterns; a match such as despair answers from a hand-chosen verse plan. Otherwise the question is embedded with bge-m3 and matched against one vector per language per verse, then ranked speaker-aware so Krishna’s answer leads. Every verse is shown with its chapter:verse address — retrieval only, nothing invented.

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 both obvious approaches fail. Keyword search cannot: “stressed” appears nowhere in the 700 verses. A generative model can, and that is worse — inventing scripture and attributing it to a sacred text is the one failure this must not have.

What I built

  • A 700-verse corpus in Sanskrit (Devanagari and IAST), Hindi and English, addressed by canonical chapter:verse, with 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 being told the language.
  • Speaker-aware ranking: every verse is labelled by who speaks it, so Krishna’s answer leads and Arjuna’s lament is shown separately.
  • A rule-based router ahead of embedding: 47 patterns across English, romanised Hindi and Devanagari, answering despair from a hand-chosen verse plan rather than from similarity.
  • A character-level transformer written from scratch in PyTorch, and a `/lab` page drawing it work: next-character probabilities, per-head attention, the causal mask.

Technical decisions & trade-offs

  • Nothing on the answer path generates text. Verse, translations and citation all come verbatim from the corpus, so a fabricated verse is structurally impossible rather than prompt-mitigated.
  • The router runs before the embedding call. Similarity matches despair to verses that are wrong to show someone in that state — 2:22 likens dying to changing clothes — so that query is never embedded at all.
  • Its tests assert both directions: fifteen cases prove ordinary distress does not over-route. A product that reads “my husband and I argue constantly” as a crisis is useless to the person asking.
  • Exchange pairing claims only what the labels support — 18 of 573 Krishna verses qualify, so 18:58, sitting 57 verses into a monologue, is never captioned as a reply.
  • Hindi translations are copyrighted, so only the public-domain subset is committed. The licence boundary is enforced by what exists in git, not by a note asking people to be careful.

Challenges solved

  • There is no refusal path, and measurement says a threshold will not supply one: the best out-of-corpus question scores 0.56 against a median answerable 0.52. Ask about cryptocurrency and it returns five verses.
  • The evaluation set is 16 questions — 9 answerable, 7 not — against a target of 60, and the figures are over those 9, so one verse moves them 11 points. Indicative, and recorded that way.
  • Not deployed: localhost, two terminals, a 2.2 GB model download. A real limit on who can try it, listed rather than left to be discovered.

Outcome

Open source under MIT, and carrying a finding no diagram would have given me: attention is retrieval — query against keys, weighted sum of values — and the RAG is that operation with an external corpus and a top-k instead of the softmax.

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