03Internship · Case study
AI Career Copilot: LLM Resume Analyzer
Built during my AI/ML internship at Apno AI (Oct 2026)
Overview
A web app that compares a resume against a job description and returns an ATS-style match score, matched and missing keywords, strengths, specific rewrite suggestions, and projects to close skill gaps. It’s aimed at students and fresh graduates applying to software, data, and AI/ML roles.
Starting point
The internship starter was a single HTML file with the API key exposed in the browser and one basic prompt. I turned it into a secured, tested, deployed product.
Key numbers
- scored test pairings landed in their expected range
- 7 / 7
- automated API checks
- 26
- automated UI checks
- 77
Stack
- HTML, CSS, JavaScript
- Node.js serverless on Vercel
- Google Gemini API (gemini-3.5-flash)
- pdf.js
- Tesseract.js
- localStorage
- Git and GitHub
- PowerShell
01Section
Security and architecture
- Moved the Gemini API key out of the browser into a Vercel serverless function (Node.js), so it exists only as an environment variable.
- Sent the key in a request header (not the URL) and kept it out of logs.
- Escaped all AI and user text before rendering, to prevent XSS.
- Added input limits and prompt-injection defenses: user text is wrapped in tags and treated as data.
- Handled secrets properly: .gitignore, a placeholder .env.example, and a pre-commit scan.
02Section
LLM and prompt engineering
- Prompt structure: role, task, scoring rubric with calibration bands, format, rules, and one few-shot example.
- Structured output with Gemini’s responseSchema and JSON mode, which removed malformed-JSON failures.
- A server-side check that moves any “missing” keyword actually present in the resume back to “matched”.
- Versioned prompts (v1, v2, and a v3 I tested and reverted when it measured no better), all logged in PROMPT_LOG.md.
03Section
Reliability
- Diagnosed a production failure (a retired model returning 404) by testing candidate models with the real key.
- Moved to gemini-3.5-flash with an automatic fallback to gemini-3.1-flash-lite on 503, 429, or 404.
- Added a 45-second timeout, correct Gemini 3 thinking settings, and logging of status and error messages.
04Section
Evaluation
- Wrote a test plan with 5 fictional resumes, 2 job descriptions, and 7 scored pairings with expected ranges.
- Every v2 score landed in its expected range, and the ordering held (strong candidate > fresh grad > weak and non-tech resumes).
- Edge tests: a prompt-injection resume was ignored, an XSS payload was not executed, and a recipe pasted as a resume scored 0.
- Automated tests: 26 API checks and 77 UI checks, plus a real end-to-end script.
05Section
Features
- PDF resume upload: client-side with pdf.js. It joins wrapped lines, normalizes bullets, reads two-column layouts, and gives friendly errors for scanned, password-protected, renamed, and oversized files.
- Job description screenshot upload: in-browser OCR with Tesseract.js, with drag-and-drop and paste. Large images are scaled down, dark-mode screenshots are inverted, and low-confidence text gets a warning. Nothing is sent to a server.
- Saved history: the last 10 results in localStorage, reopened with no API call. It stores results and a label, not the resume text.
- UI: aligned layout, 44px mobile tap targets, and an accessible inline SVG icon set in place of emoji, with a test that fails if an emoji comes back.
06Section
What I learned
- Prompt changes need measurement. My v3 prompt looked reasonable but did no better than v2, so I reverted it.
- Model names go stale. Production broke because a model was retired, which is why I added a fallback and logging.
- Secrets belong on the server. Anything in browser code is public.
- Free-tier limits are a design constraint. The main model allows about 20 requests a day, which shaped the fallback design.
07Section
Known limitations
- Some rewrite suggestions can add details not in the resume. I measured this, and a stricter prompt did not fix it. A server-side check would be the next step.
- “Or” requirements (AWS or GCP or Azure) are counted as missing.
- OCR accuracy depends on image quality, and PDFs with 3+ columns aren’t perfectly cleaned.
The live demo runs on the free Gemini tier, so a response may be slow or use the fallback model.
Built with AI-assisted development; I designed the prompts, ran the evaluation, and made the deployment decisions.