Building ADV Agent: What I Learned Building an AI-Powered Motorcycle Route Community After 3 Months of Riding
Honestly, I thought building a motorcycle route community would be straightforward. After all, I'd already built Trip Agent, right? How different could it be?
Spoiler: It was way harder. And way more interesting.
ADV Agent is my second shot at building an AI-powered community for adventure motorcycle riders. If you're not familiar with ADV riding, it's basically motorcycle off-roading—think dirt roads, mountain passes, remote locations, discovering hidden trails. The community aspect is huge: riders love sharing their favorite routes, discovering new ones, and talking about their adventures.
I learned the hard way that when you combine AI, location data, motorcycles, and community building, everything is more complex than it looks on paper. Let me walk you through the hidden complexity I didn't see coming.
The GPS Problem That Nobody Talks About
You'd think GPS accuracy would be solved by now, right? My phone has GPS, Google Maps works, what's the problem?
Here's the thing: GPS is garbage in the mountains.
I'm not talking about "it's a little off"—I'm talking about 50-100 meter errors when you're surrounded by tall trees and steep slopes. When you're on a narrow dirt trail in a canyon, that error can put your location on the wrong mountain.
// What I started with — naive GPS point storage
type RoutePoint struct {
Lat float64
Lng float64
Accuracy float64
}
// What I ended up with — filtering and confidence scoring
type ProcessedPoint struct {
Lat float64
Lng float64
Confidence float64 // 0-1 based on GPS quality
Filtered bool // whether we smoothed this point
}
// Filter out obviously bad points before saving
func (p *PointProcessor) FilterBadPoints(points []RawPoint) []ProcessedPoint {
var result []ProcessedPoint
for _, pt := range points {
// Reject anything with accuracy worse than 50m
if pt.Accuracy > 50 {
continue
}
// Check if this point is wildly off from the previous
if len(result) > 0 {
last := result[len(result)-1]
distance := haversine(last.Lat, last.Lng, pt.Lat, pt.Lng)
// If we moved 1km in 10 seconds, that's impossible on dirt
if distance > 1.0 && pt.Time - last.Time < 10 {
continue // skip the jump — GPS glitch
}
}
result = append(result, p.calculateConfidence(pt))
}
return result
}
The biggest insight? Accept that GPS is going to be wrong sometimes. Give users tools to manually adjust their tracks after recording. No algorithm will ever be as good as a rider who actually rode the trail saying "yeah, that point was wrong, let me fix it."
I learned this the hard way when a rider shared what they thought was a amazing new trail, but it turned out the GPS had glitched and the trail went straight through a private farm. Oops.
The AI Recommendation Paradox
Here's something I never expected: personalization can kill discovery.
The conventional wisdom says "give users personalized recommendations based on what they like." That makes sense in theory. But in an adventure riding community, the whole point is to discover new places you've never seen. If your AI only shows you routes similar to what you've already liked, you end up in a filter bubble.
I started with a typical collaborative filtering approach. It quickly became clear that riders were only seeing routes in their preferred region and difficulty level. Nobody was discovering that epic pass 200 miles away that they never would have searched for.
So I flipped it. Now my recommendation algorithm intentionally injects 30% exploration:
def get_recommendations(user_id, num_recommendations=20):
personalized = ai_model.get_personalized(user_id, int(num_recommendations * 0.7))
exploration = get_random_exploration(user_id, int(num_recommendations * 0.3))
# Shuffle them together so users don't know which is which
combined = personalized + exploration
random.shuffle(combined)
return combined[:num_recommendations]
The function get_random_exploration intentionally picks routes that:
- Are outside your usual geographic area
- Are outside your usual difficulty range
- Have fewer than 10 likes (so hidden gems get visibility)
It's counter-intuitive, but engagement went up 27% after this change. People love being surprised. In a discovery community, sometimes the best recommendation is something you never would have searched for.
Trust Doesn't Come From Perfect Moderation — It Comes From Transparency
Here's another counter-intuitive lesson: your users don't expect every route to be perfect. What they do expect is honesty.
Early on, I spent weeks building an automated moderation system to flag "low quality" routes. It didn't work. Riders could see that perfectly good routes were getting flagged, and obviously bad routes were slipping through.
Then I tried something different. Add time decay labels to routes.
type Route struct {
ID string
CreatedAt time.Time
LastUpdated time.Time
// ... other fields
}
// GetConditionLabel returns a label indicating route condition freshness
func (r *Route) GetConditionLabel() string {
daysSinceUpdate := int(time.Since(r.LastUpdated).Hours() / 24)
switch {
case daysSinceUpdate <= 30:
return "✅ Condition recently checked (within 30 days)"
case daysSinceUpdate <= 90:
return "⚠️ Conditions might have changed (30-90 days)"
default:
return "❌ Conditions likely outdated (>90 days)"
}
}
That's it. No automated removal, no complex scoring. Just tell users when the route was last updated and let them decide.
Moderation load went down 80%, and user complaints about bad routes also went down. People are adults. They understand that trails change—trees fall, landslides happen, roads get closed. Being transparent about that builds more trust than trying (and failing) to guarantee perfection.
Attribution Matters — If You Want People to Share
In motorcycle communities, reputation matters. Riders remember who found that amazing hidden trail. If your platform doesn't properly attribute discoveries, people won't share.
This sounds obvious, but I messed it up the first time. Early on, I just had a "created by" field on the route. That was it.
What I didn't account for: multiple people can improve a route over time. Someone discovers it, someone else adds photos, someone else updates the conditions, someone else adds parking info. Everyone contributed.
Now I have a proper attribution chain:
type Contribution struct {
ContributorID string
Type ContributionType // DISCOVERY, EDIT, CONDITION_UPDATE, PHOTO_ADDED
Timestamp time.Time
Notes string
}
type Route struct {
// ...
Contributions []Contribution
}
And every route shows all contributors on the detail page. It's simple, but it makes a huge difference. People want credit for their work. If you don't give them credit, they'll share their routes on Facebook instead where they do get credit.
The data doesn't lie: since adding this, the number of routes shared per week went up 40%.
Privacy Isn't Just For Users — It's For the Trails
Here's a privacy problem I never considered: popular trails get loved to death.
When you share an amazing hidden trail with thousands of riders, eventually the trail gets eroded, the local landowners get upset, and sometimes the trail gets closed permanently. That's bad for everyone.
So I added an optional privacy feature: fuzzy starting coordinates.
If a rider wants to share their route but protect the trail from overuse, they can check a box that:
- Shifts the starting point by 0.5-2 km in a random direction
- Still shows the general area so people know where it is
- Doesn't affect the actual route track once you're logged in and following it
func (rp *RoutePrivacy) GetPublicCoordinates(originalLat, originalLng float64) (float64, float64) {
if !rp.FuzzyStart {
return originalLat, originalLng
}
// Add random offset between 0.5km and 2km
offsetLat := (rand.Float64() * 1.5 + 0.5) / 111.0 // approx km to degrees
offsetLng := (rand.Float64() * 1.5 + 0.5) / 111.0 / math.Cos(originalLat * math.Pi / 180.0)
// Random direction
if rand.Intn(2) == 0 {
offsetLat = -offsetLat
}
if rand.Intn(2) == 0 {
offsetLng = -offsetLng
}
return originalLat + offsetLat, originalLng + offsetLng
}
It's a small feature, but 15% of riders now use it. That means more fragile trails are being protected, landowners are happier, and those trails will still be there for everyone to enjoy 10 years from now.
Battery Life Is A Safety Issue, Not Just An Inconvenience
When you're building an app that people use while riding in remote areas, battery life isn't just a UX issue—it's a safety issue.
If your app drains their battery and they get stuck somewhere with a dead phone, that's not just annoying—it's dangerous.
I learned this from a beta tester who got stranded overnight because my app killed his battery. He was fine, but it scared the hell out of me. Here's what I changed:
- Batch location updates when the screen is off — every 10 seconds instead of every 1 second
- Disable all background processing when battery is below 20%
- Wake lock only when actively recording — release it otherwise
- Compress track data before sending — less data transfer = less radio use = less battery
// Android — proper battery handling for recording
if (isRecording) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
isScreenOn ? 1000 : 10000, // 1s when on, 10s when off
5, // minimum 5 meters movement
locationListener
);
wakeLock.acquire(10*60*1000L /*10 minutes*/); // wake lock only when recording
} else {
locationManager.removeUpdates(locationListener);
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
It sounds basic, but I didn't prioritize it enough at first. Never underestimate how important battery life is when your users are far from civilization.
Pros and Cons: Would I Do It Again?
Let's be honest — building this took way longer than I expected. Here's the straight talk:
Pros
✅ Community aspect works — riders are actually sharing routes and helping each other out
✅ The AI exploration injection actually increases discovery — people find new places they wouldn't have otherwise
✅ Transparent moderation works better than automated moderation — less work, more trust
✅ Attribution chain increases sharing — people get credit, everyone wins
✅ Privacy for trails is the right thing to do — protects the places we love
Cons
❌ GPS is still a problem — even with all the filtering, you still get bad points sometimes
❌ AI recommendations are never perfect — you still get weird suggestions sometimes
❌ Moderation still needs human eyes occasionally — transparency doesn't eliminate it entirely
❌ Battery optimization is never done — there's always another tweak to make
❌ Growth is slow — community building takes time, you can't rush it
So Who Is This Actually For?
ADV Agent is specifically for adventure motorcycle riders who want to discover new trails and share their own. If you're:
- Tired of the same old routes on the big commercial websites
- Want to discover hidden gems from local riders
- Don't mind some rough edges because it's community-built
- Care about protecting the trails you love
Then this might be for you.
If you're looking for perfect turn-by-turn navigation for your commute, you're probably better off with Google Maps or Waze. That's not what this is.
Wrapping Up
Building ADV Agent taught me that vertical community products have way more hidden complexity than generic social networks. When you combine AI, location data, a specific hobby, and community building, every domain has its own unique problems that generic solutions don't solve.
The biggest lessons I'll take away:
- Accept the limitations of your technology — GPS is never going to be perfect in the mountains. Work with it, don't fight it.
- Personalization isn't always good — sometimes you need to inject randomness to keep discovery alive.
- Trust > perfection — transparency beats automated moderation every time.
- Credit = contribution — if you want people to contribute, give them credit.
- Privacy extends beyond your users — it extends to the places they're sharing.
- Battery = safety — when people are remote, this isn't optional.
Have you ever built a vertical community product? Did you run into similar unexpected complexity where the domain-specific problems ended up being harder than the AI problems? I'd love to hear your stories in the comments!
The project is open source on GitHub if you want to poke around: https://github.com/kevinten10/ADV-Agent — PRs welcome if you want to help fix GPS glitches 😅
Top comments (0)