Qwen Global AI Hackathon 2026 — Track 1 (MemoryAgent) Submission
MentorOS is a full-stack AI mentor that remembers every student's learning journey and continuously generates personalized career recommendations, skill-gap analysis, project ideas, and learning roadmaps — powered by long-term semantic memory.
| Name | Role |
|---|---|
| Muhammad Bilal Hussain | Team Lead · Backend · AI · Memory Engine |
| Noor Fatima | Presentation · UI/UX · Documentation · Testing |
- Features
- Architecture
- Tech Stack
- Project Structure
- Prerequisites
- Step-by-Step Setup
- Environment Variables Reference
- API Reference
- How the Memory Engine Works
- Deployment to Vercel
- Testing
- Troubleshooting
- Not Yet Built
| Feature | Description |
|---|---|
| Auth | Register, login (JWT),GET /auth/me, POST /auth/refresh (silent session renewal). Login triggers a memory decay pass. |
| Student Data | Profile, Skills, Projects, Certificates, Career Goals — full CRUD, scoped per user. New Career Goals automaticallysupersede the previous one (preserved, not deleted). |
| Resume Upload | PDF /.txt → Qwen structured extraction → automatically populates domain tables + memory. Zero disk writes — fully in-memory processing. |
| Memory Engine | Every fact is embedded, stored in a vector DB, and tracked in SQL with an importance score. Retrieval uses cosine similarity re-ranked by importance. Two forgetting mechanics:supersession (contradiction) and decay (time-based archival on login). Gracefully degrades if the embedding API is unavailable — memory is saved in SQL and retrieval returns empty results. |
| AI Recommendations | Roadmap, skill-gap analysis, and project ideas — all grounded in retrieved memory via Qwen, and written back into memory. |
| Feature | Description |
|---|---|
| Pages | Login, Register, Dashboard, Profile, Resume Upload & Analysis, Skills, Projects, Certificates, Memory Timeline, AI Recommendations, Career Goals, Settings. |
| Dashboard | Summary cards (skills/projects/certificates/goal counts), memory statistics pie chart, skill-level bar chart, resume status, AI recommendation panel, recent-activity timeline, quick-action buttons. |
| Auth | JWT stored client-side, Axios request interceptor attaches token automatically. Two-layer refresh: proactive (silent refresh ~2 min before expiry) + reactive (Axios interceptor catches 401, retries once). |
| Dark / Light | Theme toggle vianext-themes. |
| Responsive | Collapsible sidebar, mobile-friendly layout. |
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Next.js 15) │
│ App Router · TypeScript · Tailwind · TanStack Query · Axios │
│ 12 protected pages · JWT auth · Dark/Light theme │
└──────────────────────────┬──────────────────────────────────────┘
│ REST API (JSON)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend (FastAPI) │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ Routers │→│ Services │→│ Repositories (SQLAlchemy) │ │
│ │ (HTTP thin)│ │ (business) │ │ (all direct DB access) │ │
│ └──────────┘ └──────┬───────┘ └───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Memory Engine │ │
│ │ writer │ → embeds via Qwen API │
│ │ retriever │ → cosine similarity search │
│ │ importance │ → scoring + decay │
│ └───────┬────────┘ │
│
8000
│ │
│ ┌───────────┴───────────┐ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ PostgreSQL │ │ Vector Store │ │
│ │ + pgvector │ │ (auto-selected) │ │
│ │ (production) │ │ pgvector OR │ │
│ └──────────────┘ │ ChromaDB (local) │ │
│ └───────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Alibaba Cloud Qwen (OpenAI-compatible) │ │
│ │ qwen3.7-plus · text-embedding-v4 (1024d)│ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Layer | Technology |
|---|---|
| Backend | Python 3.11+, FastAPI, SQLAlchemy 2.0, Pydantic Settings |
| Database | SQLite (local dev) / PostgreSQL (production) |
| Vector Store | ChromaDB (local) / pgvector extension (production) |
| AI / LLM | Alibaba Cloud Qwen (qwen3.7-plus for generation, text-embedding-v4 for embeddings) |
| Auth | JWT (PyJWT + bcrypt), 12-hour token expiry |
| Frontend | Next.js 15 (App Router), TypeScript, Tailwind CSS, shadcn/ui-style components |
| State | TanStack Query (server state), React Context (auth) |
| Charts | Recharts |
| Deployment | Vercel (frontend + backend serverless) |
mentoros/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI entry point, CORS, routers
│ │ ├── core/
│ │ │ ├── config.py # All env-driven settings (pydantic-settings)
│ │ │ └── deps.py # Dependency injection (get_current_user)
│ │ ├── db/
│ │ │ ├── session.py # SQLAlchemy engine, Base, get_db()
│ │ │ └── base.py # Imports all models for metadata discovery
│ │ ├── models/ # SQLAlchemy ORM models (9 models)
│ │ │ ├── user.py, profile.py, skill.py, project.py
│ │ │ ├── certificate.py, career_goal.py, resume.py
│ │ │ └── memory.py, ai_insight.py
│ │ ├── schemas/ # Pydantic request/response schemas
│ │ ├── repositories/ # All direct database access (4 files)
│ │ ├── routers/ # Thin HTTP handlers (9 routers)
│ │ │ ├── auth.py, profile.py, skills.py, projects.py
│ │ │ ├── certificates.py, career_goals.py
│ │ │ ├── resume.py, memory.py, recommendations.py
│ │ ├── services/ # Business logic (6 files)
│ │ │ ├── auth_service.py # Register, authenticate, issue token
│ │ │ ├── student_data_service.py # CRUD + memory writes for all entities
│ │ │ ├── resume_service.py # Upload → extract → apply (in-memory)
│ │ │ ├── recommendation_service.py # AI recommendation generation
│ │ │ ├── memory_service.py # Timeline, delete, login decay
│ │ │ └── memory_writer_service.py # Coordinates all memory writes
│ │ ├── memory_engine/ # Persistent memory system
│ │ │ ├── vector_store.py # Unified interface (auto-selects backend)
│ │ │ ├── pgvector_backend.py # Production: pgvector + embeddings table
│ │ │ ├── chroma_backend.py # Local dev: ChromaDB file-based storage
│ │ │ ├── writer.py # Writes facts to memory (SQL + vector)
│ │ │ ├── retriever.py # Cosine similarity + importance re-ranking
│ │ │ ├── importance.py # Scoring math (initial, decay, boost, composite)
│ │ │ └── decay.py # Login-triggered decay/archival pass
│ │ └── ai/
│ │ ├── llm_provider.py # Abstract LLM interface
│ │ ├── qwen_client.py # Qwen API implementation
│ │ ├── prompts.py # System prompts for extraction
│ │ └── reasoning_prompts.py # Prompts for AI recommendations
│ ├── requirements.txt # Python dependencies
│ ├── vercel.json # Vercel deployment config
│ ├── .env.example # Documented env template (18 settings)
│ └── tests/
│ └── test_api.py # Comprehensive test suite
├── frontend/
│ ├── app/
│ │ ├── layout.tsx # Root layout (ErrorBoundary + Providers)
│ │ ├── login/page.tsx
│ │ ├── register/page.tsx
│ │ └── (app)/ # Protected route group (auth guard)
│ │ ├── layout.tsx # Sidebar + Topbar shell
│ │ ├── dashboard/page.tsx
│ │ ├── profile/page.tsx
│ │ ├── skills/page.tsx
│ │ ├── projects/page.tsx
│ │ ├── certificates/page.tsx
│ │ ├── career-goals/page.tsx
│ │ ├── resume/page.tsx
│ │ ├── memory/page.tsx
│ │ ├── recommendations/page.tsx
│ │ └── settings/page.tsx
│ ├── components/
│ │ ├── ui/ # shadcn-style primitives
│ │ ├── layout/ # Sidebar, Topbar
│ │ ├── dashboard/, skills/, memory/, ...
│ │ └── common/error-boundary.tsx
│ ├── hooks/ # TanStack Query hooks (one per domain)
│ ├── lib/
│ │ ├── api/ # Axios service modules (1:1 with backend)
│ │ │ ├── client.ts # Axios instance + token interceptor
│ │ │ ├── auth.ts, profile.ts, skills.ts, projects.ts
│ │ │ ├── certificates.ts, careerGoals.ts, resume.ts
│ │ │ ├── memory.ts, recommendations.ts
│ │ └── auth-context.tsx # Auth state + proactive token refresh
│ ├── types/index.ts # TypeScript types (mirrors Pydantic schemas)
│ ├── package.json
│ ├── next.config.mjs # API proxy rewrites, image patterns
│ ├── tailwind.config.ts
│ └── .env.local.example
└── README.md
- Python 3.11+ and pip
- Node.js 18+ and npm
- An Alibaba Cloud Qwen API key (get one here)
- (Optional) A PostgreSQL database for production — Neon free tier works great
git clone https://github.com/your-org/mentoros.git
cd mentoroscd backend
python -m venv venvActivate the virtual environment:
# macOS / Linux
source venv/bin/activate
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# Windows (cmd)
venv\Scripts\activate.batInstall dependencies:
pip install -r requirements.txtCreate your .env file:
cp .env.example .envEdit .env — minimum required settings:
# Required: your Qwen API key
QWEN_API_KEY=sk-your-qwen-api-key
# Required: change this to a random secret (any long string works)
JWT_SECRET_KEY=any-random-string-at-least-32-chars
# Required on Vercel, optional locally:
FRONTEND_URL=http://localhost:3000Everything else has sensible defaults. See Environment Variables Reference for the full list.
Start the backend:
uvicorn app.main:app --reloadThe backend runs at http://localhost:8000.
- API docs (Swagger):
http://localhost:8000/docs - Health check:
http://localhost:8000/health
The first startup creates
mentoros.db(SQLite) automatically. No database setup required for local dev.
Open a new terminal (keep the backend running):
cd frontend
npm installCreate your .env.local file:
cp .env.local.example .env.localBy default it points to http://localhost:8000 — no changes needed for local dev.
Start the frontend:
npm run devThe frontend runs at http://localhost:3000.
- Open
http://localhost:3000in your browser - Register a new account
- Log in — you'll land on the Dashboard
- Fill out your profile, add skills/projects, upload a resume
- Visit the Memory Timeline to see persistent memory in action
- Click "Generate" in Recommendations to get AI-powered guidance
| Variable | Default | Description |
|---|---|---|
ENV |
development |
development or production |
DEBUG |
true |
Enable debug mode |
FRONTEND_URL |
http://localhost:3000 |
Frontend origin for CORS |
DATABASE_URL |
sqlite:///./mentoros.db |
Database connection string |
JWT_SECRET_KEY |
CHANGE_ME_IN_ENV |
Required. Secret for signing JWTs. |
JWT_ALGORITHM |
HS256 |
JWT signing algorithm |
ACCESS_TOKEN_EXPIRE_MINUTES |
720 |
Token lifetime (12 hours) |
QWEN_API_KEY |
"" |
Required. Your Alibaba Cloud Qwen API key. |
QWEN_BASE_URL |
https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
Qwen API endpoint (usedashscope.aliyuncs.com for China region) |
QWEN_MODEL |
qwen3.7-plus |
Model for chat generation |
QWEN_EMBEDDING_MODEL |
text-embedding-v4 |
Model for text embeddings |
QWEN_EMBEDDING_DIMENSIONS |
1024 |
Embedding vector dimensions |
VECTOR_BACKEND |
auto |
auto / pgvector / chroma |
CHROMA_PERSIST_DIR |
./chroma_store |
ChromaDB storage path (local only) |
CHROMA_COLLECTION_NAME |
mentoros_memories |
ChromaDB collection name |
MEMORY_DECAY_RATE |
0.05 |
Importance lost per idle week |
MEMORY_ARCHIVE_THRESHOLD |
0.15 |
Below this → memory is archived |
MEMORY_RETRIEVAL_TOP_K |
8 |
Max memories retrieved per query |
How VECTOR_BACKEND=auto works:
DATABASE_URLstarts withpostgresql→ uses pgvector- Otherwise → uses ChromaDB (local file-based)
International vs China endpoint:
| Region | QWEN_BASE_URL |
|---|---|
| International | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
| China | https://dashscope.aliyuncs.com/compatible-mode/v1 |
Make sure your API key matches the endpoint region. An international key won't work with the China endpoint and vice versa.
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
http://localhost:8000 |
Backend API base URL |
All endpoints below require a JWT token via Authorization: Bearer <token> header, except register and login.
| Method | Endpoint | Body | Description |
|---|---|---|---|
POST |
/auth/register |
{ "email", "password" } |
Create account |
POST |
/auth/login |
{ "email", "password" } |
Get JWT token |
GET |
/auth/me |
— | Current user info |
POST |
/auth/refresh |
— | Extend current token |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/profile/me |
— | Get or create profile |
PUT |
/profile/me |
{ "full_name"?, "bio"?, "education"?, ... } |
Update profile |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/skills |
— | List all skills |
POST |
/skills |
{ "name", "level" } |
Add a skill |
DELETE |
/skills/{id} |
— | Remove a skill |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/projects |
— | List all projects |
POST |
/projects |
{ "title", "description"?, "url"?, ... } |
Add a project |
DELETE |
/projects/{id} |
— | Remove a project |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/certificates |
— | List all certificates |
POST |
/certificates |
{ "name", "issuer"?, "date"?, ... } |
Add a certificate |
DELETE |
/certificates/{id} |
— | Remove a certificate |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/career-goals |
— | List all goals (active + superseded) |
POST |
/career-goals |
{ "title", "description"? } |
Add goal (supersedes previous active) |
| Method | Endpoint | Body | Description |
|---|---|---|---|
GET |
/resume |
— | List uploaded resumes |
POST |
/resume/upload |
multipart/form-data (PDF or TXT) |
Upload → AI extraction → auto-populate profile + memory |
| Method | Endpoint | Description |
|---|---|---|
GET |
/memory/timeline |
All memories (active / superseded / archived), newest first |
DELETE |
/memory/{id} |
Delete a memory (SQL row + vector) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/recommendations/roadmap |
Personalized learning roadmap |
POST |
/recommendations/skill-gap |
Skill gap analysis |
POST |
/recommendations/projects |
Project ideas |
Note: If the embedding API is unavailable, recommendations still work but without memory context (the AI generates generic advice based on the student's profile). The system degrades gracefully — no 500 errors.
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Returns app name, env, database type, vector backend |
The memory engine is the core of the MemoryAgent track. Here's how it works:
Every time a student adds a skill, project, certificate, career goal, resume entry, or generates an AI recommendation, the system:
- Generates a natural-language "fact" string (e.g.,
"User added Python at level Advanced to their skills.") - Creates a SQL row (
memorytable) with importance score and metadata - Generates a 1024-dimensional embedding via
text-embedding-v4 - Stores the embedding in the vector backend (pgvector or ChromaDB)
If the embedding API fails, the SQL row is still saved — the memory is just not searchable by similarity until a retry.
When generating a recommendation, the system:
- Embeds the current student context (recent facts, skills, goals)
- Queries the vector store for the top-K most similar memories (cosine similarity)
- Re-ranks results by composite score =
importance × cosine_similarity - Injects retrieved memories as context into the Qwen prompt
If embedding or vector search fails, retrieval returns an empty list and the recommendation is generated without memory context.
| Mechanism | Trigger | What happens |
|---|---|---|
| Supersession | Contradictory data added (e.g., new career goal) | Old memory status →superseded, linked to its replacement |
| Decay | Student logs in | Memories loseMEMORY_DECAY_RATE importance per idle week. Below MEMORY_ARCHIVE_THRESHOLD → status → archived |
This gives the system realistic memory evolution — memories fade naturally unless reinforced.
vector_store.py (unified interface)
├── pgvector_backend.py ← auto-selected when DATABASE_URL = postgresql
│ (stores embeddings in a PostgreSQL table via pgvector extension)
└── chroma_backend.py ← auto-selected when DATABASE_URL = sqlite
(stores embeddings in a local ChromaDB directory)
- Push
backend/to a Git repository (or deploy as a subdirectory) - Connect to Vercel — it auto-detects
@vercel/pythonviavercel.json - Set environment variables in Vercel Dashboard (same as
.env— see reference) - Key production settings:
DATABASE_URL→ PostgreSQL connection string (e.g., Neon, Supabase, or RDS)ENV=productionFRONTEND_URL→ your Vercel frontend URL (e.g.,https://mentor-os.vercel.app)JWT_SECRET_KEY→ a secure random stringQWEN_BASE_URL→ use the international endpoint unless your server is in China
- Deploy —
vercel.jsonalready hasmaxDuration: 30for slower cold starts
- Connect
frontend/to Vercel - Set
NEXT_PUBLIC_API_URLto your backend's Vercel URL - Deploy
The backend automatically:
- Allows
localhost:3000andlocalhost:3001for local dev - Allows
FRONTEND_URLfrom env - Allows
https://*.vercel.appvia regex pattern - Accepts additional origins via
CORS_ORIGINSenv var (comma-separated)
cd backend
python -m pytest tests/ -vThe test suite (tests/test_api.py) covers:
- Auth flow — register, login,
/auth/me,/auth/refresh - CRUD for all entities — skills, projects, certificates, career goals, profile
- Memory engine — write, retrieve (cosine similarity + importance ranking), supersession, decay/archival, cross-user isolation
- Resume upload — PDF extraction, in-memory processing, auto-population
- CORS — preflight headers, origin validation
- Edge cases — unauthorized access, empty fields, career goal supersession
Also includes a standalone memory-engine test harness using a deterministic fake embedding provider — 18 assertions covering write, retrieval ranking, supersession, decay, and isolation.
cd frontend
npm run build # catches TypeScript errors
npm run lint # catches lint issuesAll 14 routes build with zero TypeScript errors. Every type in types/index.ts is verified field-by-field against the corresponding backend Pydantic schema.
| Problem | Solution |
|---|---|
ModuleNotFoundError: No module named 'X' |
Runpip install -r requirements.txt again. Make sure your venv is activated. |
pydantic_settings.errors.ValidationError |
Missing required.env values. Copy .env.example to .env and fill in QWEN_API_KEY and JWT_SECRET_KEY. |
sqlite3.OperationalError: no such table |
Normal on first run — tables are created automatically on startup. If it persists, deletementoros.db and restart. |
| Problem | Solution |
|---|---|
Module not found errors |
Runnpm install again. |
| API calls fail with CORS error | Make sure backend is running on port 8000. CheckFRONTEND_URL in backend/.env matches http://localhost:3000. |
NEXT_PUBLIC_API_URL not working |
It must be set asNEXT_PUBLIC_API_URL (not just API_URL). Restart the dev server after changing it. |
| Problem | Solution |
|---|---|
AccessDenied error on embeddings |
Your API key doesn't have access totext-embedding-v4. Check the Alibaba Cloud Model Studio console to enable the embedding model. Also verify you're using the correct endpoint for your region (international vs China). |
| "AI generation failed: 502" | Check yourQWEN_API_KEY is valid and has quota. Check QWEN_BASE_URL matches your region. |
| Recommendations are empty/generic | This usually means no memories exist yet (or the embedding API is unavailable). Add skills, projects, and career goals first — the AI uses these as context. Even without memory context, the AI still generates advice based on the student's profile. |
| Resume upload fails | Only PDF and TXT files are supported. Make sure the file isn't empty. |
| Vector write warnings in logs | Vector write failed for memory_id=X — memory saved in SQL only. This is normal if the embedding API is temporarily unavailable. The memory is saved in SQL and will be searchable once embeddings succeed. |
| Problem | Solution |
|---|---|
| Backend 502 on Vercel | Check Vercel function logs. Common causes: missing env vars,QWEN_API_KEY not set, cold start timeout. maxDuration: 30 is already configured. |
| Frontend can't reach backend | VerifyNEXT_PUBLIC_API_URL in Vercel env vars points to the correct backend URL. |
| Data disappears on Vercel | Expected — Vercel functions use ephemeral storage. Youmust use PostgreSQL (Neon, Supabase, etc.) for data to persist. |
- Exportable reports / analytics beyond dashboard charts
- Background task scheduler (decay runs on login by design — see
backend/app/memory_engine/decay.py) - Multi-user collaboration / sharing
- Mobile app