Case study · Avori
Avori
An AI-native travel agent built end to end - every itinerary verified before it reaches the traveler.
- Role
- Engineer (AI / Full-Stack)
- Timeline
- 2026 – Present
- AI Agents
- Flutter
- NestJS
- WebSocket
- full product, built end to end
- 0 → 1
- deterministic checks on every itinerary
- 4
- platforms: iOS & Android
- 2
Overview
Avori is an AI-native travel planner I'm building end to end: a Flutter app for iOS and Android, a NestJS/TypeScript backend on PostgreSQL, Redis, and GCP, and a multi-stage LLM planning-and-repair pipeline that verifies every itinerary before it reaches the traveler. Built independently, outside my role at Visa.
Screens shown are design mockups - photos and the map are placeholders.
Architecture
- 1. Intent extraction
- 2. Places & Routes retrieval
- 3. LLM planning
- 4. Deterministic checks
- 5. Repair pass
- 6. WebSocket streaming
- 7. Revision loop
Challenge
Most AI travel tools dump a list of attractions and call it a plan. A real itinerary has to respect opening hours, travel time between places, and the traveler's actual intent - and a model's first draft can't be trusted to get all of that right.
It also has to arrive fast enough to feel interactive, not like a batch job, and survive a dropped connection or an app relaunch mid-generation.
Approach
I architected a multi-stage LLM planning-and-repair pipeline that pairs model reasoning with deterministic checks - venue hours, routing, deduplication, and schedule feasibility - so every itinerary is verified before it reaches the user. Tiered model routing across OpenAI and Gemini sends each pipeline stage to the cheapest model that meets its quality bar, balancing output quality, latency, and inference cost.
Generation streams in real time over WebSocket (Socket.IO), with Redis and BullMQ running the background jobs. The app renders the itinerary progressively - a skeleton first, then day previews - and reconciles against a server snapshot after reconnects or relaunches, so interrupted sessions resume without losing progress.
The client is a Flutter app (Riverpod, go_router, Google Maps): a conversational planning UI plus a map-based itinerary editor with reorder, swap, per-stop notes, and closure alerts, backed by offline-aware states and local persistence with Hive. Edits respect user-set constraints - stops a traveler locks or books are preserved through every AI edit and repair pass, with booking and itinerary writes committed in one transaction under optimistic version guards.
Underneath, the Google Places and Routes integrations run with caching and fallback logic, and a curated city-data pipeline - automated verification plus monthly staleness checks in CI - keeps recommendations current across multiple cities.
Where it stands
Avori is an ongoing 0→1 build spanning the mobile app, the backend, and the AI pipeline. The idea running through all of it: treat the model as a planner whose output has to pass deterministic verification, then build the product surface - live streaming, resumable sessions, constraint-preserving edits - around that guarantee.