0.86MRR (LTR, held-out)
62eval queries
3retrieval channels
4MLOps phases
The problem
Helping buyers answer three questions
Car buyers on najdiavto.com need help with three things: is this listing fairly priced? Can I find cars using natural language? Are results ranked by relevance, not just recency? This project answers all three as a separate ML service that the app calls over HTTP.
Architecture
Four phases, one service
Phase 1Price intelligence
A price-check API that tells buyers whether a listing is fairly priced. Built on a public AutoScout24 DE dataset (~45k listings) with a feature pipeline, segment benchmarks, and a Random Forest model.
- Feature pipeline + hierarchical mileage imputation
- Compared Linear Regression, Random Forest, LightGBM
- Random Forest best at ~25% MAPE (documented dataset limitation)
- POST /check-price returns suggested price + check/warning icon
Phase 2Hybrid search
Natural-language search over live listings. Three retrieval channels fused by Reciprocal Rank Fusion (RRF) — no heavy embedding model needed at ~50 listings.
- BM25 lexical + TF-IDF vector + structured attribute matcher
- Explicit intent (fuel/body/price) becomes a hard filter
- Rule-based eval set: 62 queries in Slovenian, English, German
- Diacritics normalized so 'električni' matches 'electric'
Phase 3Learning to rank
Replaced hand-tuned RRF weights with a learned LightGBM LambdaMART reranker that re-ranks the candidate pool by predicted relevance.
- Features from raw channel scores + listing attributes
- Honest query-grouped holdout (no language leakage)
- MRR 0.79 → 0.86 on unseen query intents
- Serving auto-attaches the reranker with graceful fallback
Phase 4MLOps
Made the running service observable and safe to retrain — monitoring, drift detection, and a publish-only-if-better promotion gate.
- GET /metrics: per-endpoint latency + error stats
- Data-drift detection via PSI + category-share shift
- retrain-all: one command retrains price + search + rank
- Candidate vs incumbent promotion (never silently degrade)
Stack
Tools used
Pythonscikit-learnLightGBMMLflowFastAPIpandaspydanticDockerpytest