尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗I was traveling in Taiwan and ran into a problem I couldn't shake: I'd open Google Maps, search for food nearby, and the top results were clearly tourist traps — high ratings, thousands of reviews, packed with visitors, but locals I talked to had never heard of them.
The star rating wasn't measuring quality. It was measuring tourist attention volume.
I came back and noticed the same problem in NYC. One of the most reviewed cities in the world, but the rankings are polluted by one-time visitors who have no stake in whether a neighborhood restaurant actually holds up. The places real New Yorkers return to every week were buried under spots that had learned to optimize for foot traffic, not food.
That's what Locals is built to fix.
Google Maps has enormous amounts of data. The problem isn't data quality — it's that all reviewers are treated equally.
A tourist visiting New York once and a local who's eaten out 200 times in the city carry completely different signal value. If you know who is leaving the review, you can re-weight the ratings to surface what locals actually think.
1. Score every reviewer
Each reviewer gets a localness score from 0 to 1, built from three signals:
2. Re-weight the ratings
Using those scores, every restaurant gets two separate ratings: a local-weighted rating and a tourist-weighted rating. The gap between them is one of the strongest signals of whether a place is a neighborhood staple or riding tourist hype.
3. Run a classifier
I hand-labeled 205 restaurants as "local-approved" or not to create training data, then trained a Random Forest on 20 features — review statistics, NLP signals from review text (quality language, local vs. tourist patterns), price range, and rating distribution.
It's graded only on restaurants it didn't train on (5-fold cross-validation, repeated 10 times with different shuffles). It ranks a local-approved place above one that isn't about 70% of the time (AUC ~0.70), against 60% for sorting by Google stars.
The model outputs a confidence score for each restaurant. That score drives the final ranking.
4. Surface it cleanly
The app lets you filter by neighborhood, cuisine, price range, and whether a place is open now. Restaurant pages show the local take in plain language — no jargon, no bar charts, just: "locals rate it 4.4 vs. tourists' 3.8 — regulars love it more than visitors do."
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, TypeScript, Tailwind CSS |
| Backend | FastAPI, Python |
| Database | Supabase (PostgreSQL) |
| Auth | Supabase Auth (Google OAuth) |
| ML | scikit-learn (Random Forest), pandas, numpy |
| Data collection | Apify (Google Maps scraper) |
| Deployment | Vercel (frontend), Railway (backend) |
Dataset: 78,000+ reviews · 2,100+ restaurants scraped · 384 on the live list · 205 hand-labeled training examples · 20 classifier features
Backend
pip install -r requirements.txt
python3 -m uvicorn backend.main:app --port 8000
Frontend
cd frontend
npm install
npm run dev
App runs at http://localhost:3000.
Create frontend/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:8000
AUTH_SECRET=<your-secret>
AUTH_GOOGLE_ID=<your-google-client-id>
AUTH_GOOGLE_SECRET=<your-google-client-secret>
NEXT_PUBLIC_SUPABASE_URL=<your-supabase-url>
SUPABASE_SERVICE_ROLE_KEY=<your-supabase-service-role-key>
locals/
├── backend/
│ ├── main.py # FastAPI app — recommendations, filters, open-now logic
│ └── data.csv # Enriched restaurant dataset (ML scores + metadata)
├── frontend/
│ ├── app/
│ │ ├── recommendations/ # Main discovery page with filters
│ │ ├── restaurant/[id]/ # Restaurant detail page
│ │ ├── favorites/ # Saved restaurants (per-user, Supabase-backed)
│ │ └── api/ # Auth + saved/visited API routes
│ └── components/ # RestaurantCard and shared UI
├── data/
│ ├── reviews.csv # Raw reviews (48k+)
│ └── restaurants.csv # Raw restaurant metadata
├── pipeline.py # ML training pipeline (reviewer scoring → classifier)
├── enrich_data.py # Enrichment from raw Apify output
└── scrape_google_maps.py # Apify scraper config