Inspiration

As we are international students in NTU, we see how room renting and swapping work and it's not good. Right now, everyone relies on massive Telegram group chats where messages flood in every second, buried under spam and random chatter. We wanted room seekers to just say what they want in plain English and get back ranked rooms with an actual reason, next to a map, making it easier for them to decide. That's where we came up with "NTU NestMatch".

What it does

"NTU NestMatch" works like a dating app: you describe your dream room in plain English, and an open-source LLM parses the query into structured intent while an embedding model turns the query into a vector. By cosine similarity, It then shows you the room of your dreams, along with its location, travel time to NTU, price, and other conditions. Seekers can also shortlist rooms with a heart, draw a freehand boundary on the map to restrict results to a neighborhood, and get a ranked list of rooms with a one-line reason per match.

Room data comes from providers. Providers post a room with fixed tags plus a free-text description and a pin dropped on a map. When a seeker messages them through the built-in chat, they get notified.

Under the hood, search runs a three-stage pipeline: an open-source LLM, a SQL filter with vector similarity search, and a reranking pass — so results render in ~3.3s and explanations fill in a second later.

Challenges we ran into

  • Getting the LLM's parsed intent to behave: an early version of the parser turned "quiet room near campus" into a hard 15-minute-walk, off-campus-only filter, silently cutting 20 rooms to 1. We had to add a grounding step that strips any hard constraint the model inferred but the seeker didn't actually say, while still letting those inferences softly influence ranking.

  • Vector similarity alone wasn't good enough to rank rooms. Embeddings capture semantic meaning well‚ they can tell a "quiet study-friendly single" is topically close to a query about wanting to focus‚ but semantic closeness isn't the same as being the better match. Cosine similarity has no concept of a numeric tradeoff, so it can't weigh "$420 vs. a $650 budget" or "a 12-minute commute vs. a 45-minute one." A room whose description read similarly in meaning routinely outranked a room that was actually the better fit on hard numbers. That's why we split ranking into two stages: the vector step uses semantic similarity only to pick the shortlist (the top candidates worth considering), and a second LLM pass actually reasons about price, commute, and stated preferences to decide the final order and explain each pick. This way, the system is also scalable as cosine similarity can be computed against huge number of room embeddings cheaply.

  • The search pipeline started out slow enough to feel broken‚ 3 sequential LLM calls (parse, embed, rerank) meant a seeker could be staring at a blank page for many seconds before anything appeared, which is a hard thing to ship as a "search" feature. We had to actually profile where the time went (turned out to be ~93% LLM, database round trips were negligible) and attack it from a few angles at once: running the parse and embedding calls in parallel since neither depends on the other's output, streaming the final reranking call so the new result order arrives in ~120 tokens while the ~700 tokens of match reasons are still being generated, and awaiting only the order so cards render in their final position immediately. That took perceived time-to-content from over 7 seconds to roughly 3-4 seconds, with reasons filling in a beat later per-card instead of blocking the page.

  • Getting order to stream before reasons required a strict prompt contract: reasons run to hundreds of tokens per listing, so if the model emitted them first, the ordering wasn't usable until the entire response finished. We had to explicitly demand "order" before "reasons" in the schema.

Accomplishments that we're proud of

  • We ran an embedded LLM to turn the user's search query into a vector, so that search algorithm works much quicker. The order of rooms render first with ranking, then reasons arrive, nothing reshuffles after paint. The room ranking matches what user ask for.
  • Similar rooms appear at the bottom of a room page, computed from similar embeddings, fast and little to no cost. I learned to implement this from observing other hotel/room renting production websites, which lead to more conversions.

What we learned

  • Notice problem around us and find the way to tackle them within our ability. I have the domain knowledge, my friend has the technical know-how. So we combined what we each knew and built it.
  • By routing LLM calls through OpenRouter (using lightweight, low-cost models like Gemini 2.5 Flash-Lite) and relying on Google Maps' free tier for map rendering, we can keep the cost of a single search to roughly $0.0076. And even at 100 users, total spend is under a dollar.

What's next for build beyond infinity

We divide our next goals into two areas: technical build and business model. On the technical side, we want to support mobile for the freehand area-drawing tool, as well as work on verification, payments, and review/reporting flows. For the business model, we want to bring this to the real world. Right now, the app is made for students living on campus, but our long-term vision is to sell this platform directly to universities worldwide. Then, we can integrating with their housing regulations to make room renting and swapping official.

Built With

  • embeddings
  • google-maps
  • google-oauth
  • llm
  • next.js
  • open-router
  • prisma-orm
  • supabase
  • tailwind-css
  • typescript
  • vector-database
Share this project:

Updates

Submission history