Inspiration

Swiss households shop across 5 major grocery retailers — Migros, Coop, Aldi Suisse, Denner, and Lidl. Each has different prices, weekly promotions, and store locations. There is no unified way to compare prices, plan meals based on what's actually on sale, or get recipe recommendations that improve with every interaction. korb.guru solves all of this.

What it does

korb.guru is a web-based grocery assistant that:

  • Scrapes live prices from all 5 Swiss retailers every Monday and Thursday via Apify Actors
  • Compares prices across retailers using Qdrant hybrid search (dense + sparse vectors with Reciprocal Rank Fusion)
  • Discovers recipes through a Tinder-style swipe interface powered by Qdrant's Recommend and Discovery APIs
  • Learns user preferences through an evolving taste vector updated on every interaction via exponential moving average
  • Answers questions about products using RAG (Qdrant retrieval + OpenRouter LLM reasoning)
  • Plans meals with auto-generated grocery lists
  • Shows store locations on an interactive map with 102 stores in the Zurich region

How we built it

Backend: FastAPI (Python 3.11) with 73 REST endpoints across 20 route modules. SQLModel for PostgreSQL, Qdrant Cloud for vector search, and OpenRouter (via Apify proxy) for LLM reasoning.

Frontend: Next.js 16 with TypeScript and Tailwind CSS. Dark theme matching the landing page design. 8 pages: Deals, Recipes, Chat, Compare, Lists, Meals, Stores, and Home.

Data Pipeline: 4 custom Apify Actors scrape Swiss retailers using different strategies per retailer — Playwright for Migros, Docling OCR for Aldi PDFs, API parsing for Lidl, BeautifulSoup for Denner, and ePaper JSON for Coop. A webhook pipeline automatically embeds and upserts new products to PostgreSQL + Qdrant.

Vector Intelligence: 3 Qdrant collections (products, recipes, user_preferences) using 11 Qdrant features including named vectors, scalar quantization, Discovery API with context pairs, and preference-based re-ranking.

Landing Page: Astro SSG with EN/DE language toggle, deployed on Coolify.

Challenges we ran into

  • Each Swiss retailer has a completely different website structure — we needed 5 different scraping strategies
  • PDF extraction for Aldi and Lidl prospekts required OCR with Docling, handling double-character artifacts and layout parsing
  • Balancing cold-start recommendations (no user data) with progressive personalization as swipes accumulate
  • Migrating from a monorepo to a clean deployment-ready structure mid-hackathon

Accomplishments we're proud of

  • Self-improving recommendations: The preference vector evolves with every swipe, and after 5+ interactions the Discovery API activates context pairs for progressively refined search regions
  • Hybrid search quality: Combining semantic understanding (dense vectors) with keyword precision (BM25 sparse vectors) via Reciprocal Rank Fusion produces significantly better results than either alone
  • 102k+ real products from Open Food Facts with Nutri-Score, allergens, and nutritional data
  • 4 published Apify Actors that others can reuse for Swiss grocery data

What we learned

  • Qdrant's Discovery API with context pairs is incredibly powerful for building "taste profiles" — it's like teaching the system your preferences through examples rather than rules
  • Apify's Actor model makes it easy to package scrapers as reusable, serverless components
  • Hybrid search (dense + sparse) with RRF consistently outperforms pure semantic search for product queries where exact names matter

What's next for korb.guru

  • Mobile app (React Native/Expo) for on-the-go shopping
  • Route optimization across multiple stores
  • Receipt scanning for automatic purchase history
  • Collaborative household features (shared lists, meal polls, budget tracking)
  • Expanding beyond Zurich to all of Switzerland

Built With

Share this project:

Updates