raw job feeds in.
one clean, ranked market out.
metahunt turns the scattered, duplicated, unstructured Ukrainian tech job market into a single clean dataset — then ranks it against your CV. No black box: here is the whole system, with the real numbers.
built with
a small, honest LLM + vector pipeline — ~$9/mo to run.
collect
A Temporal schedule (rss-ingest-hourly) wakes up every hour between 06:00–22:00 Kyiv and fans out one child workflow per source. Each runs a durable chain — fetch → parse → extract → finalize — with automatic retries, so a flaky feed never drops a batch.
- RSS pull from Djinni & DOU (2 feeds today; a new source is one config row).
- New items dedup on a content hash before they ever hit the DB (→ bronze).
- Overlapping runs skipped, no double-ingest.
parse
A job post is unstructured prose. We hand the full text to DeepSeek (deepseek-v4-flash, reasoning disabled for speed & cost) through BAML — a typed schema language for LLM calls that validates and retries until the model returns exactly the shape we asked for.
- Stack, split into must-have vs nice-to-have.
- Seniority · role · work format · locations · English level.
- Salary range · employment type · company type (product/outsource/…).
- Flags: has test task, is-tech, reservation.
dedup
The same job is posted on multiple boards, worded differently each time. Collapsing those into one golden record is the hard part — and it runs on two layers.
hard filters — exact, free
- Same source + same id, parsed from the URL, upserts in place.
- RSS content-hash blocks re-ingesting an unchanged item.
soft filters — semantic, vectors
- Each vacancy → a 1536-d embedding (text-embedding-3-small).
- pgvector finds the top-20 nearest by cosine, within a ±45-day window.
- Structural gates: same role & seniority, different companies excluded.
- Join only if pairwise ≥ 0.92 and group-centroid ≥ 0.92; gold tier at ≥ 0.95.
- Canonical = earliest-published member; every source link is kept.
match — the IDF model
Ranking your CV against the market isn't keyword counting. A rare skill should count for more than one everybody lists. So each skill is weighted by inverse document frequency — how uncommon it is across all vacancies — and your match score is the sum of those weights over the skills you share.
relevance = 16.75 · required coverage → tier STRONG
the numbers
Real figures from the running system. Snapshots dated; estimates flagged.