A Streamlit chatbot that answers Canadian study-permit questions by retrieving from scraped Government-of-Canada pages and generating with Mixtral-8x7B-Instruct.

Architecture

Architecture

As an international student applying for a Canadian study permit, I spent hours hunting across IRCC pages to answer questions that should take thirty seconds. A naive LLM hallucinates wrong answers on legal content; a retrieval-augmented pipeline grounds the answer in the actual IRCC text. I scraped the Student Direct Stream (SDS) documentation, chunked it, embedded it into a FAISS index, and served a LangChain stuff QA chain over a minimal Streamlit UI, with Mixtral-8x7B-Instruct as the generator.

Problem

The Government of Canada's Student Direct Stream documentation is spread across four long pages (overview, eligibility, how to apply, after you apply). For an applicant, the answer to a single question ("what bank drafts are accepted?", "which countries are SDS-eligible?", "what happens after I submit biometrics?") usually lives buried inside one specific section of one specific page. Ctrl+F works if you know the exact phrase IRCC used. Most applicants don't.

A naive LLM is worse than Ctrl+F for this task: it will confidently hallucinate an answer, and for legal/immigration content, a hallucinated answer is actively harmful. "ChatGPT told me" is not a defence when an officer refuses your application.

A retrieval-augmented setup keeps the model grounded: it retrieves the actual IRCC chunk before generating, so the answer stays anchored to the source text. I wanted to see how far a pure-retrieval approach could get without any fine-tuning.

Retrieval vs fine-tuning

The choice was between fine-tuning a model on IRCC Q&A pairs or building a retrieval pipeline over the raw pages. I picked retrieval for three reasons:

  1. IRCC rules change. Eligibility country lists and processing times get updated. A fine-tuned model freezes the information at training time; a retrieval system is as fresh as the most recent scrape.
  2. No Q&A pairs existed. Synthesising Q&A pairs from government prose with another LLM and then fine-tuning on those pairs accumulates error at every step. Retrieval skips the synthesis step entirely.
  3. Hallucination risk is worse for fine-tuned models on legal text. A fine-tuned model generates confidently even when the question is out-of-distribution. Retrieval at least gives the generator a chance to say "the retrieved context doesn't cover this".

The tradeoff: retrieval needs a good chunking strategy, and chunking is harder than the tutorials suggest.

Why each piece of the stack

Why FAISS over Pinecone / Weaviate / Chroma. Local and free. The corpus is four pages; the entire embedded index fits in a few megabytes of RAM. FAISS builds in memory on startup, searches in microseconds, and keeps the project dependency-light. Past a few thousand chunks, a managed vector DB starts to make sense.

Why Mixtral-8x7B-Instruct over GPT-3.5 / GPT-4. Three reasons: (1) it topped the HuggingFace open instruct leaderboard at the time, (2) it's open-weight, so the project is reproducible without a paid OpenAI key, and (3) the goal was to learn the HuggingFace Hub serving path. Mixtral's mixture-of-experts architecture (8 experts, 2 active per token) gives GPT-3.5-class instruction following at a much lower effective compute cost. Temperature is 0.2 — low enough to keep the model pinned to the retrieved context, high enough to phrase answers naturally.

Why HuggingFace Hub over the OpenAI API. Using the HF Hub forces engagement with inference endpoints, rate limits, tokeniser alignment, and model selection — the model-serving layer the OpenAI SDK abstracts away. The free tier also keeps the project zero-cost.

Why Streamlit over Flask / FastAPI. The product is "a text box and an answer" — no routes, auth, or database models needed. Streamlit gives a UI for free, re-runs on input, and deploys in one command.

Why sentence-transformers/all-MiniLM-L6-v2 for embeddings. 384-dim, fast on CPU, good enough for short-passage retrieval on English government prose. Retrieval was already returning the right IRCC chunks on test queries, so benchmarking against MPNet, BGE, or E5 would have been premature optimisation.

Data collection