back to work
AI · Geospatial
WohnIQ
Personal Project

A production-grade AI-assisted apartment search platform for Berlin that parses natural-language queries, ranks listings deterministically, enriches results with live commute and POI data, and flags suspicious listings through a hybrid scam detection engine with measurable evals.
FastAPINext.js 14TypeScriptPythonGemini FlashSupabasePostgreSQLpgvectorSQLAlchemyAlembicPydanticTailwind CSSshadcn/uiLeafletimagehashBVG/VBB APIOSM OverpassRailwayVercel
Responsibilities
- Architected and shipped a full-stack AI search platform end-to-end — NLP query parsing, pgvector semantic retrieval, deterministic ranking, real-time enrichment, hybrid scam detection, and deployment.
- Built a central LLM client around Gemini Flash with Pydantic-validated structured output, one re-prompt on failure, and a deterministic fallback — ensuring no unvalidated model text reaches business logic or the user.
- Designed a hybrid scam detection engine fusing three signal sources into an explainable 0–100 risk score: deterministic price z-score rules, perceptual-hash (pHash) duplicate photo detection, and an LLM text pass extracting verbatim evidence quotes.
- Implemented real-time commute enrichment via the keyless BVG/VBB transport.rest API and neighborhood POI enrichment via OSM Overpass, cached per listing and integrated into the ranking score.
- Built a measurable eval harness (parser, ranking, scam) with threshold gates that run in CI, providing ground-truth precision/recall/F1 metrics over a synthetic dataset with labeled scam types and hard negatives.
- Deployed backend on Railway (Docker + FastAPI) and frontend on Vercel (Next.js 14), with Supabase Postgres + pgvector as the single data store for listings, embeddings, and risk assessments.
Key Actions & Decisions
- Enforced a strict language-vs-logic separation: the LLM is limited to three single-shot stateless calls (parse query, read scam text, write explanation). All ranking, scoring, and ordering is deterministic code — same input always produces the same output, making explanations trustworthy by construction.
- Chose a hybrid scam detector over a pure LLM classifier: fusing rule signals, pHash image duplicates, and a structured LLM text pass produces an explainable score with evidence, rather than an opaque verdict.
- Used synthetic listings with ground-truth scam labels by design, enabling a proper confusion matrix eval with per-type recall and hard-negative clearance — something impossible with scraped real-world data.
- Called the Gemini SDK directly behind a thin injectable transport rather than adopting LangChain/LangGraph — three stateless calls don't justify an orchestration framework, and the injectable boundary keeps tests fully offline.
- Kept all schema changes through SQLAlchemy 2.0 models + Alembic migrations with strict guardrails after a near-miss with an accidental drop_table.
Result
Deployed and live. Accepts plain-language Berlin apartment queries and returns ranked results with natural-language explanations grounded in score breakdowns, real-time commute times, neighborhood POI counts, and per-listing scam risk badges. Scam eval achieves measurable precision/recall over four scam types with hard-negative clearance tracked in CI.