// in-house build · retrieval + llm
Career advice is easy. Career advice someone is hiring for is not.
Upload a resume and get ranked career paths, the specific skills standing between you and each of them, and a learning order — every step of it derived from real job descriptions rather than from the model's general impression of the job market.
- Build
- In-house, open source
- Scope
- Parsing, matching, recommendation
- Status
- Hosted demo, sign-in required
- Source
- GitHub ↗
The challenge
Ask any competent language model to review a resume and it will produce something immediately plausible and almost entirely useless: broaden your skill set, consider leadership, learn cloud. It is not wrong. It is just advice that would apply to anybody who has ever held a job.
The reason is that the model is reasoning from a general impression of what careers look like, not from what employers are currently asking for. Those two things drift apart constantly, and the drift is exactly where the useful advice lives — in the gap between the skills someone has and the skills that appear in the postings they would need to clear.
The second failure is more practical. Advice with no opening attached to it is a reading list. Someone acting on a career plan needs to know that roles matching it exist right now, not that they existed in the training data.
The approach
Match against postings, not against intuition
A corpus of real job descriptions is embedded and indexed with FAISS ahead of time. An uploaded resume is parsed into skills, roles and seniority signals, embedded the same way, and matched against that index.
Because the match is vector similarity rather than keyword overlap, a candidate whose resume says "built ETL jobs in Airflow" matches a posting asking for "data pipeline ownership" — a match no keyword filter finds, and precisely the kind of adjacency a career move is made of.
The model reasons; it does not recall
Only after matching does the LLM get involved, and it is handed both the parsed profile and the specific postings that matched. Its job is to explain the distance between the two: which paths are reachable, what is missing for each, and in what order the gaps are worth closing.
That constraint is what makes the output specific. The model is not being asked what a data engineer should know — it is being asked what these postings ask for that this resume does not evidence.
Retrieval decides what is true about the market. The model only decides how to explain the gap.
Groq, because latency shapes whether people iterate
The reasoning step runs LLaMA 3 on Groq. The draw is inference speed: this is a tool people use by trying things — reordering priorities, asking about a different target role, re-running with an updated resume — and a thirty-second wait per attempt quietly converts an exploratory tool into a one-shot one.
Two data sources with two different lifetimes
The job-description corpus is indexed offline and changes slowly; it is what the advice is grounded in. Live openings come from the JSearch API per request, because a posting list is stale within days and there is no sense embedding something with that shelf life.
Keeping the two apart is what lets the roadmap stay stable while the openings beside it refresh. Advice that reshuffles every time a job board updates is not advice.
Where it landed
The pipeline runs end to end: resume in, ranked paths and an ordered roadmap out, with current openings pulled alongside. It is an in-house build rather than a client engagement, and the hosted demo currently sits behind a sign-in, so the source is the honest way in.
- Resume parsed into skills, roles and seniority signals, then embedded
- FAISS matching against an indexed corpus of real job descriptions
- Career paths ranked with the reasoning for each, not just a list
- Skill gaps stated as have-versus-need against specific postings
- Learning roadmap ordered by payoff rather than alphabetically
- Live openings fetched per request, kept separate from the static index