AuraGo Food
Semantic food discovery — search that understands intent, not keywords
Aug 2025 — Dec 2025 · Remote
In one paragraph
AuraGo Food is a delivery platform whose search understands meaning rather than keywords. Ask it for “something light and spicy for a rainy evening” and it returns ranked, actually-deliverable dishes — because retrieval runs over multimodal embeddings of dish text and food photography, filtered by delivery radius, and re-ranked by an LLM. Live on Google Play.
The problem
Food delivery search runs on inverted indexes — the same technology as a document search engine. Type the literal name of a dish and it works well. Type what you actually mean and it collapses, because there is no keyword to match on.
The failure mode isn't an error message, which is what makes it easy to miss. It's decision fatigue: the user scrolls through irrelevant results, gets tired, and abandons the search. The business sees a session with no order and no obvious cause.
Measured on an intent-heavy query set, keyword search scored roughly 61% precision at rank 5. That is the number the whole project exists to beat.
The pipeline
Four stages, in this order, and the order is the design:
1 · Intent classification, before anything expensive
A HuggingFace MiniLM model orchestrated through LangChain decides whether an incoming message is conversation or a genuine dish request. A search only fires when the user actually wants a recommendation.
Critically the classifier emits a probability, not a binary. Below a confidence threshold the system asks a clarifying question instead of guessing, because firing an irrelevant recommendation is worse than admitting uncertainty — a wrong dish suggestion actively erodes trust in the feature, while a clarifying question costs one tap. The threshold was tuned empirically against several hundred deliberately ambiguous queries until misrouting dropped to an acceptable rate.
2 · Geospatial filtering, before semantics
MongoDB's geospatial index narrows the candidate set to dishes inside the user's delivery radius before semantic search runs. Ranking dishes that cannot physically reach the user is wasted compute and a worse product — a perfect match 40km away is not a match.
3 · Multimodal retrieval
Voyage AI generates text and image embeddings for every dish, stored in MongoDB Atlas with native vector search. Retrieval runs against dish descriptions and food photography in the same query.
The multimodal part earns its place on visually-driven queries. “Something that looks crispy and golden” is a statement about an image, and text-only retrieval has nothing to match it against. Combining both modalities measurably outperformed text alone on that class of query.

4 · LLM re-ranking
Retrieved candidates go to Google Vertex AI through LangChain, which re-ranks and personalises. Re-ranking operates only on the top-k from vector search — typically 20 candidates — never the full catalogue, so the LLM call has a small, bounded payload.
Why MongoDB Atlas instead of a dedicated vector database
This is the decision I get asked about most, and the answer is colocation rather than benchmark scores.
The dish catalogue already lived in MongoDB. Using Atlas's native vector search meant filtering by geospatial proximity, availability and delivery radius in the same query as semantic retrieval — one round trip.
Approach | What a query costs |
Atlas native vector search | One round trip: geo filter + availability + semantic retrieval in a single query against one system |
Dedicated vector store | Two network calls: query relational filters in MongoDB, then pass candidate IDs out for semantic reranking, with serialisation overhead in between |
A dedicated store wins on approximate-nearest-neighbour performance at extreme scale. At this scale, eliminating the round trip mattered more than raw ANN throughput, and one system is materially simpler to operate than two. If the catalogue grew by orders of magnitude I would revisit it — the architecture deliberately keeps the vector store swappable.
Results
92% relevance — precision at rank 5, measured over a hand-labelled set of 10,000 query–result pairs, against roughly 61% for keyword search on the same intent-heavy queries.
Intent-based queries that returned nothing usable under keyword search returned accurate, ranked recommendations consistently.
40% reduction in average time spent searching before placing an order.
25% increase in conversion from search to order, and an 85% satisfaction score in testing.
Full pipeline — classification, retrieval and re-ranking — under 800ms for the large majority of queries, with a cache short-circuiting semantically-near repeat queries.
What I would do differently
The 92% figure comes from my own labelling against my own rubric. That is the standard retrieval evaluation method and it carries obvious subjectivity — I decided both what counted as relevant and whether each result met it.
With hindsight I would have had a second labeller annotate a sample and reported inter-rater agreement alongside the headline number, so the metric could be trusted by someone who wasn't me. The relative comparison against keyword search on the identical query set is the more defensible claim, and it's the one I'd lead with now.
Stack
Go · Flutter · MongoDB Atlas (vector + geospatial) · LangChain · HuggingFace MiniLM · Voyage AI multimodal embeddings · Google Vertex AI · Redis · Google Cloud Run · GitHub CI/CD