Case study
CPAL
Canvas Personal Assistant for Learning: a RAG chatbot that answers Canvas LMS support questions with concise, cited responses.
- Python
- RAG
- LLMs
- FastAPI
- Pinecone (Vector Database)
- React
- TypeScript
- Go
- PostgreSQL
- Docker
See it work
Answers you can verify.

The frustration
Students and instructors click through a dozen Canvas documentation pages to answer one simple question. Every answer technically exists, but finding it means knowing which guide, which forum thread, which page.
The dig
Every Canvas question already had an answer, buried somewhere in the Instructure Community guides and forum threads. The friction wasn't missing information; it was finding the right answer quickly. With the right context, an LLM could make trusted answers much faster to find, so I built a RAG system over the sources people already trust.
The feature
Ask CPAL a Canvas question in plain language. It rewrites your question for better recall, searches an index of documentation and solved threads, and answers concisely with citations back to the original sources, so you can verify instead of trust.
How it works
Question in. Cited answer out.
- 1
Extract
A Go web scraper crawls Canvas Community guides and solved forum threads, turning them into clean Markdown with metadata.
- 2
Embed
Python chunks the corpus, generates embeddings, and stores them with metadata in Pinecone.
- 3
Retrieve
FastAPI calls an LLM to rewrite the question for better recall, runs similarity search, and reorders results to favor forum answers over questions.
- 4
Answer
The LLM synthesizes a concise Markdown answer with source links. Every Q&A is logged to Postgres for later LLM evals.
Architecture
The pieces.
Backend
FastAPI serves the API and the built frontend from one container. CAPTCHA-guarded query endpoint.
Frontend
Vite, React, and TypeScript. Ask, stream, verify: sources rendered with every answer.
Retrieval
Pinecone vector store, MiniLM embeddings, top-k search with score filtering.
LLM
Gemini for query rewriting and answer synthesis over retrieved passages.
Data pipeline
Go extractor to Python chunking to embedding generation to vector upsert. Run once to bootstrap the index.
Ops
Q&A events logged to Postgres for quality review. Single-container deploy on Fly.io.
Receipts
See for yourself.
The full source, pipeline, backend, frontend, and deploy config, is on GitHub. CPAL is archived now: Instructure redesigned the Community site and broke the extractor's assumptions. The architecture stands as a reference for how I design RAG systems.