← All projects

Senior project · 2025 - 2026

AI Career Intelligence Platform

A full-stack platform that reads your CV, extracts your skills, matches you to roles, and shows you exactly which skills you are missing.

Watch demoView code ↗
Next.jsTypeScriptNestJSPythonFastAPIPostgreSQLOllamaHugging Face

Overview

Job seekers rarely get a straight answer to the most useful question: given what I can already do, which roles am I actually close to, and what is the shortest path to the ones I am not? Job boards list requirements, but nobody tells you how far away you are from meeting them.

This platform answers that. You upload a CV, it extracts the skills you can evidence, matches them against role requirements, and returns the specific gaps standing between you and each role - then turns those gaps into an ordered learning roadmap you can work through.

It was built as my senior project across three separate services: a Next.js frontend, a NestJS backend, and a Python FastAPI service handling everything AI-related. Splitting the AI work into its own service meant the Python ecosystem's model tooling stayed where it belongs, and the backend stayed a clean TypeScript API.

Features

CV upload and skill extraction

Uploaded CVs are parsed server-side - pdf2json for PDFs, mammoth for DOCX - and run through keyword extraction using word-boundary regex so that matching a skill name inside a longer word does not produce a false positive. Each user is limited to one active CV, so the skill profile always reflects a single source of truth.

CV upload and skill extraction

Digital Twin

The dashboard centrepiece is an SVG visualization that positions each skill as a node using trigonometry, distinguishing skills verified from the CV from skills the target role requires but the candidate is missing.

Digital Twin

Job matching and missing skills analysis

Extracted skills are compared against role requirements using sentence embeddings from Hugging Face's all-MiniLM-L6-v2 model with cosine similarity, so a match does not depend on exact wording. The result is not just a score: the missing skills are returned as an explicit list, which is what actually tells you what to do next.

Job matching and missing skills analysis

Growth roadmap

Those missing skills feed a growth roadmap that orders them into phases, with progress persisted so the roadmap survives between sessions.

Growth roadmap

Personalized career recommendations

Once the skill profile and target role are known, the recommendations module suggests roles the candidate is genuinely close to and orders the missing skills into a phased growth roadmap rather than a flat list. Phase order is persisted as JSONB in PostgreSQL, so progress survives between sessions and the roadmap picks up where it was left.

Personalized career recommendations

Cover letter generation

Generates a cover letter for a target role from the candidate's own extracted skills and experience rather than a generic template, so the letter references what is actually on the CV. The generated letter is stored against the user, so it can be revisited and reused instead of regenerated from scratch each time.

Cover letter generation

Role-based interview questions

Generates interview questions tailored to the specific role a candidate is targeting, then holds a conversational interview session so answers can be rehearsed in context rather than read off a list. Generation runs on Llama 3 through Ollama, which keeps inference free and means CV content never leaves the machine.

Role-based interview questions

AI interview simulator

Generates interview questions tailored to the target role and holds a conversational interview session, so candidates can rehearse against the specific job they are applying for. Generation runs on Llama 3 through Ollama, which keeps inference free and means CV content never leaves the machine.

AI interview simulator

How it works

Challenges

A hydration error caused by floating-point maths in SVG

The Digital Twin dashboard threw a React hydration mismatch that only appeared once the skill nodes rendered. The coordinates were computed with Math.cos and Math.sin, and the server and the browser produced values that differed in the last few decimal places - enough for React to consider the markup different and bail out. Rounding the computed coordinates with toFixed(4) before they reached the DOM made both sides agree. It took a while to find because the error pointed at the component, not at the arithmetic inside it.

Keeping three services agreeing on identity

With a Next.js frontend, a NestJS backend and a Python service all in play, the session had to mean the same thing everywhere. Early on, foreign keys mismatched between UUID and integer types across services, and NextAuth v5 cookie decryption failed when the backend tried to read a session it had not issued. The fix was to stop trying to decode the session in two places: the backend forwards the cookie to a dedicated verification endpoint on the frontend and trusts the answer, so there is exactly one component that understands the session format.

← Back to all projects