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
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.