Skip to content
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.

Back to Experience