Inspiration# YojanaSaathi — the missing reasoning layer between India's schemes and its entrepreneurs

Inspiration

India runs hundreds of welfare and entrepreneurship schemes, and most of them fail at the same place — not design, but discovery. Eligibility is written in the language of policy ("SC micro-enterprise, category B"), while real people describe their lives in the language of life ("I stitch clothes from home and need money for a machine").

The moment that framed the whole project: a home tailor already qualifies for PM Vishwakarma's ₹15,000 toolkit and a 5% collateral-free loan — she just doesn't know the scheme exists, what it's called, or that she fits its criteria. The government did the hard part (funding the scheme). The citizen is left alone with the hardest part (finding it, understanding it, proving eligibility).

MyScheme.gov.in proved the problem is real — the government built it to solve exactly this. But it's a search-and-filter directory: it assumes you already know what to search for. Haqdarshak proved the market is real. What neither does well: meet a person in their own words, reason about their messy situation, and hand them a trustworthy next step. That's the gap YojanaSaathi fills.

What it does

YojanaSaathi is a conversational eligibility-matching system. A user describes their situation in plain language — work, location, community category, what they need — and gets back:

  • Ranked scheme matches with plain-language reasons ("why this fits you") tied to their own words
  • Confidence badges on every match — high / possible — never implied certainty
  • A generated document checklist and step-by-step application walkthrough with the official portal link
  • Clarifying questions (tappable chips) when the answers would change eligibility
  • And — the feature I'm most proud of — an honest "I don't know" when the data isn't there, instead of a confidently hallucinated match

It answers the doc's own test case: "I stitch clothes from home and need money for a machine" → PM Vishwakarma and MUDRA, with a 7-step checklist. And it politely refuses "I need ₹500 crore to start a rocket company" — because a wrong answer costs this user base real time, money, and trust.

How I built it

Architecture: user → single-file chat UI → FastAPI → slot extraction + hybrid retrieval → confidence-gated reasoning → structured JSON → cards and checklist.

  • Corpus: 15 real, manually curated schemes (PM Vishwakarma, MUDRA, Stand-Up India, PMEGP, PM SVANidhi, NRLM/NULM, Startup India Seed Fund, NSSH, PM-AJAY, CGTMSE, SFURTI, ASPIRE + TN NEEDS and Karnataka Udyogini as state layers) as versioned JSON — each with eligibility rules, benefits, documents, steps, portal
  • Retrieval: hybrid search — vector similarity + BM25 keyword scoring — so exact scheme names ("PMEGP") and fuzzy descriptions ("I stitch clothes") both work. ChromaDB when available, in-memory numpy fallback.
  • Understanding: a slot extractor pulls occupation, SC/ST/OBC/minority/women category, state (incl. city lookup: Bengaluru → Karnataka), loan amount ("1 lakh" → ₹100,000), business stage and SHG/group status from messy free text
  • Reasoning: a weighted, confidence-gated scoring engine with hard eligibility filters:

$$S = 3\,o + 2\,c + 1.5\,s + 1.5\,a + g + 2r + b - p$$

matching only if $S \geq 4$ and at least one structural eligibility fact fired (occupation, category, state, or amount), "high confidence" only at $S \geq 7$ — and category-restricted or out-of-state schemes get capped or zeroed. Below threshold, the system refuses to guess and asks questions instead.

  • LLM layer: behind the same strict JSON contract — Claude (tool-use) or Gemini (JSON mode) receives ONLY the retrieved schemes as context, and every model output is sanitized against the corpus: hallucinated scheme IDs are dropped, names and portals re-attached from source data. The model can never invent a scheme.
  • Demo resilience: with no API keys and no internet, it runs a deterministic offline rules engine on a free-tier 512MB–2GB host. The demo cannot die on stage.

Stack: Python · FastAPI · Pydantic · NumPy · BM25 · ChromaDB (optional) · BGE-M3 embeddings (optional) · Claude API (optional) · vanilla HTML/JS · Uvicorn.

Challenges I ran into

  1. Free-tier reality vs. ideal-stack ambitions. BGE-M3 (the right multilingual embedding model) needs ~2GB RAM — impossible on a 512MB free deploy. So embeddings, vector store, and LLM are all pluggable backends: a deterministic hashing embedder keeps the core experience alive offline; BGE-M3/ChromaDB/Claude turn on with env vars.
  2. Retrieval noise is dishonesty in disguise. Early on, "₹500 crore rocket company" matched SFURTI, and CGTMSE outranked MUDRA for a tailor. Fixes: a structural-signal gate (no eligibility fact, no match), amount-sanity penalties, an editorial priority_boost knob, and tightened occupation tags.
  3. Conversation memory that forgets correctly. Testers would answer "Artisan" once, then type "I am a startup founder" — and get artisan results, because every old answer stuck forever. I redesigned slot resolution to latest-message-wins for identity slots (occupation/category/state/amount) while keeping accumulated context for clarifying answers — so follow-ups work AND new descriptions override cleanly.
  4. Hallucination-adjacent UX bugs. The document checklist once mixed SFURTI's NGO cluster paperwork into an individual artisan's list. Now group schemes can never leak documents into individual checklists.
  5. The boundary between honest and useless. A strict refusal engine feels broken if its vocabulary is thin ("welder", "hotel", "paper plates" initially got refused). I expanded occupation aliases mapped onto canonical eligibility tags, added plural and typo tolerance ("tail,or" → tailor) — while keeping the refusal intact for genuinely unknown ground like "hello".

Accomplishments I'm proud of

  • "I'd rather ask than guess" is enforced in code, not hoped for in a prompt — the core trust feature survives even if the LLM hallucinates.
  • The LLM path cannot invent schemes, by construction (corpus-validated output).
  • It runs a full conversational demo on a ₹0 host with zero internet — no live API dependency on stage.
  • Real user testing during the build surfaced real bugs (slot contamination, checklist leakage) that are now regression-tested fixes.

What I learned

  • For high-stakes, trust-critical AI, reliability is an engineering property, not a prompt property — thresholds, gates, and sanitizers beat clever system prompts.
  • Honest refusal is a product feature with real UX weight, and designing for it (clarifying chips, confidence badges) is as important as the matcher itself.
  • Test transcripts are gold: the most valuable improvements came from pasting real user sessions and fixing what broke, not from benchmarks.
  • Constraints (free tier, offline, no keys) produced a better architecture than unlimited resources would have.

What's next for YojanaSaathi

  • Flip on the already-wired Claude integration for nuanced multi-turn reasoning
  • Hindi and regional-language intake (BGE-M3 embeddings already support it) + voice input for low-literacy users
  • Corpus growth to 100+ schemes with state layers for every state/UT, plus a human curation pipeline to keep scheme data current
  • Pilots through SHGs/NGOs (B2B2C) and district administrations (B2G), with an anonymized demand-signal dashboard for policymakers — "what are people actually asking for in District X"

Built With

python fastapi pydantic numpy bm25 chromadb claude-api bge-m3 vanilla-javascript uvicorn

Built With

  • bm25
  • chromadb
  • civic-tech
  • claude
  • conversational-ai
  • embeddings
  • fastapi
  • financial-inclusion
  • generative-ai
  • govtech
  • html
  • llm
  • natural-language-processing
  • numpy
  • pydantic
  • python
  • rag
  • semantic-search
  • social-impact
  • vanilla-javascript
  • vector-database
Share this project:

Updates

Submission history