SEBI Trading Compliance Assistant
A RAG-based assistant that answers questions about Indian securities trading regulations (SEBI margin rules) and F&O taxation, grounded in real regulatory circulars and tax reference material — not the model's general knowledge.
Live demo: https://frontend-omega-six-90.vercel.app
Backend API: https://sebi-trading-assistant.onrender.com
Note: the backend runs on Render's free tier, which spins down after inactivity. The first request after a period of no traffic may take 30-60 seconds to respond while it wakes up.
The problem
Indian retail traders need to navigate margin rules, F&O taxation treatment, STT rates, and tax audit thresholds — information that exists in official SEBI circulars and tax guidance, but is scattered across dense PDFs and rarely explained together. This assistant answers questions directly from a curated set of these documents, citing its sources, and explicitly declines to answer when a question falls outside what its documents cover — rather than guessing.
Architecture
- Frontend: Next.js, deployed on Vercel
- Backend: FastAPI, deployed on Render
- Embeddings:
sentence-transformers/all-MiniLM-L6-v2, run locally on the backend via fastembed (ONNX Runtime, no PyTorch) — free, no API key, and small enough for a 512 MB free-tier server. The same implementation (scripts/embeddings.py) is used for ingestion and querying, so index and query vectors always match
- Vector store: FAISS (default corpus index committed to the repo; per-session indexes for uploaded documents held in memory)
- LLM: GPT-OSS 20B (
openai/gpt-oss-20b) via Groq, through LangChain's chat-model interface, streamed token-by-token
- Orchestration: LangChain (document loading, chunking, retrieval, prompt construction)
Key design decisions
- Guardrail-first prompting: the system prompt explicitly instructs the model to answer only from retrieved context and to decline when the context is insufficient, rather than guessing. This was deliberately tested (see Evaluation below) against both thin-context and fully out-of-scope questions.
- Source attribution: every answer is returned alongside the specific document(s) it was grounded in, so a user can verify the answer rather than trust it blindly.
- Scoped corpus as a deliberate MVP decision: the knowledge base currently covers 7 documents (SEBI peak margin circulars, F&O taxation rules, tax audit thresholds, STT rates) rather than the full regulatory corpus. The retrieval/generation pipeline is corpus-agnostic and would scale to a larger document set without architectural changes — this scope was chosen to validate the approach end-to-end rather than to be comprehensive.
- Two kinds of decline: a question whose closest match in the corpus is too distant (e.g. weather, stock tips) gets a specific "outside what I cover" reply listing the topics the assistant does cover, without calling the LLM at all. The distance cutoff was calibrated on the eval set: answerable questions scored ≤ 1.14 and clearly unrelated ones ≥ 1.26, so it sits at 1.25. Borderline questions (related topic, but the detail isn't in the documents) still reach the LLM, which replies "I don't have enough information in my documents…". The cutoff only applies to the default corpus, not uploaded PDFs.
- Conversation memory for follow-ups: the last 3 Q&A turns are sent with each question. A follow-up like "what about options?" is first rewritten into a standalone question ("How is STT charged on options?") by a small, low-reasoning LLM call so retrieval finds the right passages; the answer prompt also includes the recent turns.
- Session-based document upload (replace mode): a user can upload their own PDF and ask questions about it directly. Rather than merging the uploaded document into the default corpus, the system switches retrieval to search only the uploaded document for the duration of that session — a deliberate choice for predictability: the user always knows exactly what the assistant is drawing from. Session state (the uploaded document's vector index) is held in memory on the backend, keyed by a per-browser-session ID.
Evaluation
Measured on a hand-labeled, 20-question test set spanning all 7 source documents plus deliberately out-of-scope questions:
| Metric | Result |
|---|
| Fully correct | 18/20 (90%) |
| Partially correct (right facts, minor omission) | 2/20 (10%) |
| Incorrect / hallucinated | 0/20 (0%) |
| Out-of-scope questions correctly declined | 4/4 (100%) |
Full question set and results: eval/eval_questions.json, eval/scorecard.md
Known limitations
- Free-tier backend hosting means a cold-start delay after inactivity (see note above)
- Document corpus is intentionally scoped to 7 documents (see Key design decisions)
- The out-of-scope distance cutoff is calibrated for the current 7-document corpus and should be re-checked if the corpus changes significantly
- Conversation memory lives in the browser tab only (last 3 turns); refreshing the page starts a new conversation
- Uploaded documents are stored in-memory per session and are not persisted — they're lost on a backend restart or redeploy. A production version would use a proper per-user storage layer (e.g., Redis or a database-backed vector store) instead.
- Responses are streamed token-by-token for a faster perceived response time, but the frontend also paces the visual reveal independently of network speed, since Groq's inference speed can otherwise make the streaming effect imperceptible for short answers.
Running locally
Backend:
pip install -r requirements.txt
# add GROQ_API_KEY to a .env file
python scripts/ingest.py # one-time: build the vector index from docs/
uvicorn app:app --reload
Frontend:
cd frontend
npm install
# add NEXT_PUBLIC_API_URL=http://127.0.0.1:8000 to .env.local
npm run dev
Tech stack
Python, FastAPI, LangChain, FAISS, Hugging Face Inference API, Groq, Next.js, React