Spot On is a fast, parallel multi-agent travel recommendation system that provides curated suggestions for:
- Restaurants - Best dining options
- Travel Spots - Must-see attractions
- Hotels - Accommodations with per-night pricing when available
- Car Rentals - Vehicle rental options
- Flights - One-way or round-trip flights
graph TD
ParseRequest --> RestaurantAgent
ParseRequest --> AttractionsAgent
ParseRequest --> HotelAgent
ParseRequest --> TransportAgent
RestaurantAgent --> |enrichment| EnrichAgent
RestaurantAgent --> |skip| QualitySplit
AttractionsAgent --> |enrichment| EnrichAgent
AttractionsAgent --> |skip| QualitySplit
HotelAgent --> |enrichment| EnrichAgent
HotelAgent --> |skip| QualitySplit
TransportAgent --> |enrichment| EnrichAgent
TransportAgent --> |skip| QualitySplit
EnrichAgent --> |gap>50% & loops<2| EnrichAgent
EnrichAgent --> QualitySplit
QualitySplit --> BudgetAgent
BudgetAgent --> END
Execution Flow:
- ParseRequest - Validate constraints and derive query context
- 4 Domain Agents (parallel) - Tavily search + LLM structured normalization per category
- EnrichAgent (optional) - Tavily extract + targeted follow-up search to fill missing fields (bounded loop)
- QualitySplit - Demotes items missing critical fields (name, url) or all important fields into
references - BudgetAgent - Produces
report.total_estimated_budgetand materializesfinal_output
.
├── README.md
├── docs/
│ └── TECHNICAL_DOC.md
├── backend/
│ ├── app/ # FastAPI app + LangGraph workflow + agents
│ ├── tests/
│ ├── Dockerfile # AWS/production container
│ └── .elasticbeanstalk/ # Elastic Beanstalk config (Docker platform)
└── frontend/
├── app/ # Next.js UI
└── lib/
- Install dependencies:
cd backend
uv venv && source .venv/bin/activate
uv pip install -e .- Set environment variables:
cp .env.example .env- Start MongoDB:
# Using Docker:
docker run -d -p 27017:27017 --name travel-mongo mongo:7
# Or using local MongoDB:
mongod --dbpath /path/to/data-
Create MongoDB Atlas cluster:
- Go to https://cloud.mongodb.com
- Create free M0 cluster
- Create database user (username + password)
- Whitelist your IP (or 0.0.0.0/0 for dev)
-
Get connection string:
- Click "Connect" → "Connect your application"
- Copy connection string:
mongodb+srv://user:pass@cluster.mongodb.net/
-
Update .env:
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/
DB_NAME=YOUR_DB_NAME- Start backend:
uv run uvicorn app.main:app --reload
# Backend runs on http://localhost:8000- Install dependencies:
cd frontend
bun install
cp .env.example .env.local # Set NEXT_PUBLIC_API_URL- Start dev server:
bun run dev # Frontend runs on http://localhost:3000- Open http://localhost:3000
- Fill in the form:
- Origin: e.g., "NYC, Osaka, etc"
- Departing Date: e.g., "2026-03-15"
- Returning Date: (optional) e.g., "2026-03-18"
- Vibe / Budget / Climate: used to recommend a destination first
- Deep Research: toggles
skip_enrichment— when enabled, EnrichAgent runs to fill missing fields
- Click "Find Recommendations"
- The UI will:
- call
POST /recommendto choose a destination, then - call
POST /runsto execute the LangGraph workflow and stream progress
- call
Request:
{
"origin": "San Francisco",
"departing_date": "2026-03-15",
"returning_date": "2026-03-20",
"vibe": "cultural and historic",
"budget": "moderate",
"climate": "warm"
}Response:
{
"destination": "Kyoto",
"reasoning": "Rich cultural heritage with temples, traditional districts, and spring weather"
}Request:
{
"constraints": {
"origin": "San Francisco (SFO)",
"destination": "Tokyo (NRT)",
"departing_date": "2026-03-15",
"returning_date": "2026-03-20"
},
"options": {
"skip_enrichment": false
}
}Response:
{
"runId": "run_abc123"
}Response:
{
"runId": "run_abc123",
"status": "done",
"updatedAt": "2026-02-14T10:30:00Z",
"progress": {
"nodes": {
"RestaurantAgent": { "node": "RestaurantAgent", "status": "end" },
"AttractionsAgent": { "node": "AttractionsAgent", "status": "end" }
}
},
"constraints": {
"origin": "San Francisco (SFO)",
"destination": "Tokyo (NRT)",
"departing_date": "2026-03-15",
"returning_date": "2026-03-20"
},
"final_output": {
"restaurants": [{ "name": "Sukiyabashi Jiro", "cuisine": "Sushi", "area": "Ginza" }],
"travel_spots": [{ "name": "Senso-ji Temple", "kind": "Historic", "area": "Asakusa" }],
"hotels": [{ "name": "Park Hyatt Tokyo", "area": "Shinjuku", "price_per_night": "$450" }],
"car_rentals": [{ "provider": "Toyota Rent a Car", "vehicle_class": "Compact" }],
"flights": [{ "airline": "ANA", "route": "SFO-NRT", "price_range": "$800-1200" }],
"constraints": { "origin": "San Francisco (SFO)", "destination": "Tokyo (NRT)" },
"references": [],
"report": { "total_estimated_budget": "$3,200-4,800" }
},
"warnings": [],
"error": null
}Server-Sent Events stream with real-time updates.
Event types:
node—{ node, status, message? }per agent lifecycleartifact—{ type: "constraints" | "final_output", payload }materialized datalog— progress messages
Best-effort cancellation of a running workflow.
Export completed run results as a PDF document (status=done only).
Export completed run results as an Excel spreadsheet (status=done only).
Happy travels!