https://github.com/akorablov/hiking_agent_eu
Discover the best places to walk near you. No API keys. No cost. No tracking.
https://github.com/akorablov/hiking_agent_eu
ai-agent api-requests genai geocoder gpt-oss llm ollama python
Last synced: about 1 month ago
JSON representation
Discover the best places to walk near you. No API keys. No cost. No tracking.
- Host: GitHub
- URL: https://github.com/akorablov/hiking_agent_eu
- Owner: akorablov
- Created: 2026-04-18T12:06:27.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-13T20:56:34.000Z (about 1 month ago)
- Last Synced: 2026-05-13T22:37:27.213Z (about 1 month ago)
- Topics: ai-agent, api-requests, genai, geocoder, gpt-oss, llm, ollama, python
- Language: Jupyter Notebook
- Homepage:
- Size: 136 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README

# Hiking & Walking Finder
### AI-powered walk recommendations Β· Anywhere in the world Β· Zero cost
[](https://huggingface.co/spaces/akorablov/trail-finder)
*Detects your location > checks today's weather > discovers every walkable green area nearby > recommends the best outing for the day.*
---
## What it does
Run one command. The agent handles everything:
| Step | What happens |
|------|-------------|
| π **Locate** | Detects your city from your IP address - no GPS, no permissions |
| π€ **Weather** | Fetches a live forecast for your exact coordinates |
| π€ **Decide** | Asks the LLM *"is it a good day to go outside?"* - exits early if not |
| πΊ **Discover** | Finds every walkable green area within 25 km via OpenStreetMap |
| π₯Ύ **Trails** | Fetches hiking routes for all areas in a single efficient request |
| π¬ **Recommend** | Returns top 2-3 picks with practical, local-guide-style reasons |
| π **Chat** | Answers follow-up questions with full conversation memory |
When running **locally** via [main_eu.py](hiking_agent/main_eu.py), location is detected automatically from your public IP address, no permissions needed.
When using the [**live demo**](https://huggingface.co/spaces/akorablov/trail-finder) on Hugging Face, your browser will show a *"Wants to know your location"* popup. Click **Allow**, this is expected and necessary. The reason: Hugging Face servers are hosted in Virginia, USA, so server-side IP geolocation would always return Virginia rather than your actual location. The browser's GPS bypasses this entirely and gives your real position directly.
The coordinates are used only to find nearby green areas. Nothing is stored or tracked.
---
## Architecture
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PIPELINE OVERVIEW β
βββββββββββ¬ββββββββββ¬βββββββββββ¬ββββββββββ¬ββββββββββ¬ββββββββββ¬βββββββββββ€
β STEP 1 β STEP 2 β STEP 3 β STEP 4 β STEP 5 β STEP 6 β STEP 7 β
β β β β β β β β
β Detect β Fetch β LLM β β Query β Fetch β LLM β‘ β Chat β
βLocation β Weather β Go/No-Go β Areas β Trails β Picks β Loop β
β via IP β Open- β yes/no βOverpass β batch β + why β Q&A β
β β Meteo β β OSM β query β β β
βββββββββββ΄ββββββββββ΄βββββββββββ΄ββββββββββ΄ββββββββββ΄ββββββββββ΄βββββββββββ
β if "no"
βΌ
Agent exits early
(skips all API calls)
```
### Data sources - all free, no keys required
| Source | Provides | Cost |
|--------|----------|------|
| [geocoder](https://geocoder.readthedocs.io) | IP > lat/lon/city/country | Free Β· no key |
| [Open-Meteo](https://open-meteo.com) | Hourly weather forecast, global | Free Β· no key |
| [Overpass API](https://overpass-api.de) / OSM | Parks, reserves, forests, trails | Free Β· no key |
| [Ollama (local/online)](https://ollama.ai) / [Groq API](https://console.groq.com) | Cloud LLM inference | Free ~ 14,400 req/day |
> **Total running cost: β¬0**
---
## Project structure
```
hiking-agent/
βββ main_eu.py # Entry point for local use
βββ parks_eu.py # Overpass queries, dedup, distance sorting, fallback
βββ location_eu.py # IP geolocation
βββ weather.py # Open-Meteo forecast + WMO weather code lookup
βββ hiking_agent.ipynb # Annotated walkthrough notebook with 11 live tests
βββ *app.py # Deployment. Streamlit web UI (Hugging Face Spaces / Docker)
βββ *Dockerfile # Deployment. Docker build instructions for HF Spaces
βββ *requirements.txt # Deployment. Python dependencies for cloud deploy
βββ *README.md
```
---
## Quick start
### 1 Β· Install Ollama
Ollama runs LLMs locally - no cloud account needed.
| Platform | Command / Installer |
|----------|-------------------|
| **macOS** | Download `.dmg` from [ollama.ai](https://ollama.ai) |
| **Linux** | `curl -fsSL https://ollama.com/install.sh \| sh` |
| **Windows** | Download `.exe` from [ollama.ai](https://ollama.ai) |
Verify the installation:
```bash
ollama --version
```
> Ollama starts as a background service automatically. If you ever need to start it manually: `ollama serve`
---
### 2 Β· Pull a model
```bash
ollama pull llama3
```
Choose based on your hardware:
| Model | Size | Notes |
|-------|------|-------|
| `phi3` | 2.3 GB | Best for low-spec laptops |
| `mistral` | 4.1 GB | Fast, good all-rounder |
| `llama3` | 4.7 GB | **Recommended starting point** |
| `llama3:70b` | 40 GB | Best quality - needs 48 GB+ RAM |
Check what you have installed:
```bash
ollama list
```
---
### 3 Β· Install Python dependencies
```bash
pip install geocoder requests ollama
```
> Requires **Python 3.9+**. Use a virtual environment if you prefer:
> `python -m venv venv && source venv/bin/activate` (Linux/macOS)
> or `venv\Scripts\activate` (Windows)
---
### 4 Β· Set your model
Open `main_eu.py` and update line 6:
```python
MODEL = "llama3" # must match exactly what `ollama list` shows
```
---
### 5 Β· Run
```bash
python main_eu.py
```
**Example session:**
```
π Location detected: Prague, CZ (50.0880, 14.4208)
π€ Today's forecast: Partly cloudy, average 13Β°C, max precip 0%.
β
Weather approved - searching for nearby areas...
π² Found 6 nearest area(s):
0.7 km [Park] FrantiΕ‘kΓ‘nskΓ‘ zahrada
0.8 km [Nature Reserve] LetenskΓ½ profil
1.0 km [Park] Kampa
1.1 km [Park] VrchlickΓ©ho sady
1.4 km [Forest] DivokΓ‘ Ε Γ‘rka
2.1 km [Protected Area] ProkopskΓ© ΓΊdolΓ
--- Walking & Hiking Recommendations ---
1. Kampa (1.0 km) - A beautiful island park on the Vltava with riverside paths
and views of Charles Bridge. Perfect 30-45 min stroll. Flat, paved, dog-friendly.
2. DivokΓ‘ Ε Γ‘rka (1.4 km) - Prague's wildest green valley. Forested ravines,
a natural swimming lake in summer. Allow 1-2 hours. Mostly easy terrain.
You > Which one is better for dogs?
Agent > Both are excellent for dogs, but Kampa edges it...
```
- #### *View my notebook with detailed steps here > [hiking_agent.ipynb](hiking_agent/hiking_agent.ipynb)**
- #### *Try it live > [huggingface.co/spaces/akorablov/trail-finder](https://huggingface.co/spaces/akorablov/trail-finder)**
---
## Configuration
All settings live at the top of `main_eu.py` and `parks_eu.py`:
| Setting | Default | Effect |
|---------|---------|--------|
| `MODEL` | `llama3` | Ollama model name - must match `ollama list` |
| `MAX_PARKS` | `6` | Maximum number of areas returned |
| `SEARCH_RADIUS_KM` | `25` | How far to look for green areas (km) |
| `TRAIL_RADIUS_KM` | `10` | Trail search radius around each area centre (km) |
| `FALLBACK_RADIUS_KM` | `300` | Max distance for offline fallback parks (km) |
| `MAX_HISTORY` | `20` | Sliding window size for conversation memory |
---
## How it works
### π Location detection
`location_eu.py` resolves your public IP to a lat/lon coordinate using the `geocoder` library. Accuracy is typically 10-50 km, more than sufficient for a 25 km search radius. No GPS, no browser permissions, no personal data stored.
### π€ Weather
`weather.py` queries Open-Meteo for an hourly forecast at your exact coordinates and extracts the **08:00-17:00 daylight window**, summarising it into one sentence:
> *"Today's forecast: Partly cloudy, average 13Β°C, max precipitation probability 0%."*
This is passed verbatim to the LLM as the go/no-go input.
### πΊ Area discovery
`parks_eu.py` queries the Overpass API with **deliberately broad OSM tags** - national parks, nature reserves, protected areas, named forests, regional parks, and local parks. Small and obscure local spots are included intentionally.
Results are then:
- **Sorted by real haversine distance** from your position
- **Deduplicated by proximity** - OSM sometimes tags the same physical place twice with slightly different names (e.g. `"LetenskΓ½ profil"` and `"pΕΓrodnΓ pamΓ‘tka LetenskΓ½ profil"`). Any two entries within 100 m of each other are collapsed into one, keeping the higher-priority type label and the shorter name
### π‘ Resilience
| Layer | Mechanism |
|-------|-----------|
| **Mirror failover** | 3 public Overpass endpoints tried in sequence |
| **Retry logic** | Timeouts and 504s skip to the next mirror automatically |
| **Hardcoded fallback** | ~70 curated national parks across every continent, distance-filtered to your actual position |
| **Graceful degradation** | If trails fail, areas are still recommended; if weather fails, the agent exits cleanly with a message |
### π₯Ύ Trail fetching
Rather than one Overpass HTTP request per park (which caused cascading 429 rate-limit errors), all trails for all parks are fetched in **a single batched union query**. Each trail is then assigned to its nearest park centre.
### π§ Conversation memory
`main_eu.py` maintains memory at two levels:
- **Sliding window** - conversation history is trimmed to `MAX_HISTORY` messages so the LLM context window never overflows across long sessions
- **Pinned facts** - type `remember: I prefer flat walks` to pin a preference that survives history trimming and is injected into every subsequent prompt
---
## β οΈ Limitations
| Area | Detail |
|------|--------|
| **IP geolocation** | City-level accuracy (~10-50 km). Rural users may get the nearest town as their position |
| **OSM coverage** | Western Europe and North America are very well mapped. Parts of Africa, Central Asia, and rural South America have sparser data |
| **Overpass availability** | Public mirrors are best-effort. The fallback list covers major parks on every continent but cannot replicate live OSM richness |
| **Trail difficulty** | Relies on the `sac_scale` OSM tag, which is not universally applied. `Unknown` difficulty means unrated in OSM, not dangerous |
| **Language** | Area and trail names are returned in the local OSM language (Czech, German, etc.) - the LLM handles translation in its response |
---
## License
MIT. Do whatever you like with it. Contributions welcome.
---
Built with OpenStreetMap Β· Open-Meteo Β· Groq Β· Python Β· Docker