CareerCompass AI is a local-first Streamlit platform that turns a resume into a market-aware career plan. It parses a candidate's profile, scans live job listings, ranks job fit through taxonomy-first semantic matching, surfaces skill gaps, builds a learning roadmap, generates tailored application documents, and runs voice-based mock interviews with scoring — all in one workflow.
Project status: Portfolio-ready local AI application. The core workflow is orchestrated through core/pipeline.py; LLM features run on Groq, skill intelligence runs on sentence-transformers, and agentic document generation runs on LangGraph/LangChain.
| Capability | User Outcome |
|---|---|
| Resume intelligence | Extracts contact info, validated skills, proficiency signals, achievements, seniority, and recommendations. |
| Market scan | Validates full schema.org postings, retains blocked pages as labelled partial snippets, and clearly labels fallback demo data. |
| Semantic job matching | Scores roles using skills, title similarity, and experience fit, with transparent score breakdowns. |
| Gap analysis | Clusters missing skills and weights them by market frequency and domain to prioritize what to learn next. |
| Learning roadmap | Converts gaps into dependency-aware learning paths with projects and resource links. |
| Mock interview | Combines Groq, Whisper, and EdgeTTS/gTTS with optional camera analytics for realistic interview practice and scoring. |
| Career documents | Tailors resumes and drafts company-aware cover letters through LLM and LangGraph workflows. |
- End-to-end career workflow: Covers analysis, matching, gap diagnosis, upskilling, documents, and interview practice in a single app.
- Multimodal AI experience: Text, voice, speech analytics, and optional webcam feedback combine into one interactive interview simulator.
- Provenance-aware recommendations: Verified pages feed market analytics; blocked snippets provide capped, provisional matches; partial and demo data are kept out of trend history.
- Evidence-based employer identity: Company names are recovered from the strongest available structured page, listing card, search title, snippet, page metadata, or public URL evidence, with source and confidence shown separately.
- Engineering depth: Shared pipeline orchestration, taxonomy-first matching, lazy model loading, runtime artifact hygiene, and test coverage make this more than a UI demo.
- Calm, responsive interface: A minimalist visual system, clear data hierarchy, mobile-safe layouts, sticky career-tool navigation, visible focus states, and reduced-motion support keep dense analysis readable.
- Recruiter-visible polish: Screenshots, dashboards, reports, cover letters, PDFs, and score explanations make the output tangible and easy to evaluate at a glance.
- Validates skills against a curated taxonomy, alias list, regex heuristics, and semantic similarity.
- Estimates proficiency from surrounding resume context, such as years of experience, seniority language, and project verbs.
- Extracts achievements, digital presence, and employment timelines; calendar working years remain a factual metric while career level uses a conservative, deterministic evidence model.
- Scores professional maturity from timeline (45%), role scope (25%), work-evidenced technology depth (20%), and demonstrated impact (10%).
- Links skills to dated employment evidence, professional duration, recency, and extraction confidence; general skill-list breadth cannot create seniority, while project/certification support is capped at five technical-depth points.
- Separates total working experience from target-role-relevant experience and reports readiness and hybrid role level for each selected role.
- Lets users correct contact details, employment records, role-specific skills, responsibilities, achievements, experience, and skills before refreshing matches, gaps, and the roadmap.
- Searches LinkedIn, JobStreet, Indeed, and Glassdoor via public listing discovery and DuckDuckGo dorking.
- Preserves richer duplicate metadata and recovers employer names without replacing known listing-card evidence with poorer search results.
- Fetches and validates complete schema.org job descriptions before ranking them.
- Falls back to blocked search snippets as capped, partial matches when full pages are inaccessible.
- Uses clearly labelled synthetic demo templates only when neither verified nor usable partial results are available.
- Scores jobs on a
skills(60%) /title(20%) /experience(20%) weighting; experience itself combines relevant years (50%), role scope (25%), and professional technology alignment (25%). - Returns matched skills, missing skills, score breakdowns, action items, and market gap severity.
- Builds prerequisite-aware learning paths from
data/skill_ontology.py. - Generates transfer-learning roadmap advice with deterministic fallback resources.
- Tailors resumes into ATS-oriented PDFs and writes researched, company-aware cover letters.
- Runs voice-based mock interviews with LLM scoring, speech metrics, and optional camera posture/eye-contact signals.
- Loads the optional WebRTC/MediaPipe camera runtime only after camera feedback is enabled, keeping the normal dashboard startup path lightweight.
A recruiter-style overview of extracted profile signals, target-role readiness, employment history, evidence-backed skills, completeness, and editable verification status.
Taxonomy-first skill extraction groups validated skills by domain and estimates proficiency from resume context.
Each job receives a weighted match score alongside matched skills, missing requirements, and recommended next actions.
The roadmap converts market gaps into a prioritized learning path complete with dependencies, projects, and resource links.
The LangGraph cover-letter flow researches company context before drafting a targeted application letter.
Interview sessions can be tuned by target role, focus area, and difficulty level.
The interviewer combines text, voice, scoring, and sample-answer feedback into a single practice workflow.
Voice answers are transcribed, evaluated, scored, and summarized into an end-of-session report.
- Python 3.10+
- Groq API key (free at console.groq.com) for LLM features
- Microphone for the mock interview feature
- Webcam (optional, for body language scoring)
Without GROQ_API_KEY, some deterministic/fallback paths still run, but roadmap generation, resume tailoring, cover letters, transcription, and interview questions all require Groq access.
Semantic matching is local-first. The app uses a cached all-MiniLM-L6-v2 snapshot when available and otherwise fails fast to deterministic taxonomy/lexical matching. To permit a one-time model download explicitly, set CAREERCOMPASS_ALLOW_MODEL_DOWNLOAD=1 before starting the app.
# 1. Clone the repository
git clone https://github.com/yixin16/CareerCompass-AI-Intelligent-Resume---Market-Gap-Analyzer.git
cd CareerCompass-AI-Intelligent-Resume---Market-Gap-Analyzer
# 2. Create and activate a virtual environment
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Set your Groq API key
# Option A — .env file (recommended)
echo "GROQ_API_KEY=gsk_your_key_here" > .env
# Option B — environment variable
set GROQ_API_KEY=gsk_your_key_here # Windows CMD
export GROQ_API_KEY=gsk_your_key_here # macOS/Linuxstreamlit run app.pyApp opens at http://localhost:8501
# Place your resume in sample_data/resumes/
# Edit config.py to set JOB_SEARCH_KEYWORDS and JOB_SEARCH_LOCATION
python main.py
# All outputs saved to output/pytest -qThe lightweight test suite uses dummy semantic matchers, so it never downloads or loads the transformer model.
- In the sidebar, enter your Groq API key (auto-loaded from
.envif set). - Upload your resume (PDF, DOCX, or TXT).
- Set your job keywords (e.g.,
Data Scientist, ML Engineer) and location. - Click 🚀 Analyze resume & market.
- Review the profile summary, skill distribution, strengths, and recommendations.
The profile dashboard distinguishes extracted information from information you have confirmed.
- Review the Profile Completeness score and its missing-data prompts.
- Inspect each employment record for title, employer, dates, duration, responsibilities, and evidenced skills.
- Compare total working years with the target-role readiness card for each keyword you selected.
- Open Review and correct extracted profile to amend contact details, total working years, employment entries, or skills.
- Save corrections to mark the profile as user-verified and immediately refresh job matches, skill gaps, the roadmap, and recommendations.
- Skill radar chart — visual breakdown of your skills by category.
- Market keyword word cloud — trending skills employers are seeking right now.
- Salary signals use the currency associated with the selected location; for example, a Malaysia search displays MYR values.
- Browse ranked job opportunities with match scores.
- See exactly which skills you have and which are missing for each job.
- Read the Listing quality and Confidence columns before acting on a role:
| Label | Meaning | Included in market intelligence? |
|---|---|---|
| ● Verified | Complete schema.org job posting was retrieved | Yes |
| ◐ Partial | Direct page was unavailable; matching uses a search snippet and is capped at 60% | No |
| ○ Demo | Clearly labelled synthetic fallback, not a real vacancy | No |
- AI Resume Tailor — select any job, click "✨ Generate Tailored Resume PDF," and download an ATS-optimized resume.
Each missing skill has an expandable panel with:
- A learning strategy that leverages your existing skills.
- A specific hands-on project idea.
- A difficulty rating and estimated time.
- Direct links to documentation, YouTube, and LeetCode/Kaggle.
- Select your target job.
- Click "✨ Draft Cover Letter (Agentic Mode)."
- The AI researches the company online first, then writes a personalized letter.
- Edit it in the text area and download it as
.txt.
Configuration:
| Setting | Options |
|---|---|
| Target Position | Any matched job, or a generic "AI Engineer" |
| Interview Focus | ⚖️ Balanced · 💬 Behavioral · 💻 Technical |
| Difficulty Level | 🌱 Entry · 🚀 Mid · ⭐ Senior · 👑 Staff |
Interview flow:
① Start Session → AI greets you (voice)
② AI asks a question (voice + text displayed)
③ Click 🎤 Record → speak your answer
④ Whisper transcribes → Llama evaluates → EdgeTTS responds
⑤ See a real-time score + sample answer
⑥ Repeat for 5–10 questions
⑦ End Session → view a full performance report (A–F grade)
| Layer | Technology | Version | Why This Choice |
|---|---|---|---|
| LLM | Llama 3.3 70B via Groq | Latest | 300+ tok/s, JSON mode, free tier |
| Embeddings | all-MiniLM-L6-v2 | — | Local inference, 384-dim, ~20ms/batch |
| Orchestration | LangGraph + LangChain | — | Stateful multi-agent pipelines |
| Speech-to-Text | Whisper Large V3 Turbo | — | 8× faster than Whisper base |
| Text-to-Speech | Edge-TTS (+ gTTS fallback) | — | Neural voice quality at zero cost |
| Computer Vision | MediaPipe + OpenCV | ≥ 0.10 | Real-time landmark tracking |
| Vector Similarity | sentence-transformers cosine similarity | — | Local semantic matching with taxonomy-first shortcuts |
| Web Scraping | DuckDuckGo Search + requests | — | Anti-bot resilient, no API key needed |
| PDF Processing | pdfminer.six + xhtml2pdf | — | Text extraction and PDF generation |
| Frontend | Streamlit + streamlit-webrtc | ≥ 1.40 | Session state, file upload, WebRTC |
| Charts | Plotly + Matplotlib + WordCloud | — | Interactive visuals |
| Config | python-dotenv | — | Secure API key management |
The full end-to-end data journey — from raw resume file to personalized output.
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 1 │ Resume Ingestion │
│ INPUT │ PDF / DOCX / TXT file │
│ MODULE │ core/resume_parser.py │
│ OUTPUT │ Cleaned raw text string │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 2 │ AI Profile Construction │
│ INPUT │ Raw resume text │
│ MODULE │ core/resume_analyzer.py + core/semantic_matcher.py │
│ OUTPUT │ Profile, employment evidence, completeness, role readiness │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 3 │ Live Job Market Scan │
│ INPUT │ Job title keywords + location │
│ MODULE │ core/job_scraper.py │
│ OUTPUT │ Verified pages, partial snippets, or labelled demo records │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 4 │ Semantic Job Matching │
│ INPUT │ Candidate profile + scraped job list │
│ MODULE │ core/job_matcher.py + core/semantic_matcher.py │
│ OUTPUT │ Ranked matches with provenance and confidence-aware scores │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 5 │ Skill Gap Analysis │
│ INPUT │ Aggregated missing skills from ranked match results │
│ MODULE │ core/gap_analyzer.py │
│ OUTPUT │ Prioritised gaps: critical / medium / low-priority │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 6 │ Transfer Learning Roadmap Generation │
│ INPUT │ Top gaps + candidate profile │
│ MODULE │ core/learning_roadmap.py + Llama 3.3 via Groq │
│ OUTPUT │ Week-by-week curriculum with projects + curated links │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 7 │ AI Mock Interview Simulation │
│ INPUT │ Voice (microphone) + optional webcam feed │
│ MODULES │ core/interviewer.py · speech_analyzer.py · interview_scorer │
│ │ core/camera_stream.py · camera_analyzer.py │
│ OUTPUT │ Real-time Q&A + performance score (0–100, A–F grade) │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 8 │ Resume Tailoring & Cover Letter Generation │
│ INPUT │ Candidate profile + target job description │
│ MODULES │ core/resume_tailor.py · core/agent_graph.py (LangGraph) │
│ OUTPUT │ ATS-optimised PDF resume + personalised cover letter .txt │
└────────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ STAGE 9 │ Reporting & Visualisation │
│ INPUT │ All analysis data │
│ MODULES │ utils/report_generator.py · visualizer.py · pdf_generator │
│ OUTPUT │ Charts/reports from verified jobs; user-facing match results │
└─────────────────────────────────────────────────────────────────────────┘
graph TB
subgraph "📥 Ingestion Layer"
A["Resume (PDF/DOCX/TXT)"] -->|"pdfminer / python-docx"| B["Raw Text"]
B -->|"Taxonomy + Regex"| C["Structured Profile JSON"]
C -->|"Taxonomy + SentenceTransformer"| D["Canonical Skills + Embeddings"]
E["Job Keywords + Location"] -->|"Public Pages + Search Discovery"| F["Live Job Listings"]
F -->|"Snippet Extraction"| G["Job Requirement Texts"]
G -->|"Batch Embedding"| H["Market Vectors"]
end
subgraph "🧠 Intelligence Engine"
D --> I["Cosine Similarity"]
H --> I
I -->|"Skill × Experience × Title"| J["Weighted Match Scores"]
J -->|"Aggregate Missing Skills"| K["Gap Frequency Counter"]
K -->|"× Domain Weight"| L["Severity Scores"]
L -->|"Threshold split"| M["Critical / Medium / Low Gaps"]
end
subgraph "🤖 Agentic Layer"
M -->|"Llama 3.3 70B + Transfer Learning"| N["Personalised Roadmap"]
J -->|"Llama 3.3 STAR Rewrite"| O["Tailored Resume PDF"]
P["Company Name"] -->|"LangGraph: Research Node"| Q["Company Intel"]
Q -->|"LangGraph: Writing Node"| R["Cover Letter .txt"]
end
subgraph "🎤 Interaction Layer"
S["Microphone Input"] -->|"Whisper Large V3 Turbo"| T["Transcript"]
U["Webcam (Optional)"] -->|"MediaPipe + OpenCV"| V["Posture / Eye Contact"]
T --> W["Llama 3.3 Interviewer"]
V --> W
W -->|"EdgeTTS / gTTS"| X["AI Voice Response"]
W --> Y["Answer Score (0–100)"]
end
subgraph "📤 Output Layer"
N --> Z["Streamlit Dashboard"]
O --> Z
R --> Z
Y --> Z
end
style I fill:#e1f5ff,stroke:#0288d1
style W fill:#f3e5f5,stroke:#7b1fa2
style L fill:#fff3e0,stroke:#f57c00
CareerCompass-AI/
│
├── 📄 app.py │ Streamlit web dashboard (main entry for UI)
├── 📄 main.py │ CLI pipeline (headless / batch mode)
├── 📄 config.py │ All configs: paths, weights, keywords, thresholds
├── 📄 requirements.txt │ Python dependencies
├── 📄 .env │ API keys (gitignored)
│
├── 📁 core/ │ Core AI & business logic
│ ├── pipeline.py │ Shared resume→market analysis orchestrator
│ ├── resume_parser.py │ PDF/DOCX/TXT text extraction
│ ├── resume_analyzer.py │ Full profile construction (skills, exp, achievements)
│ ├── semantic_matcher.py │ Skill taxonomy + lazy embedding matcher
│ ├── job_scraper.py │ Multi-platform DuckDuckGo dorking scraper
│ ├── job_matcher.py │ Weighted cosine similarity job ranker
│ ├── gap_analyzer.py │ Gap clustering, severity scoring, triage
│ ├── learning_roadmap.py │ Transfer-learning curriculum generator (Llama 3.3)
│ ├── cover_letter_generator.py │ Standalone cover letter writer (Groq)
│ ├── resume_tailor.py │ STAR-method ATS resume rewriter (Llama 3.3)
│ ├── agent_graph.py │ LangGraph 2-node agentic cover letter workflow
│ ├── interviewer.py │ Mock interviewer: LLM questions + TTS audio
│ ├── speech_analyzer.py │ Pure-Python speech metrics (WPM, fillers, STAR)
│ ├── interview_scorer.py │ Unified A–F performance scoring engine
│ ├── camera_analyzer.py │ MediaPipe frame-level body language analysis
│ ├── camera_stream.py │ WebRTC video processor bridge for Streamlit
│ ├── market_intelligence.py │ Skill demand, trend, and salary analytics
│ ├── skill_graph.py │ Learning dependency graph resolver
│ └── resume_optimizer.py │ Resume version history and score deltas
│
├── 📁 utils/ │ Shared helpers
│ ├── logger.py │ Structured file + console logger
│ ├── helpers.py │ Banner printer, summary display
│ ├── audio.py │ Safe audio-byte and duration helpers
│ ├── session_runtime.py │ Per-browser-session runtime paths
│ ├── visualizer.py │ Skill radar chart + market word cloud
│ ├── report_generator.py │ CSV / JSON / Markdown report builder
│ └── pdf_generator.py │ HTML → PDF converter for tailored resume
│
├── 📁 data/ │ Static source data + ignored runtime snapshots
│ ├── skill_categories.py │ Validated skill taxonomy and aliases
│ ├── skill_ontology.py │ Skill prerequisites and learning estimates
│ └── job_templates.py │ Fallback synthetic job structures
│
├── 📁 sample_data/resumes/ │ Place your resume here (CLI mode)
├── 📁 runtime/ │ Ignored local uploads and generated interview audio
├── 📁 output/ │ Ignored auto-generated reports and charts
├── 📁 logs/ │ Ignored structured application logs
├── 📁 tests/ │ Pytest coverage for core behavior
└── 📁 assets/ │ Screenshots for README
- Shared orchestration:
core/pipeline.pycentralizes the resume-to-market workflow so the Streamlit and CLI paths reuse the same analysis logic. - Taxonomy-first matching:
core/semantic_matcher.pynormalizes known skills and aliases before falling back to embedding similarity. - Lazy, offline-safe ML loading:
sentence-transformersandtorchload only whenSemanticMatcher()is instantiated. Cached model files are resolved withlocal_files_only=True; an incomplete cache falls back immediately to deterministic taxonomy/lexical matching instead of retrying the network. - Deterministic tests:
tests/covers core matching along with session isolation, audio timing, job provenance, demo-data exclusion, Windows encoding, and camera compatibility — all without downloading the transformer model. - Runtime hygiene: Uploads, generated audio, logs, reports, snapshots, and resume-version history live in ignored runtime paths unless intentionally committed.
CareerCompass AI is designed as a local-first Streamlit project.
| Data | Where it goes | Notes |
|---|---|---|
| Uploaded resumes | runtime/sessions/<session-id>/uploads/ |
Isolated to one browser session; ignored by git. |
| Generated interview audio | runtime/sessions/<session-id>/audio/ |
Isolated to one browser session; ignored by git. |
| Reports and charts | runtime/sessions/<session-id>/output/ |
Session-scoped local exports; ignored by git. |
| Logs | logs/ |
Local diagnostics; ignored by git. |
| Market snapshots and resume versions | runtime/sessions/<session-id>/ |
Separate history per Streamlit browser session. CLI mode uses the configured global paths. |
| LLM requests | Groq API | Required for LLM features; avoid submitting sensitive resumes to hosted APIs unless appropriate for your use case. |
The static files under data/ — skill_categories.py, skill_ontology.py, and job_templates.py — are source data and should remain versioned.
| Symptom | Likely Cause | Fix |
|---|---|---|
| AI writing/interview is unavailable | GROQ_API_KEY is missing |
Add it to .env, Streamlit secrets, or enter a per-session override. Server keys are never prefilled into the browser. |
| Semantic model is unavailable | The local transformer cache is missing or incomplete | The app continues in deterministic lexical mode. Set CAREERCOMPASS_ALLOW_MODEL_DOWNLOAD=1 for one trusted run if downloads are permitted. |
| Some jobs show "Partial snippet" | A job board returned 403/429 or otherwise blocked full-page validation | The match stays provisional and capped at 60%; partial jobs are excluded from market trends. |
| Camera tab is unavailable | Dependencies are missing, or MediaPipe lacks the legacy Solutions API | Install the pinned requirements; this implementation uses mediapipe==0.10.21. Newer builds such as 0.10.33 no longer expose the required solutions namespace. |
| PDF export is blank | xhtml2pdf failed to render malformed generated content |
Retry tailoring, or inspect the generated profile structure. |
Role: Converts raw document files into clean plain text.
| Step | Action |
|---|---|
| 1 | Detect the file extension (.pdf, .docx, .txt) |
| 2 | Extract text with pdfminer.six (PDF) or python-docx (DOCX) |
| 3 | Strip null characters and collapse excess whitespace with regex |
| 4 | Return a cleaned string ready for NLP |
Role: Provides taxonomy-first skill normalization plus vector-based operations through a lazy singleton.
# Loaded lazily on first SemanticMatcher() instantiation
model = SentenceTransformer('all-MiniLM-L6-v2') # 384-dim embeddings
# Pre-computes anchor embeddings for 5 categories:
# Programming · Data & AI · Web & UI · Cloud & DevOps · Soft Skills
# Key methods:
batch_filter_skills(candidates) # Matrix multiply vs. 10 "skill" anchors
batch_classify_categories(skills) # Assigns each skill to a category
find_best_match(query, options) # 1-to-N semantic match with threshold
get_similarity(text1, text2) # Pairwise cosine score (0.0–1.0)Why a lazy singleton? Importing and loading the transformer model is expensive. The model and its ML dependencies load only when SemanticMatcher() is instantiated, then get reused across modules (resume_analyzer, job_matcher, gap_analyzer).
Role: Builds a comprehensive 360° candidate profile from raw resume text.
Processing pipeline:
_extract_contact_info()— Best-effort name extraction plus regex for email, phone, and LinkedIn/GitHub URLs.extract_skills_dynamically():- Extracts known skills first from
data/skill_categories.pyanddata/skill_ontology.py. - Normalizes aliases, such as
Amazon Web Services→AWS,React.js→React, andsklearn→Scikit-Learn. - Uses regex to cast a broader net, then validates unknown candidates with
batch_filter_skills()at a semantic threshold ≥ 0.40. - Assigns categories with
batch_classify_categories()— Programming/Data/Cloud/Web/Soft Skills. _estimate_proficiency_enhanced()checks a ±100-character window around each skill mention for keywords like "advanced" or "5+ years," returning a score of 0.5–1.0 and a label (Beginner/Intermediate/Expert).
- Extracts known skills first from
_calculate_experience_robust()— Isolates the work-experience section, excludes education/project dates, merges overlapping employment ranges, falls back to explicit work-experience statements, and preserves the years-only result astimeline_seniority_level._extract_employment_history()— Builds dated role/company records with responsibilities, achievements, skills used, duration, source excerpts, extraction confidence, employment type, and deterministic scope/impact signals._attach_skill_evidence()— Links skills to employment, project, certification, or resume evidence and calculates professional years, recency, and confidence.calculate_technology_depth()— Scores the strongest eight work-evidenced skills from 55% professional duration (two-year cap per skill), 25% evidence confidence, and 20% recency. Breadth and multi-domain influence are capped; projects/certifications add no more than five points.calculate_professional_maturity()— Produces an explainable 0–100 score using 45% timeline, 25% role scope, 20% technical depth, and 10% impact. Internship scope/impact use a0.60multiplier, and duration guards prevent short careers from being over-promoted.build_target_role_profiles()— Calculates role-specific readiness from relevant duration (30%), expected-skill coverage (30%), professionally evidenced target-stack depth (25%), and relevant responsibility/impact alignment (15%). Role level uses the same hybrid model restricted to relevant jobs and technologies.calculate_profile_completeness()— Scores contact details, timeline quality, quantified achievements, skill evidence, and professional links._extract_achievements()— Regex forN%,$N,NM userspatterns with surrounding context._analyze_skill_diversity()— Classifies the profile as Specialist / Generalist / Broad Explorer._generate_profile_summary()— Produces a human-readable 3–5 sentence AI-style summary using the hybrid career level._generate_recommendations()— Rule-based, actionable tips on career growth, branding, and experience.
Professional-maturity level mapping:
| Maturity score | Displayed level |
|---|---|
<30 |
Entry-Level |
30–54 |
Junior |
55–69 |
Mid-Level |
70–79 |
Mid-Senior |
80–89 |
Senior |
90+ |
Staff/Principal |
Output JSON profile:
{
"contact_info": { "email": "...", "phone": "...", "links": [...] },
"skills_by_category": {
"Data & AI": [{"skill": "PyTorch", "proficiency": 0.92, "proficiency_level": "Expert", "professional_years": 3.5, "last_used_year": 2026, "evidence_confidence": 0.9}]
},
"total_skills": 38,
"experience": {
"total_years": 4,
"seniority_level": "Mid-Level",
"timeline_seniority_level": "Mid-Level",
"professional_maturity": {
"overall_score": 66.5,
"level": "Mid-Level",
"components": {"timeline": 65, "role_scope": 62, "technical_depth": 76, "impact": 48},
"calculation_basis": "hybrid_professional_evidence",
"explanations": ["Timeline contributes 45% from 4 verified working years."]
},
"career_level_basis": "hybrid_professional_evidence"
},
"employment_history": [{"role": "ML Engineer", "company": "Example", "duration_years": 3.5, "employment_type": "full_time", "role_scope_score": 62, "impact_score": 48, "technology_evidence_score": 76}],
"target_role_profiles": [{"target_role": "ML Engineer", "relevant_years": 3.5, "readiness_score": 82, "experience_score": 100, "role_scope_score": 62, "technical_depth_score": 76, "impact_score": 48, "role_level_basis": "hybrid_professional_evidence_restricted_to_target"}],
"profile_completeness": {"score": 85, "missing_items": ["Add a portfolio or professional profile link"]},
"career_level": "Mid-Level",
"career_level_basis": "hybrid_professional_evidence",
"skill_diversity": { "profile_type": "Full-Stack / Generalist", ... },
"achievements": [{ "metric": "40%", "context": "..." }],
"strengths": ["Seasoned Professional with 4+ years..."],
"profile_summary": "A Mid-Level professional...",
"recommendations": [{ "priority": "High", "suggestion": "..." }]
}Role: Discovers public job listings, handles blocked pages conservatively, and preserves the strongest accessible metadata.
Strategy — "search engine dorking": Direct API access to job boards is blocked, so DuckDuckGo is used to search within specific domain namespaces instead:
# Example dork query:
site:linkedin.com/jobs/view "Data Scientist" Malaysia
site:my.jobstreet.com/job/ "Machine Learning Engineer" Malaysia
site:malaysia.indeed.com "Python Developer" Malaysia
site:glassdoor.com "AI Engineer" Malaysia
- Uses current
my.jobstreet.com/job/<id>vacancy URLs and rejects aggregate JobStreet search pages. - Fetches public JobStreet result cards when accessible, with current-domain DuckDuckGo discovery as a fallback.
- Fetches the top search results per keyword per platform.
- Fetches each destination and prefers complete schema.org
JobPostingdescriptions. - Retains usable snippets as
search_snippet_partialwhen a page returns 403/429 or stays unavailable after one retry. - Opens a per-run domain circuit after the first 403/429 to avoid repeatedly hitting a blocked host.
- Recovers employer identity from schema.org, explicit listing cards, visible page metadata, search-result titles, selected snippets, and LinkedIn URL slugs in descending confidence order.
- Merges duplicate discoveries field by field so a richer company name or longer snippet is not overwritten by a poorer result.
- Records source, retrieval timestamp, description source, data quality,
company_source,company_confidence, and whether the employer name was inferred. - Uses short randomized discovery delays to reduce rate-limiting.
- Caps partial match scores at 60% and excludes partial records from market snapshots, salary signals, word clouds, and historical trends.
- Resolves salary currency from the selected location (e.g., Malaysia → MYR) and extracts only explicitly matching currency amounts.
- Fallback: Only when both verified and partial collections are empty does
generate_synthetic_jobs()create visibly labelled demo roles.
Role: Scores each candidate–job pair across three dimensions.
Matching algorithm:
Step 1: Extract job requirements
- Taxonomy and aliases identify known skills first
- Regex finds additional capitalised tokens from the job description
- batch_filter_skills() validates unknown candidates (threshold ≥ 0.45)
Step 2: Skill Match Score
For each required skill:
→ Try an exact case-insensitive match first (fastest)
→ Fall back to find_best_match() semantic match (threshold 0.70)
score = matched_count / required_count
Step 3: Experience Match Score
- Extract "N+ years" from the job description
- Identify relevant employment from job-title tokens, required technologies, and the closest target-role profile
- Experience score = relevant-years fit × 0.50
+ relevant role-scope alignment × 0.25
+ professionally evidenced technology alignment × 0.25
- Unrelated calendar experience cannot satisfy a target role by itself
Step 4: Title Semantic Match
- Compare the job title with selected target roles and actual employment titles
- Use the closest real title; do not synthesize a generic "<level> Developer" proxy
Step 5: Final Weighted Score
overall = (skill_score × 0.60) + (title_sim × 0.20) + (exp_score × 0.20)
Match quality labels:
| Score | Label | Priority |
|---|---|---|
| ≥ 0.80 | Excellent | 🔥 URGENT |
| ≥ 0.65 | Strong | ✅ HIGH |
| ≥ 0.50 | Good | 📝 MEDIUM |
| < 0.50 | Weak |
Role: Identifies and prioritizes what the candidate is missing at market scale.
4-step algorithm:
Step 1: Aggregation
- Collect all missing_required skills from every match result
- Count raw frequency: {"MLflow": 8, "Airflow": 6, "Spark": 4}
Step 2: Semantic Clustering (threshold 0.85 — very strict)
- Prevents "React" and "ReactJS" from being counted separately
- Merges synonyms into canonical clusters
Step 3: Severity Scoring
- Severity = frequency × domain_weight
- Domain weights:
Programming → 1.5× multiplier
Data & AI → 1.4×
Cloud & DevOps → 1.3×
Web & UI → 1.2×
Soft Skills → 0.8× (important, but not a hard blocker)
- Market Penetration = (frequency / total_jobs) × 100%
Step 4: Threshold-Based Triage
- Critical: market_penetration > 40% OR severity_score ≥ 2.5
- Medium: frequency > 1
- Low: single occurrence ("nice-to-have")
Output:
{
"critical_gaps": ["MLflow", "Airflow"],
"medium_priority_gaps": ["FastAPI", "Terraform"],
"low_priority_gaps": ["Tableau"],
"detailed_analysis": [
{"skill": "MLflow", "frequency": 8, "market_penetration": 72.7, "severity_score": 11.2}
]
}Role: Generates a personalized learning curriculum using transfer-learning strategies via Llama 3.3.
Workflow:
- Extracts the candidate's current skills and experience level from the profile.
- Builds a focused prompt (capped at 6 target skills to stay within the context window).
- The transfer-learning prompt instructs Llama 3.3 to:
- Reference overlapping concepts from the candidate's existing stack.
- Suggest projects that combine old and new skills.
- Estimate realistic timelines based on skill proximity.
- The AI responds in structured JSON; post-processing then adds deterministic resource links (no hallucinated URLs):
- Programming → LeetCode + GitHub
- Data & AI → Kaggle + Papers With Code
- Cloud & DevOps → AWS Workshops + Docker Hub
- Web & UI → MDN Web Docs
- Resources are sorted by difficulty: Low → Medium → High.
Fallback (no API key): Returns a rule-based template roadmap without AI-generated content.
Role: Powers the end-to-end mock interview conversation via Groq LLMs and TTS.
Key capabilities:
- Difficulty system (4 levels): Each level has its own
expectation,technical_depth,behavioral_depth, and example questions, hard-coded indifficulty_configs. - Focus system (3 modes):
behavioral(80/20 split),technical(90/10), andbalanced(50/50), each with its own topic list. - AI Engineer knowledge base: Built-in domain knowledge covering ML fundamentals, LLMs/RAG, computer vision, MLOps, and system design — injected into every system prompt.
- Structured JSON output: Llama 3.3 runs in
json_objectresponse mode, returningquestion,sample_answer, andanswer_score(5 rubric dimensions, each scored 1–10). - Progressive questioning: Rounds 1–2 warm up, 3–5 test core knowledge, 6–8 go deeper, and 9+ probe edge cases.
- TTS pipeline:
- Tries EdgeTTS (
en-US-AriaNeural) first for neural voice quality. - Falls back to gTTS if EdgeTTS fails.
- Validates audio file size (> 1KB) before returning the path.
- Tries EdgeTTS (
Rubric dimensions (each scored 1–10):
| Dimension | What It Measures |
|---|---|
content_relevance |
Did they actually answer the question? |
technical_depth |
Depth of technical knowledge shown |
communication_clarity |
Clarity and structure of the answer |
star_method_usage |
STAR framework adherence (behavioral) / structured problem solving (technical) |
specific_examples |
Concrete examples with quantified metrics |
Role: Extracts quantitative metrics from transcribed interview answers. Pure Python — no external ML dependencies.
Metrics computed:
| Metric | How | Ideal Range |
|---|---|---|
| Words Per Minute | word_count / (duration_sec / 60) |
130–170 WPM |
| Filler Word Count | Regex patterns: \bum\b, \buh\b, \byou know\b, etc. |
< 3% of words |
| Verbal Confidence | assertive_count - hedge_count, normalized per 100 words |
Net positive |
| STAR Detection | Keyword patterns for Situation / Task / Action / Result | ≥ 2/4 components |
| Vocabulary Diversity | Type-token ratio: unique_words / total_words |
≥ 0.55 |
| Avg Sentence Length | word_count / sentence_count |
12–25 words |
Component scores (0–100) → composite overall_speech_score:
overall = WPM (20%) + Filler Control (25%) + Confidence (25%) + Structure (15%) + Vocabulary (15%)
Role: Merges LLM answer quality, speech analytics, and camera data into a single quantitative performance score.
Dimension weights:
| Dimension | With Camera | Without Camera |
|---|---|---|
| Content (LLM) | 35% | 45% |
| Speech Delivery | 25% | 30% |
| Body Language | 20% | — |
| Structure / STAR | 20% | 25% |
Content score breakdown:
content_score = (content_relevance × 30%) + (technical_depth × 30%)
+ (communication_clarity × 25%) + (specific_examples × 15%)
Letter grade scale:
| Score | Grade | Score | Grade | |
|---|---|---|---|---|
| ≥ 93 | A | 77–79 | C+ | |
| 90–92 | A- | 73–76 | C | |
| 87–89 | B+ | 70–72 | C- | |
| 83–86 | B | 67–69 | D+ | |
| 80–82 | B- | 60–66 | D/D- | |
| < 60 | F |
Session report (get_session_report()): Averages all per-answer scores, identifies top strengths and improvement areas, detects improvement trajectory (comparing 2nd-half scores to 1st-half scores), and summarizes aggregate speech/camera stats.
Role: Analyzes body language in real time during interviews via webcam.
- Uses
streamlit-webrtcfor browser-based WebRTC video streaming. InterviewVideoProcessorprocesses every 3rd frame (~10 FPS analysis) to maintain performance.CameraAnalyzer(MediaPipe) tracks:- Eye contact % — iris landmark projection relative to the screen midpoint.
- Posture score — shoulder alignment and head tilt angle.
- Movement stability — frame-to-frame landmark displacement.
- Attention drift — head rotation away from the screen.
- A thread-safe
_lockensures the main Streamlit thread can safely read the latest analysis.
Role: Rewrites the candidate's resume profile to maximize match with a target job description.
Rules enforced in the system prompt:
- Truthfulness — Never invent experience; only optimize what already exists.
- ATS keyword injection — Extract the top 5 technical keywords from the JD and ensure they appear in the summary/skills.
- STAR method rewrite — Transform bullet points into
[Action Verb] + [Context] + [Result/Metric]. - Skill re-ranking — Move JD-relevant skills to the top of each category list.
- Summary hook — A 3-sentence elevator pitch strictly aligned with the target role.
Reliability safeguard: If the LLM omits contact_info or restructures experience incorrectly, the original values are restored from the input profile before returning.
Role: A two-node agentic workflow that researches a company before writing a personalized cover letter.
State: { company_name, job_title, job_description, resume_text, research_data, cover_letter }
Node 1 — Researcher:
→ DuckDuckGoSearchRun: "recent news mission values work culture of {company}"
→ Stores results in state["research_data"]
Node 2 — Writer:
→ ChatPromptTemplate: uses resume_text + job_title + research_data
→ Instructions: open with a company news hook, connect candidate skills to the JD
→ Writes to state["cover_letter"]
Edge: researcher → writer → END
Role: Runs the resume → jobs → matches → gaps → roadmap workflow once for both the Streamlit and CLI entry points.
CareerAnalysisPipeline.run_resume_market_analysis() accepts a resume path, keywords, location, an optional API key, and an optional progress callback. Its constructor also accepts session-scoped output, resume-history, and market-snapshot paths. The result includes parsed data plus explicit job-data provenance.
Role: Provides the interactive dashboard and delegates the core workflow to CareerAnalysisPipeline.
Analysis flow triggered by "Analyze resume and market":
- Create an isolated
runtime/sessions/<session-id>/workspace and save the upload under a generated filename. - Call
CareerAnalysisPipeline.run_resume_market_analysis()with Streamlit status updates. - Store the returned
resume_text,resume_profile,matches,roadmap, market snapshot/history, and dependency chain in session state. - Render charts, job matches, the roadmap, the cover letter, resume tailoring, and the mock interview tabs.
The interface uses a responsive two-surface layout: the sidebar contains upload and target controls, while the main canvas uses spacious two-column evidence rows, progressive-disclosure profile sections, task-grouped navigation, and readable job cards. The full comparison table is available on demand. Subtle entrance motion is disabled automatically when the operating system requests reduced motion. Optional camera dependencies are imported only when camera feedback is enabled.
6 task-oriented workspaces:
| Tab | Content |
|---|---|
| Profile insights | Skill distribution and verified market-demand visuals |
| Job matches | Spacious recommendation cards, employer/listing provenance, filters, and optional comparison table |
| Growth plan | Nested learning-roadmap and skill-graph views |
| Applications | Nested cover-letter and resume-lab views |
| Interview | Voice interview simulator with optional, lazily loaded camera feedback |
| Market insights | Salary, demand, snapshots, and location-aware currency insights from verified jobs |
See System Improvement Roadmap for the prioritized reliability, feature, and architecture plan.
Role: Provides an alternative command-line pipeline for batch or server-side processing (no browser required).
Runs the same shared CareerAnalysisPipeline headlessly, then writes timestamped reports to output/:
- Strategic job match CSV
- High-priority hotlist CSV
- Learning roadmap CSV
- Full analysis JSON
COVER_LETTER_1_<Company>.txtfor top viable matchesvisual_skill_radar.pngvisual_market_wordcloud.png
GROQ_API_KEY=gsk_... # Required for all LLM features
CAREERCOMPASS_ALLOW_MODEL_DOWNLOAD=0 # Default: never download embeddings implicitly# ── Job Search ────────────────────────────────────────
JOB_SEARCH_KEYWORDS = ["Data Scientist", "ML Engineer", "AI Engineer"]
JOB_SEARCH_LOCATION = "Malaysia"
TOTAL_MAX_JOBS = 30
INCLUDE_SYNTHETIC_JOBS = True # Fallback if scraping returns 0 results
# ── Matching Weights ──────────────────────────────────
MATCHING_WEIGHTS = {
'skills': 0.60,
'title': 0.20,
'experience': 0.20
}
SKILL_CATEGORY_WEIGHTS = {
'ml_ai': 1.2, # High-value AI skills
'data_engineering': 1.1,
'cloud': 1.1,
'languages': 1.0,
'databases': 1.0,
'web_frameworks': 0.9,
'tools': 0.8
}
# ── Runtime Paths ────────────────────────────────────
TEMP_UPLOADS_DIR = RUNTIME_DIR / "temp_uploads"
AUDIO_DIR = RUNTIME_DIR / "audio"
# ── Match Quality Thresholds ──────────────────────────
MATCH_THRESHOLDS = {
'excellent': 0.85,
'strong': 0.70,
'good': 0.55,
'fair': 0.40
}
# ── Interview Model Settings ──────────────────────────
# Configured directly in core/interviewer.py:
LLM_MODEL = "llama-3.3-70b-versatile" # Groq model
STT_MODEL = "whisper-large-v3-turbo" # Whisper model
TTS_VOICE = "en-US-AriaNeural" # EdgeTTS voice