CareerMatch Agent

CareerMatch Agent

← Back to projects

Context: Independent Machine Learning Engineering Project · 2026–Present
Status: Active technical alpha
Focus: ML systems · Semantic retrieval · Hybrid ranking · LLM evaluation · Bounded agentic workflows

CareerMatch Agent is an end-to-end AI-assisted job-search and recommendation system that turns a CV PDF and natural-language preferences into ranked job opportunities with structured, evidence-grounded suitability reports.

I built the system around a deliberate principle: LLMs should assist with interpretation, planning and explanation, but they should not silently override explicit user constraints or invent unsupported candidate/job claims.

Instead of relying on a single prompt to decide whether a job is a good match, CareerMatch combines:

View source on GitHub →

Why I built it

Job matching is a useful test case for a broader AI-engineering problem: how do you combine probabilistic models with deterministic rules when some requirements must remain authoritative?

A fully LLM-driven matcher can produce fluent explanations while still:

CareerMatch separates these responsibilities. Hard constraints remain deterministic; semantic models provide retrieval and similarity signals; LLMs handle tasks where language understanding adds value; generated findings are then checked against the evidence supplied to the evaluator.

The result is an AI workflow designed to be inspectable, testable and replaceable at each stage rather than one opaque model call.

End-to-end architecture

The current workflow is:

CV PDF + Natural-Language Preferences
            +
 Optional HackerRank Evidence
            +
 LLM / Job-Provider Selection
            ↓
 Candidate Profile Extraction
            ↓
 Preference Extraction + Validation
            ↓
 Search Planning
            ↓
 Multi-Provider Job Retrieval
            ↓
 Deduplication + Normalization
            ↓
 Deterministic Filtering
            ↓
 Semantic / Hybrid Ranking
            ↓
 Evidence Bundle Construction
            ↓
 Grounded LLM Evaluation
            ↓
 Ranked Job Recommendations

The workflow is orchestrated through a bounded LangGraph agent. If too few suitable jobs survive filtering, the system can broaden and rerun the search, but the number of attempts is explicitly limited to prevent uncontrolled agent loops.

The FastAPI backend owns the workflow and domain logic, while the Streamlit frontend acts as a user-facing client. This keeps the core system reusable outside the UI.

Deterministic constraints before AI judgment

Explicit user preferences are treated as authoritative.

Before ranking, jobs can be rejected for constraints such as:

Natural-language preferences are first converted into a structured schema and then deterministically validated. Missing information is kept unknown or unrestricted rather than silently replaced with assumptions.

This prevents a semantically attractive job from being promoted simply because an LLM decides it is “close enough” to a requirement the user explicitly provided.

Multi-provider retrieval with a common job model

CareerMatch currently integrates:

A composite provider layer allows several sources to participate in the same run.

Each provider implements a common interface and its response is normalized into a shared job representation before filtering and ranking. This isolates provider-specific retrieval logic from downstream matching logic and makes the retrieval layer extensible.

Users can select the active providers for each workflow run.

Semantic and hybrid ranking

Jobs that pass deterministic filtering are ranked using a combination of signals rather than semantic similarity alone.

The current hybrid score combines:

Semantic matching uses:

sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2

Candidate and job text are represented through bounded evidence chunks so that ranking and later explanation can retain interpretable supporting context.

Embedding-model selection

I evaluated a larger multilingual MPNet model during calibration, but rejected it because it reduced ranking quality while increasing runtime.

During an earlier embedding-model calibration experiment:

Model / rankingnDCG@5nDCG@10Precision@10
MiniLM hybrid0.7940.8140.900
MPNet hybrid0.7030.7590.800

This was an important engineering result: a larger embedding model was not assumed to be better simply because it was larger.

Evidence-grounded LLM evaluation

The evaluator does not receive an unconstrained prompt asking whether a candidate is suitable.

Instead, CareerMatch constructs an explicit evidence bundle containing items such as:

Generated findings must reference supplied evidence IDs.

A deterministic validation layer checks:

Unsupported strengths, gaps and risks are removed rather than presented as valid findings.

The LLM is therefore responsible for structured interpretation of known evidence, not for inventing the evidence itself.

Multi-LLM support without vendor lock-in

CareerMatch implements a common structured LLM interface supporting:

The provider and model can be selected at runtime through the UI or API.

The selected provider is reused consistently across LLM-dependent stages, including candidate-profile extraction, preference extraction, search planning/replanning and grounded suitability evaluation.

Non-LLM stages—including PDF extraction, job retrieval, deterministic filtering, SentenceTransformer ranking, LangGraph orchestration and grounding validation—remain independent of the selected LLM vendor.

This separation allows the same workflow to run with local or hosted models without coupling the core architecture to one provider.

Benchmarking instead of subjective inspection

I built a benchmark framework so changes to the filtering and ranking pipeline can be evaluated quantitatively rather than judged only by manually inspecting recommendations.

The benchmark measures:

LLM-generated suitability reports are evaluated separately and are not part of the filtering/ranking benchmark.

Development benchmark suite

The frozen development suite contains four synthetic candidate scenarios with 20 labelled job postings each, for a total of 80 job cases:

Each scenario is evaluated with the same three ranking configurations:

All three configurations achieved 1.000 filtering F1 and 1.000 rejection-reason F1 across the four development scenarios.

For ranking:

ConfigurationMean nDCG@5Mean nDCG@10
Hybrid0.87670.8976
Semantic only0.82770.8737
Deterministic only0.91290.9309

The development suite was used for calibration and regression analysis, so these values should not be interpreted as independent estimates of real-world performance.

Frozen holdout evaluation

After development, I evaluated the unchanged filtering and ranking pipeline on a separate frozen holdout suite containing 40 labelled job postings across two previously unscored synthetic candidate scenarios:

The holdout datasets, labels and checksums were frozen before the benchmark was executed. The ranking configurations were not retuned in response to the holdout results.

Filtering performance across the two holdout scenarios was:

MetricMacro mean
Filtering F10.955
Rejection-reason F10.947

Ranking performance was:

ConfigurationMean nDCG@5Mean nDCG@10
Hybrid0.9010.948
Semantic only0.8970.940
Deterministic only0.9590.936

Development and frozen holdout ranking benchmark across hybrid, semantic-only and deterministic-only configurations

The results show that ranking behaviour depends on both the scenario and the evaluation cutoff. Deterministic-only achieved the highest mean nDCG@5 on the holdout suite, while hybrid ranking achieved the highest mean nDCG@10.

These results measure agreement with the frozen synthetic relevance labels. They do not establish that one configuration is generally superior or represent real-world hiring accuracy.

What the evaluation revealed

The holdout evaluation exposed two role-based filtering disagreements: one unexpected acceptance in each of the two scenarios.

That matters because the purpose of the benchmark is not to produce perfect-looking metrics. It is to expose concrete failure cases that can be preserved as regression tests and addressed in later engineering work without rewriting the original benchmark.

The benchmark therefore serves as both a performance-measurement framework and a mechanism for identifying weaknesses in filtering and ranking logic.

Ranking latency measurements remain exploratory because model initialization and embedding warm-up are not yet standardized across repeated runs.

Reusable workflow state

CV parsing and structured preference extraction can be relatively expensive, especially when LLMs are involved.

CareerMatch can export intermediate workflow state as structured JSON, including:

A prepared workflow can later be reloaded while changing the active LLM, job providers and search configuration.

This avoids unnecessarily repeating candidate-side preprocessing during experimentation and makes provider/model comparisons easier.

User-facing automated workflow

The Streamlit interface exposes the complete pipeline.

A user can:

  1. upload a CV,
  2. describe job preferences in natural language,
  3. optionally provide external assessment evidence,
  4. select an LLM provider/model,
  5. choose one or more job providers,
  6. configure bounded search behavior,
  7. run the complete workflow,
  8. inspect the interpreted profile and preferences,
  9. review search and agent metrics,
  10. inspect ranked jobs and grounded suitability reports.

Career Match UI

Engineering quality

The repository is structured as an application rather than a notebook-only prototype.

It separates:

Automated GitHub workflows include:

The development workflow uses:

The project also explicitly keeps CV-derived artifacts and API credentials outside public version control because workflow outputs may contain personal information.

Key engineering decisions

Deterministic where requirements are hard.
Location, seniority, language and explicit inclusion/exclusion rules should not depend on model judgment.

Semantic where wording varies.
Embeddings help match related skills and responsibilities even when job descriptions use different terminology.

LLMs where interpretation adds value.
Candidate extraction, search planning and explanation benefit from language understanding, but outputs remain structured.

Grounding after generation.
LLM findings must point back to known evidence and unsupported claims are rejected.

Provider interfaces over vendor-specific logic.
Both LLM and job-retrieval layers can change without rewriting downstream workflow logic.

Bounded agents over uncontrolled loops.
Search replanning is useful, but agent behavior needs explicit limits.

Benchmarks over intuition.
Ranking configuration and embedding-model choices are evaluated quantitatively instead of selected from subjective examples.

Current limitations

CareerMatch remains a technical-alpha project, not a production job-search platform.

Current limitations include:

These limitations define the next engineering work rather than being hidden behind the AI layer.

What I contributed

I designed and implemented the system end to end, including:

What this project demonstrates

CareerMatch is my strongest example of machine-learning and AI systems engineering.

It demonstrates that I can work across the boundary between ML and software engineering:

Most importantly, the project is built around the idea that useful AI systems need explicit constraints, measurable evaluation and verifiable evidence—not only good prompts.

Core technologies: Python · FastAPI · Pydantic · LangGraph · SentenceTransformers · Hugging Face · OpenAI · Gemini · Ollama · Streamlit · pytest · mypy · Ruff · GitHub Actions

View source on GitHub →

← Back to projects