← Selected work

Harvest: from grocery list to local producers

Abstract

Harvest flips local-food search around: instead of browsing directories producer by producer, the user gives their grocery list in natural language, their position and a radius. An agent identifies the requested products, a geospatial query finds the local producers who sell them, and the answer arrives as action-ready cards: address, distance, opening hours, contact. Designed, built and operated by Myelink.

01The problem

Buying local today means browsing directories: producer by producer, page by page, hoping one of them sells what you need. Harvest takes the problem from the consumer's side: you start from the grocery list ("eggs, beef, wheat", or even "I want proteins and carbs"), and the producers are the ones who show up.

02The approach

Three inputs: the list in natural language, the browser's position, an adjustable search radius. An agent translates the user's free-form terms into real catalogue products, writing its own SQL queries against a whitelist of tables. Finding the producers, however, is fully deterministic: a PostGIS geospatial query filters by real distance, aggregates the covered products per merchant and ranks nearest first.

The answer is rendered as producer cards: address, distance, opening hours, phone, website, directions. All in the detected language of the question. The database grows through contributions: submitted producers go through a moderation queue before publication.

03The architecture

Two services: a Django frontend handling accounts, chat sessions and rendering, and a FastAPI service hosting the LangGraph graph. They talk over authenticated REST and share a PostgreSQL/PostGIS database whose schema belongs to the frontend. Conversations keep their memory across turns through Redis checkpoints.

navigateur liste de courses + position + rayon front Django sessions, SSE, rendu des cartes service FastAPI - graphe LangGraph product matcher agent SQL, tables en liste blanche sortie structuree produits apparies, schema Pydantic recherche marchands PostGIS: distance, couverture - sans LLM formatter genere le minimum, le reste re-attache en Python PostGIS partage produits, marchands, lieux Redis memoire de conversation cartes producteurs: adresse, distance, contact encadre bleu = appel LLM
Fig. 1 - The graph: two LLM calls bracket a fully deterministic geospatial search.

04The design choice

The slowest part of an LLM pipeline is decoding, token by token. Harvest's formatter therefore only generates what needs generating: the detected language, a greeting, and a short description per producer. Everything else, coordinates, addresses, contacts, product lists, is re-attached in Python from the database, byte for byte. The model produces a few percent of the answer; the code guarantees the other 95.

The same discipline applies to guardrails: the agent can only read whitelisted tables, off-topic requests short-circuit the graph through sentinel tags filtered from display, and model output is escaped before any rendering, with injection through malicious content treated as a nominal case.

05The stack

LayerChoice
FrontendDjango, vanilla ES modules, SSE, browser geolocation
AgentFastAPI + LangGraph, Pydantic structured outputs
GeospatialPostgreSQL + PostGIS: real distance, per-merchant aggregates
MemoryRedis checkpoints, conversation persisted across turns
Inferencemodels through an OpenAI-compatible gateway, temperature 0 for matching
ObservabilityLangfuse: tracing and versioned prompt management
Qualityaround a hundred Python and JavaScript tests, CI on every branch

06Status

Harvest is in production, operated by Myelink, with producer coverage expanding region by region, fed by moderated contributions.

@software{harvest,
  author = {Vinceslas, Medhy},
  title  = {Harvest: de la liste de courses aux producteurs locaux},
  note   = {En production, Myelink EURL}
}
Visit Harvest Get in touch