1. System Architecture
Blueprints a RAG-based SaaS for generating quizzes and summaries from uploaded documents using a decoupled Next.js, FastAPI, and Supabase stack.
What this file does
Blueprints a RAG-based SaaS for generating quizzes and summaries from uploaded documents using a decoupled Next.js, FastAPI, and Supabase stack.
When to use it
- Building a document Q&A or study tool with vector search
- Designing a multi-service architecture with separate frontend, backend, and AI layers
- Implementing a RAG pipeline with local embeddings and streaming LLM responses
- Structuring a monorepo for a Next.js + FastAPI project
Assumes this stack
Here is the complete system architecture and design document for the NotebookLM-inspired SaaS application.
This document serves as the foundational blueprint for implementation.
1. System Architecture
The application follows a decoupled client-server architecture, separating the UI layer, processing backend, and data/storage layer to ensure scalability and cost-efficiency.
- Frontend (Next.js on Vercel): Handles routing, state management, UI rendering, and streaming LLM responses. Communicates securely with the backend via REST and Server-Sent Events (SSE).
- Backend (FastAPI on Railway/Fly.io): Handles heavy compute: document parsing, chunking, embedding generation, vector search, and LLM orchestration.
- Data & Auth Layer (Supabase):
- Auth: Supabase Authentication (JWT-based).
- Database: PostgreSQL with
pgvectorfor relationship mapping and semantic search. - Storage: Supabase Storage (S3-compatible) for retaining raw user-uploaded documents.
- AI Layer:
- LLMs: OpenRouter API (Mixtral 8x7B, DeepSeek Coder, or Llama-3 depending on the task).
- Embeddings: Hosted locally on the FastAPI backend using
sentence-transformers(e.g.,BAAI/bge-m3ornomic-embed-text) to achieve zero API costs for embeddings.
2. Database Schema (PostgreSQL + pgvector)
-- Workspaces: Logical grouping of documents and chats
CREATE TABLE workspaces (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Sources: Uploaded study materials (Max 20 per workspace enforced at app layer)
CREATE TABLE sources (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
filename VARCHAR(255) NOT NULL,
file_type VARCHAR(50) NOT NULL, -- pdf, docx, txt, md, url
storage_path TEXT, -- Path in Supabase Storage
status VARCHAR(50) DEFAULT 'processing', -- processing, ready, failed
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Document Chunks: Vector store for RAG
CREATE TABLE document_chunks (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
source_id UUID REFERENCES sources(id) ON DELETE CASCADE,
content TEXT NOT NULL,
metadata JSONB, -- Stores page_number, chunk_index, section_title
embedding VECTOR(1024) -- Dimension depends on embedding model
);
-- Create HNSW index for fast vector search
CREATE INDEX ON document_chunks USING hnsw (embedding vector_cosine_ops);
-- Chats: Conversation threads within a workspace
CREATE TABLE chats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
title VARCHAR(255) DEFAULT 'New Chat',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Messages: Chat history
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chat_id UUID REFERENCES chats(id) ON DELETE CASCADE,
role VARCHAR(50) NOT NULL, -- user, assistant
content TEXT NOT NULL,
citations JSONB, -- Array of document_chunk IDs or source metadata
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Quizzes: Generated quizzes
CREATE TABLE quizzes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Quiz Questions
CREATE TABLE quiz_questions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
quiz_id UUID REFERENCES quizzes(id) ON DELETE CASCADE,
question TEXT NOT NULL,
options JSONB NOT NULL, -- ["A", "B", "C", "D"]
correct_answer VARCHAR(1) NOT NULL,
hint TEXT,
explanation TEXT,
source_citation JSONB -- Reference to chunk or source
);
3. RAG Pipeline Design (Ingestion Workflow)
When a user uploads a document, the backend triggers an asynchronous pipeline:
- Upload & Storage: File is uploaded directly to Supabase Storage via signed URL or proxied through the backend.
sourcestable is updated tostatus: processing. - Extraction:
- PDF:
PyMuPDF(fitz) orpdfplumberto extract text and page numbers. - DOCX:
python-docxfor paragraphs. - URLs:
TrafilaturaorBeautifulSoupfor main article content.
- PDF:
- Chunking:
- Use LangChain's
RecursiveCharacterTextSplitter. - Chunk size: ~800 tokens, Overlap: ~150 tokens.
- Crucial: Append document name and page number to the top of each chunk before embedding, so the model never loses context of where the chunk came from.
- Use LangChain's
- Embedding: Generate vector embeddings using the backend Python model (e.g.,
sentence-transformers). - Vector Storage: Batch insert text chunks, metadata (page #, source_id), and embeddings into
document_chunks. - Completion: Update
sourcestablestatus: ready. Notify frontend via WebSockets/polling.
4. Vector Search Workflow (Retrieval)
When a user asks a question, generates a summary, or requests a quiz:
- Query Formulation: (Optional but recommended) Route the user's raw chat query through a lightweight LLM to rewrite it into a standalone, search-optimized query.
- Query Embedding: Convert the formulated query into an embedding vector using the same local Python embedding model.
- Similarity Search: Execute a
pgvectorsimilarity search (<=>cosine distance) ondocument_chunksfiltered by the currentworkspace_id. - Top-K Retrieval: Retrieve the top 10-15 most relevant chunks.
- Context Assembly: Format the retrieved chunks into a structured text string. Example:
[Source: Biology_Notes.pdf | Page: 4] <chunk text> - LLM Generation: Inject the context and the user query into the system prompt. Stream the response back to the frontend, enforcing citation formatting (e.g., "Mitochondria is the powerhouse [1]").
5. Folder Structure
/quizme-monorepo
│
├── /frontend # Next.js Application
│ ├── /app
│ │ ├── (auth) # Login / Register
│ │ ├── dashboard # Workspace list
│ │ ├── workspace/[id] # The 3-panel UI
│ │ └── api # Next.js route handlers
│ ├── /components
│ │ ├── layout # 3-panel layout components
│ │ ├── chat # Chat UI & streaming
│ │ ├── sources # Uploader & source list
│ │ └── studio # Quiz & Summary UI
│ ├── /lib # Utils, Supabase client
│ └── /store # Zustand state (workspace, chat history)
│
├── /backend # FastAPI Application
│ ├── /app
│ │ ├── /api # Route definitions
│ │ ├── /core # Config, auth middleware
│ │ ├── /services # RAG, LLM, Extraction logic
│ │ ├── /models # Pydantic & SQLAlchemy schemas
│ │ └── /utils # Chunking, text parsers
│ ├── main.py # Entry point
│ └── requirements.txt
│
└── /docker # Optional: Docker-compose for local dev
6. API Endpoints (FastAPI)
Workspaces & Sources:
GET /api/workspaces- List user's workspacesPOST /api/workspaces- Create new workspaceGET /api/workspaces/{workspace_id}/sources- List uploaded materialsPOST /api/workspaces/{workspace_id}/sources/upload- Upload file & trigger pipelineDELETE /api/sources/{source_id}- Delete a source & cascade delete chunks
Interaction (RAG):
POST /api/chat/completions- Acceptsworkspace_idandmessages. Returns StreamingResponse (SSE).GET /api/chats/{chat_id}/messages- Fetch chat history.
Studio Tools:
POST /api/studio/summary- Triggers summary generation (can be synchronous or SSE).POST /api/studio/quiz- Generates quiz payload. Requires structured JSON response from LLM.
7. Prompt Engineering Templates
1. Chat with Sources (Strict RAG)
System:
You are an expert AI learning assistant. Your task is to answer the user's question based strictly on the provided <context> retrieved from their study materials.
Rules:
1. Do not use outside knowledge. If the answer is not in the context, say "I cannot find the answer to this in your uploaded documents."
2. Cite your sources using the source ID or Document Name and Page Number provided in the context blocks. Format citations like this: [DocName, Page X].
3. Be concise, objective, and clear.
<context>
{context_blocks}
</context>
2. Quiz Generation (Structured JSON)
Using OpenRouter's structured output / JSON mode (e.g., via instructor library)
System:
You are a master educator. Generate a multiple-choice quiz based on the provided <context>.
You must output a valid JSON array of objects. Do not output markdown code blocks.
Generate {num_questions} questions.
Rules for each question:
1. The question must test deep understanding, not just trivia.
2. Provide exactly 4 options labeled "A", "B", "C", "D".
3. The correct_answer must be the letter only.
4. The hint should nudge the user without giving away the answer.
5. The explanation must explain WHY the answer is correct using the text.
6. Provide the source_citation exactly as it appears in the context header.
<context>
{context_blocks}
</context>
Expected JSON Schema:
{
"questions": [
{
"question": "string",
"options": {"A": "string", "B": "string", "C": "string", "D": "string"},
"correct_answer": "string",
"hint": "string",
"explanation": "string",
"source_citation": "string"
}
]
}
(Note: Hint and Explanation are generated concurrently to save context window and API calls, rather than doing separate calls for hints).
3. Document Summary
System:
You are an expert summarizer. Provide a comprehensive summary of the provided <context> documents.
Format your response strictly as follows:
## Overview
[1-2 paragraphs giving a high-level summary]
## Key Concepts
* [Concept 1]: [Brief explanation]
* [Concept 2]: [Brief explanation]
...
## Structured Outline
1. [Topic]
- [Subtopic]
2. [Topic]
- [Subtopic]
Ensure all points are grounded in the provided text.
<context>
{context_blocks}
</context>
8. Recommended Libraries & Frameworks
Frontend
- Framework: Next.js 14+ (App Router)
- State Management: Zustand (perfect for managing the 3-panel state without prop-drilling)
- Data Fetching: SWR or React Query
- Styling: Tailwind CSS + ShadCN UI + Radix UI primitives
- AI Streaming: Vercel AI SDK (
npm i ai) - specificallyuseChatfor the center panel andstreamTextfor summaries. - PDF Rendering (optional):
react-pdfif you want to show the actual PDF in the left panel.
Backend
- Framework: FastAPI (Python 3.11+)
- Database ORM:
supabase-py(for direct API interactions) orSQLAlchemy+psycopg2for raw pgvector queries. - LLM Orchestration:
Instructor(for enforcing strict JSON outputs for Quizzes) +OpenAIpython SDK (configured with OpenRouter base URL). - Document Parsers:
PyMuPDF(fitz): Fastest and most reliable for PDFs.python-docx: For Word documents.Trafilatura: For scraping clean text from URLs.
- Embeddings:
sentence-transformers(runs locally, completely free). - Chunking:
langchain-text-splitters(you only need this specific package, avoid pulling in the whole bloated LangChain framework).
What's inside
8 sections: architecture diagram, database schema, RAG pipeline, vector search, folder structure, API endpoints, prompt templates, library recommendations
Change this for your project
- Replace
quizme-monorepowith your project's monorepo name - Replace
BAAI/bge-m3ornomic-embed-textwith your chosen embedding model - Replace
Mixtral 8x7B, DeepSeek Coder, or Llama-3with your LLM model IDs - Replace
Biology_Notes.pdfin the context assembly example with a generic placeholder
Where it goes
Reference documentation for a retrieval pipeline. Keep with the ingestion or retrieval code it describes.
Worth borrowing
- Appending document name and page number to each chunk before embedding to preserve provenance
- Using a lightweight LLM to rewrite user queries into search-optimized queries before embedding
- Generating hints and explanations concurrently in a single LLM call to save tokens
Related Documents
SUMMARY
Proposes three on-prem AI architectures, modular, hybrid, and fully local RAG, with hardware specs and vendor lists.
Retrieval & Prompts
Explains how CharMemory's extraction prompt and Vector Storage settings determine memory retrieval quality in SillyTavern.
App Review Support Guide — Switch2Go
Explains an AAC app's accessibility permissions, hardware needs, and reviewer walkthrough to pass App Store review.
RFC-BLite: High-Performance Embedded Document Database for .NET
Specifies an embedded document database for.NET with zero-allocation I/O, C-BSON format, and ACID transactions.