Instructions to use lacuna-ai/lacuna-celer with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use lacuna-ai/lacuna-celer with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('text-classification', 'lacuna-ai/lacuna-celer');
Lacuna Celer
Mind the gap, before you apply.
Code, training pipeline and the Chrome extension: github.com/cucoleadan/lacuna-ai.
Lacuna Celer scores how a job posting fits a CV, in the browser, on the user's device. It is the fast tier (68M parameters, 76 MB) of the Lacuna Chrome extension; Lacuna Altus is the large one.
It is Ettin-68M fine-tuned with five heads and distilled from Lacuna Altus: trained on the LLM labels plus Altus's predictions on 13,970 extra unlabelled CV × posting pairs.
| File | Precision | Size | Note |
|---|---|---|---|
model_q4f16.onnx |
4-bit weights, fp16 | 76 MB | Default. Same argmax as PyTorch on 94-99% of validation pairs per head |
model_fp16.onnx |
fp16 | 137 MB | Identical predictions to PyTorch |
What it does
Reads a CV and a job posting together (up to 4,096 tokens) and predicts, in one pass:
| Head | Classes | Meaning |
|---|---|---|
verdict |
apply / stretch / skip | Overall fit for this candidate |
location |
yes / unclear / no | Can this person work where the job requires? |
seniority |
below / at / above | Candidate's level vs. the level the posting asks for |
must_met |
no / yes | Are the posting's must-have requirements covered? |
line_met (per job line) |
none / yes / partial / no | For each line of the posting: is it a requirement, and does the CV cover it? |
line_must (per job line) |
probability | Is that requirement a must-have? |
The per-line heads are what make the gaps concrete: the job's must-have lines the CV does not cover.
It is a triage aid for job seekers, used on their own CV. It ranks postings so you read the promising ones first and says where a posting does not fit you. It does not decide anything.
Intended use and limits
- For job seekers only. Do not use it to screen, rank or reject candidates. It was not built or evaluated for that, and hiring decisions about people need human judgement (and, in many places, legal safeguards).
- Labels come from an LLM, not from recruiters. The model learned DeepSeek V4.1 Flash's judgement; it can be no better than that judgement, and has its biases.
- Synthetic CVs. Training CVs were written by an LLM from realistic career paths; real CVs are messier. Normalise your CV the same way (see Input format).
- English only, mostly tech, product, data, design, marketing, sales and operations roles, Europe-heavy locations.
- Weak spots: "too senior for me" postings are often missed; personal taste (industry, company type) is not modelled; US-only remote roles are treated as "not eligible" for people outside the US, which is right for employees but not for contractors.
- Location
noshould not hide a job outright unless the model is confident; the reference extension hides only at P(no) ≥ 0.8 and shows "unclear" otherwise.
Training data
- About 82,000 public job postings from company career sites and job boards (not redistributed).
- 2,217 synthetic CVs, each built from a sampled career path (roles taken from real postings, dates, location, languages, CV style and quality) and written by an LLM.
- 12,699 labelled CV × posting pairs. Per CV: its target job, a similar job, one at another level, one outside the person's work location, plus over-qualified and stretch pairs. Split by CV: 10,171 train / 1,270 validation / 1,258 test.
- Labels: DeepSeek V4.1 Flash read each pair with a fixed rubric (requirements, met / partial / not met, location eligibility, years and level); the verdict is derived from those answers by rules, not asked directly.
- CVs pass through one normalisation (
normalize_cv: block-aware text, ALL-CAPS headings, "- " bullets, contact details removed) in training and at inference.
Input format
[CLS] normalize_cv(cv) [SEP] job [SEP] # truncated longest-first to 4,096 tokens
job = "Title: …\nCompany: …\nLocation: …\nWorkplace: …\n\n" + posting as markdown
ONNX inputs input_ids, attention_mask (int64, [batch, seq]). Outputs: verdict, location, seniority, must_met logits [batch, k]; line_met [batch, seq, 4] and line_must [batch, seq] per token. Average the per-token line outputs over each posting line's tokens (lines of 15+ characters), then softmax / sigmoid. Divide head logits by the temperatures in jobfit.json before the softmax for calibrated probabilities. Labels and temperatures are in jobfit.json.
Evaluation
Macro-F1 on held-out CVs (the test CVs never appear in training, also not in the distillation pairs).
| Model | Params | Verdict | Seniority | Location | Must-haves | Req. lines |
|---|---|---|---|---|---|---|
| Lacuna Celer (this model, PyTorch) | 68M | 0.686 | 0.718 | 0.903 | 0.810 | 0.709 |
| Lacuna Celer, 3 seeds mean | 68M | 0.665 | 0.718 | 0.899 | 0.808 | 0.706 |
| Ettin-68M, same data, no distillation | 68M | 0.654 | 0.667 | 0.891 | 0.804 | 0.686 |
| Lacuna Altus (teacher) | 395M | 0.671 | 0.731 | 0.901 | 0.806 | 0.715 |
Test split, 1,258 pairs. Distillation lifts seniority clearly (+0.05, about three times the seed-to-seed spread); on verdict, location and must-haves it is within noise. The student reaches the teacher.
On one real user's own past apply/skip decisions (private), Celer ranked applied postings above skipped ones about as well as Altus, and far better than off-the-shelf similarity (TF-IDF, bge-m3, gte-modernbert-base), which cannot tell where a person may work. That check is a single person, not a benchmark.
Training: 3 epochs, lr 1e-4, AdamW, 4,096 tokens, soft-target weight 0.5, 2× T4 GPUs, about 2 hours.
Use
The Lacuna extension does everything above (CV normalisation, encoding, line averaging, calibration) in the browser with ONNX Runtime Web on WebGPU. A JavaScript port of the CV normaliser and encoder is tested against the Python pipeline (identical token ids and line ranges on 200 validation pairs).
Model tree for lacuna-ai/lacuna-celer
Base model
jhu-clsp/ettin-encoder-68m