The Definitive Master Report: Combining P1 (API & Data), P2 (AI & LangGraph), and P3 (Infrastructure & Fine-Tuning) Handoffs + The 4 Optimization Pillars
Project Version:v0.1.0
Target Audience: New developers joining the project, team members reviewing system flows, or owners revising individual components.
- Project Overview & Core Objective
- Architectural Breakdown & Team Roles
- System Flow & Data Lifecycle
- Complete API Surface & Endpoint Reference
- Database Schemas & Persistence Layer
- Caching & Vector DB Infrastructure
- The 4 Backend & System Optimization Pillars
- Codebase Walkthrough by Component
- Step-by-Step Setup & How-to-Run Guide
- Troubleshooting & Maintenance Checklist
Smart-Educator is an automated, AI-driven educational platform designed to turn raw educational documents (PDFs, text passages, curricula) into structured, high-quality assessment datasets.
- Document Ingestion & Chunking: Streams PDF/TXT documents asynchronously and chunks them for LLM processing.
- Automated Learning Outcome (LO) Extraction: Uses Google Gemini to analyze context and extract explicit, verifiable Learning Outcomes (
text,concept,source_evidence). - Multi-Type Question Generation: Generates Multiple-Choice Questions (MCQs), True/False, and Short Answer questions aligned with specific LOs and difficulty distributions.
- Semantic Question-to-LO Linking: Uses local embedding similarity (
sentence-transformers) to score question-to-outcome alignment with confidence scores and reasoning. - 8-Criteria LLM-as-Judge Evaluation: Scores generated questions across 8 strict criteria (grounding, clarity, answer correctness, explanation correctness, LO alignment, difficulty, validity, choices) and categorizes items as
accepted,needs_review, orrejected. - Clean Dataset Export: Filters approved assessment data for downstream usage.
- Training Pair Construction & Fine-Tuning: Generates positive, negative, and Jaccard-based hard-negative pairs to fine-tune embedding models using
CosineSimilarityLoss. - Vector Similarity Recommendation: Uses ChromaDB and Gemini embeddings to perform semantic search and recommend relevant questions for user queries.
The system is organized into three decoupled, complementary layers:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI REST API Layer β
βββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββ
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββ
β P1: API, Data & Export Layer β β P2: AI & LangGraph Pipeline β
β - FastAPI Routes & Request Schemas β β - 5-Node State Machine Pipeline β
β - Document Ingestion & Async Chunking β β - Google Gemini Structured Output β
β - PostgreSQL Async Engine & Alembic β β - SBERT Question-to-LO Linker β
β - CRUD Layer & Dataset Export Endpoint β β - 8-Criteria LLM-as-Judge Evaluator β
βββββββββββββββββββββββ¬ββββββββββββββββββββββ βββββββββββββββββββββββ¬ββββββββββββββββββββββ
β β
βββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β P3: Infrastructure, Vector DB & ML Tuning β
β - Docker Compose (Postgres, Redis, ChromaDB) β
β - Redis Caching Layer (Idempotency & Embeddings) β
β - VectorStoreService (ChromaDB + text-embedding-004) β
β - Pair Generator & SentenceTransformerTrainer β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Layer | Primary Responsibilities | Key Files / Modules |
|---|---|---|
| P1: API & Data | Endpoints (/health, /data/*, /dataset/export), File Ingestion, PostgreSQL ORM, Async CRUD Operations |
routes/data.py, routes/dataset.py, controllers/, models/, helpers/db.py |
| P2: AI Pipeline | LangGraph Orchestration, Gemini API Wrapper, LO Extraction, Question Generation, Semantic Linker, LLM-as-Judge Evaluator | graph/graph.py, services/gemini_client.py, services/lo_extraction.py, services/question_generator.py, services/lo_linker.py, services/evaluator.py |
| P3: Infra & ML | Docker Stack, Redis Cache, ChromaDB Vector DB, Training Pair Generator, Embedding Model Fine-Tuning | docker/docker-compose.yml, helpers/redis_client.py, helpers/hashing.py, services/embedding_service.py, stores/vector/vector_store.py, services/pair_generator.py, training/fine_tune.py |
- User uploads a PDF or TXT file to
POST /data/upload/{project_id}. DataControllervalidates format and file size limit, generates a unique filename, and writes chunks asynchronously usingaiofiles.- User triggers
POST /data/process/{project_id}.ProcessControlleroffloads heavy PyMuPDF/TXT parsing to a threadpool (asyncio.to_thread) to prevent blocking the event loop, splitting text into structured chunks (RecursiveCharacterTextSplitter).
flowchart TD
A[EducationalContext Payload] --> B[Compute Context Hash: sha256 passage + config]
B --> C{Check Redis Cache}
C -->|Cache Hit| D[Return Cached JSON Instantly - 2ms]
C -->|Cache Miss| E[LangGraph 5-Node State Machine]
E --> E1[Node 1: parse_node]
E1 --> E2[Node 2: extract_outcomes_node via Gemini]
E2 --> E3[Node 3: generate_questions_node via Gemini]
E3 --> E4[Node 4: link_outcomes_node via SBERT]
E4 --> E5[Node 5: evaluate_node via LLM-as-Judge]
E5 --> F{Evaluate Batch Status}
F -->|All Rejected| G[log_and_discard_node: Log & Skip DB Write]
F -->|Accepted / Needs Review| H[store_node: Save LOs, Questions, Links, Evals to PostgreSQL]
H --> I[Store Result JSON in Redis - TTL 24h]
I --> J[Return Response Summary JSON]
-
Export (
GET /dataset/export): Queries PostgreSQL for questions with statusacceptedorneeds_review, matches them with their LOs and evaluation scores, and exports clean JSON arrays. -
Pair Building (
POST /training-pairs/build): Converts DB records intoQuestionWithLOobjects, executesgenerate_pairs()to produce positive, negative, and hard-negative pairs, and writesassets/training_pairs.jsonl. -
Fine-Tuning (
training/fine_tune.py): TrainsSentenceTransformer(paraphrase-multilingual-mpnet-base-v2) on pairs usingCosineSimilarityLoss. -
Semantic Search (
POST /recommendations/questions): Converts user search query to a 768-dim embedding (text-embedding-004), queries ChromaDB, and returns top$K$ matching questions.
| Method | Path | Summary | Inputs | Output |
|---|---|---|---|---|
GET |
/api/v1/health |
Service health status | None | {"status": "ok", "version": "0.1", "app": "smart-educator"} |
POST |
/api/v1/data/upload/{project_id} |
Upload passage file | file: UploadFile |
{"signal": "...", "file_id": "..."} |
POST |
/api/v1/data/process/{project_id} |
Chunk uploaded file | ProcessRequest(file_id, chunk_size, overlap_size) |
Array of text chunk objects |
POST |
/api/v1/dataset/generate |
Run full AI pipeline | EducationalContext JSON payload |
Summary counts (learning_outcomes_created, questions_created, etc.) |
POST |
/api/v1/dataset/evaluate |
Re-evaluate DB questions | EvaluateRequest(question_ids: Optional[list]) |
{"questions_evaluated": N, "status_breakdown": {...}} |
GET |
/api/v1/dataset/export |
Export clean dataset | Query param: difficulty (optional) |
Array of PRD Β§8.6 flat dataset records |
POST |
/api/v1/training-pairs/build |
Generate training pairs | None | {"total_pairs": N, "positive_pairs": X, "negative_pairs": Y, "hard_negative_pairs": Z} |
POST |
/api/v1/recommendations/questions |
Vector similarity search | RecommendationRequest(query, top_k, difficulty) |
{"query": "...", "recommendations": [...]} |
PostgreSQL schema auto-creates on FastAPI startup via create_tables() in main.py.
βββββββββββββββββββββββββββ
β learning_outcomes β
βββββββββββββββββββββββββββ€
β id (UUID, PK) β
β text (String) β
β concept (String) β
β source_evidence (String)β
β context_hash (Indexed) β
ββββββββββββββ²βββββββββββββ
β
β (via QuestionLOLink)
β
ββββββββββββββ΄βββββββββββββ
β question_lo_links β
βββββββββββββββββββββββββββ€
β id (UUID, PK) β
β question_id (FK) ββββββββΌβββββββββββ
β lo_id (FK) β β
β confidence (Float) β β
β reason (String) β β
βββββββββββββββββββββββββββ β
β
βΌ
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β evaluation_results β β questions β
βββββββββββββββββββββββββββ€ βββββββββββββββββββββββββββ€
β id (UUID, PK) β β id (UUID, PK) β
β question_id (FK) ββββββββΌββββββββββββββββββ€ question_text (String) β
β 8 Criteria Scores (0-1) β β question_type (String) β
β overall_score (Float) β β choices (JSON) β
β status (Enum String) β β correct_answer (String) β
βββββββββββββββββββββββββββ β explanation (String) β
β difficulty (String) β
β estimated_time_minutes β
β related_LO_ids (JSON) β
β source_evidence (String)β
βββββββββββββββββββββββββββ
- Pipeline Cache (
dataset_generate:<sha256_hash>): Caches complete/dataset/generateoutput payloads for 24 hours (REDIS_TTL=86400). - Embedding Cache (
embedding:<sha256_text_model>): Caches 768-dim Googletext-embedding-004vectors to minimize API usage.
- Stores question text vectors in collection
"questions". - Performs
$L_2$ / Cosine distance nearest-neighbor search (search(query_embedding, top_k)).
The backend codebase incorporates four production optimization pillars:
βββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
β 1. Pydantic v2 Guardrails β βββΊ β 2. SQLAlchemy 2.0 Queries β
β Field & Model Validators β β Bulk WHERE id IN & Flush β
βββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
β 3. Deep Async Python β βββΊ β 4. Clean API Contracts β
β Parallel asyncio.gather β β Global Exception Middlewareβ
βββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββ
- File: educationalContext.py
- Enforces multi-field validation rules automatically on incoming HTTP requests:
QuestionConfig: Validates thatmcq_count + true_false_count + short_answer_count > 0.DifficultyDistribution: Validates thateasy + medium + hard == 1.0.
- Key Advantage: FastAPI invokes
@model_validatorautomatically under the hood, instantly returning an HTTP 422 error on invalid inputs before hitting LLMs or the DB.
- File: questions.py
- Replaced
$N$ single queries in a loop with a single bulk queryget_questions_by_ids(db, question_ids)using SQLWHERE id IN (...). - Uses
await db.flush()beforeawait db.commit()insave_questions()to populate generated UUID keys in memory prior to transaction completion. - Uses
.distinct()on JOIN queries to avoid duplicate rows during clean dataset export.
- Files: dataset.py and training_pairs.py
- Replaced sequential Await calls with
asyncio.gather(get_accepted_questions(db), get_all_outcomes(db))to execute independent database queries concurrently in parallel, cutting route latency in half. - Offloaded heavy synchronous PyMuPDF file parsing in
ProcessController.pyto a threadpool viaasyncio.to_thread.
- File: main.py
- Added global exception handlers for
HTTPException,RequestValidationError(422), and unhandledException(500). - Prevents database connection details, file paths, or internal tracebacks from leaking to API callers, returning standardized
{ "success": False, "error": ... }JSON contracts.
src/
βββ main.py β App entry point, lifecycle startup, global exception middleware
βββ .env β Local settings (DB URL, Gemini API Key, Redis, Chroma)
β
βββ helpers/
β βββ config.py β BaseSettings schema with LRU cache & fallbacks
β βββ db.py β Async SQLAlchemy engine, Base, and get_db session dependency
β βββ redis_client.py β Async Redis client with get_cached() and set_cached()
β βββ hashing.py β SHA256 context hasher
β
βββ models/ β SQLAlchemy ORM Table Definitions
β βββ learningOutcome.py
β βββ questions.py
β βββ questionL0Link.py
β βββ evaluationResult.py
β
βββ routes/ β API Routers
β βββ base.py β GET /health
β βββ data.py β POST /upload, POST /process
β βββ dataset.py β POST /generate, POST /evaluate, GET /export
β βββ training_pairs.py β POST /build
β βββ recommendations_questions.py β POST /questions
β
βββ controllers/
β βββ CRUD_Operations/ β Async DB Operations
β β βββ learning_outcomes.py
β β βββ questions.py
β β βββ question_lo_links.py
β β βββ evaluation_results.py
β βββ DataController.py β Upload validation and unique filename generation
β βββ ProcessController.py β Async PDF/TXT loader and character text chunking
β βββ ProjectController.py β File path management
β
βββ services/ β Core AI & ML Services
β βββ gemini_client.py β Gemini SDK wrapper with retries & structured output
β βββ lo_extraction.py β Extract LOs using Gemini
β βββ question_generator.py β Generate MCQs/TF/Short Answer using Gemini
β βββ lo_linker.py β Local SBERT semantic similarity linker
β βββ evaluator.py β 8-criteria LLM-as-Judge evaluator
β βββ pair_generator.py β Positive / Negative / Hard-Negative pair builder
β βββ embedding_service.py β Text embedding service (Redis-cached)
β
βββ graph/
β βββ graph.py β LangGraph 5-node orchestration pipeline
β
βββ stores/vector/
β βββ vector_store.py β VectorStoreService wrapping ChromaDB HttpClient
β
βββ training/
βββ fine_tune.py β SentenceTransformerTrainer fine-tuning logic
Copy .env.example to .env in src/.env and verify settings:
APP_NAME="smart-educator"
APP_VERSION="0.1"
GEMINI_API_KEY="your_actual_gemini_api_key"
DATABASE_URL="postgresql+asyncpg://postgres:postgres@localhost:5432/smart_educator"
REDIS_HOST="localhost"
REDIS_PORT=6379
CHROMA_HOST="localhost"
CHROMA_PORT=8000
REDIS_TTL=86400docker compose -f docker/docker-compose.yml up -d
docker compose -f docker/docker-compose.yml pspip install -r src/requirements.txt
β οΈ IMPORTANT: Always run commands from the project root directory (so relative.envresolution works).
python -m uvicorn main:app --app-dir src --reload --port 8000- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
| Symptom / Error | Root Cause | Solution |
|---|---|---|
ValidationError: Field required on startup |
Executing python commands from inside src/ directory |
Execute commands from the project root: python -m uvicorn main:app --app-dir src |
Redis connection error / ConnectionRefusedError |
Docker Redis container is offline | Run docker compose -f docker/docker-compose.yml up -d |
Cannot connect to Postgres on port 5432 |
Local PostgreSQL instance conflict or container down | Ensure container is healthy via docker compose ps |
| Fast API Event Loop freezes during PDF upload | Synchronous file loader called directly | Wrap loader calls using await asyncio.to_thread(loader.load) |
google-generativeai import errors |
Deprecated Gemini SDK | Use from google import genai (google-genai package) |
Document Status: Master Architecture Report Complete & Verified (
v0.1.0).