AI tutoring platform
Tutorbin: course-scoped chat and RAG over uploaded materials
Tutorbin is a private Next.js tutoring platform. Each course is a workspace with its own chat threads, uploaded materials, study aids, and syllabus calendar. Students ask questions; the app retrieves from pgvector on every turn instead of answering from generic model knowledge. After months of daily use, I audited 906 production messages across biology, chemistry, and philosophy to find where retrieval actually failed.
Next.js / Postgres / pgvector / OpenRouter / Coolify
Problem
Build a tutoring assistant that answers from uploaded course materials rather than generic model knowledge, across biology, chemistry, and philosophy. The product needed course workspaces, reliable document ingestion, streaming chat, study aids, and calendar context that all pull from the same retrieval stack.
Product shape
Each course is a workspace with many URL-addressable chat threads. Students upload PDFs, DOCX, and PPTX; files are chunked, embedded via OpenRouter, and stored in pgvector. Every user message triggers retrieval from ready documents plus optional web context, voice style samples, and syllabus calendar events.

RAG pipeline
Ingestion runs through a bounded queue (max two concurrent jobs). Extraction uses Poppler for PDFs, mammoth for DOCX, officeparser for PPTX. Chunks land in document_chunks with cosine retrieval per turn: distance threshold, filename keyword boost, and source labels in the system prompt.


Beyond chat
Study aids generate from chat context plus course RAG: quizzes, flashcards, practice tests, and adaptive follow-ups from missed items. Desmos graph cards and persisted Leonardo illustrations extend replies without indexing generated images back into RAG.


Production audit
Once Tutorbin was in daily use, I exported the production database and analyzed 906 chat messages across biology, chemistry, and philosophy courses. The goal: find where RAG failed, where the model looped, and where product behavior needed tightening.
- Questions that should have pulled from ingested material were retrieving weak or unrelated chunks instead, silently, with no signal to the student that the answer missed.
- Streaming would sometimes loop until I killed the process. That became a repetition guard and clearer retry copy in the UI.
- Cross-course contamination when thread and course boundaries weren't enforced. A data shape problem, not a model problem.
- Ingestion gaps: .ppt legacy files rejected without notice, a handful of documents stuck in processing indefinitely.
Outcomes
- Course workspaces with URL-addressable threads, document ingestion, and streaming chat across multiple subjects.
- Study aids and syllabus calendar built on the same RAG core as tutoring chat.
- Retrieval filtering and logging tightened after the audit. RAG misses now show up in ops logs instead of disappearing silently.
- Added a repetition guard to streaming so loops abort cleanly instead of running until I notice.
- Ingestion status is inline now. Students see whether their documents are ready before expecting answers from them.
