Zalaegerszegen élek. Ha egy villanyoszlop kidől, egy játszótéri hinta eltörik, vagy egy utca hónapokig nem kap zebrát ott, ahol kellene, akkor az az információ általában három helyre kerül: egy Facebook-csoportba, ahol elsüllyed; egy telefonbeszélgetésbe a szomszéddal; és sehová máshová. A városvezetés nem látja, a képviselők nem látják, és a lakó sem tudja, hogy van-e rajta kívül, aki szintén ezen bosszankodik.
Ez engem régóta zavart. Nem elsősorban azért, mert politikailag baj, hanem azért, mert rossz az információ-folyam. A lakó tudja mi a probléma, a döntéshozó nem; és nem is tud róla, mert nincs csatorna, amin odaérne az infó. Úgyhogy megcsináltam. Így született a zeghangja.hu — a Zalaegerszeg Hangja, röviden Z!.
Mi ez pontosan?
Egy független, nonprofit közösségi platform. Zalaegerszegen bárki feltehet egy bejelentést: cím, rövid leírás, kategória, fotó, térképen kijelölt hely. Más lakók anonim módon szavazhatnak rá — igen, egyetértek, ez tényleg gond; vagy nem, szerintem ez nem probléma. A szavazat a körzet lakosságával arányosítva súlyozódik, tehát nem az történik, hogy egy nagy körzetből kényelmesen eltapossák egy kis körzet ügyét.
A platform 12 zalaegerszegi választókerületet ismer. Mindegyiknek saját toplistája van, saját képviselővel, saját áttekintéssel. A térkép Leaflet-es, a körzethatárokat GeoJSON-ból rajzolja, és minden aktív bejelentés egy marker. Amikor valaki bejelenti hogy „ez megoldódott" — pl. kijavították a padkát — egy 7 napos közösségi szavazás indul arról, hogy tényleg megoldódott-e. Nem én mondom meg, nem a városvezetés, hanem a körzet lakói.
Miért kellett ide AI?
Először nem terveztem vele. Aztán rájöttem: ha egy lakó elkezdi írni a bejelentését és nem tud választani a „köztér" vagy „zöld" kategória között, akkor vagy rossz kategóriát választ, vagy feladja a felét. Ha két ember ugyanazt a problémát jelenti be két szinten, az két külön szavazás lesz ahelyett, hogy egymást erősítsék.
Itt jön be a GPT-4o-mini. Négy dologra használom, mindegyiket pici, fókuszált prompttal:
- Kategória-javaslat. Ahogy gépelsz, a szöveg elemzése alapján villan fel a javasolt kategória. Nem kényszerít — csak segít.
- Sürgősség-értékelés. 4 fokozatú skála (alacsony → sürgős). Nyitott lyuk az úttesten az nem olyan, mint egy ledőlt padka.
- Duplikátum-felismerés. Az utolsó 50 nyitott bejelentéssel összeveti a tiédet, és ha van hasonló, megmutatja: „hé, ez ugyanaz lehet, szavazz inkább arra".
- Tartalom-szűrés. Ha valaki magánügyet ír („szomszéddal bajom van"), üzletieket vagy politikai pamfletet — az LLM megszűri. Nem automatikusan töröl, csak jelzi.
A prompt injection ellen system message + JSON-encoded user input a védelem. A felhasználó szövegét sosem interpolálom nyers stringként a promptba — JSON mezőben megy át, és a system message kimondja az LLM-nek, hogy a user-content NEM utasítás. Ez nem tökéletes, de egyszerű, olcsó és nagyjából megállítja a tipikus „ignore previous instructions"-támadásokat.
A 18+ ami nem tárol születési dátumot
Az egyik legizgalmasabb GDPR-döntés. A platform 18+, mert a tartalmi moderáció miatt kiskorúakat nem akarok a rendszerben. De a születési dátum egy érzékeny adat, amit felesleges tárolni: egyetlen dologra kell — ellenőrizni hogy van-e legalább 18 éve. Ehhez nem kell megőrizni.
A megoldás: regisztrációkor megadod a dátumot, a szerver kiszámolja a kort, és
a dátumot eldobja. A DB-ben csak egy is_adult: true flag
marad. Ez GDPR data minimization, textbook-példa.
Ugyanezt csinálom a lakcímmel. A pontos címet nem tárolom sehol: amikor beírod, a szerver SHA-256-tal hash-eli, plusz meghatározza a körzet-azonosítót. A DB-ben csak a hash és a körzet-ID van. Ha valaki feltörné a rendszert, a pontos címeket nem kapja meg — én magam sem tudom visszafejteni. Ennyi a lényege a one-way hash-nek.
Tech stack röviden
- Backend: Python 3 + Flask 3.1, PostgreSQL, Gunicorn + Nginx
- AI: OpenAI GPT-4o-mini — olcsó, gyors, pont elég okos ilyesmire
- Email: Brevo (jelszó-reset + admin riasztás)
- Frontend: vanilla HTML/CSS/JS, Leaflet térkép, semmi framework
- Push: Web Push API + VAPID + Service Worker (PWA-ként telepíthető)
- CDN: Cloudflare (TLS, cache, AI bot management)
- Auth: Flask-Login + Flask-WTF (CSRF) + bcrypt
A nagy tanulság: Flask 3.1 + PostgreSQL még mindig remek alap egy ilyen közepes méretű projekthez. Nem kellett microservice, nem kellett React. Jinja2 template-ek, szerver oldali rendering, egy kis progresszív JS az interakcióhoz — és pont ennyi. Az egész app.py ~2200 sor, egy ember el tud igazodni benne egy hétvége alatt.
Biztonság és moderáció
A közösségi platform legnagyobb veszélye nem a hacker — a troll. Ezért a rendszer három dolgot csinál:
- Reputáció-alapú shadowban. Aki sok „ez nem probléma" szavazatot kap a bejelentéseire, annak a tartalma elkezd csak magának látszódni. A troll szempontjából a rendszer úgy néz ki, hogy minden normálisan megy, csak éppen senki nem reagál.
- Rate limit. Bejelentkezés 5/5 perc, jelszó-reset 3/10 perc, regisztráció 3/óra. IP alapon.
- Trágárságszűrő. Jinja2
|censorfilter, magyar csúnyaszó-listával, automatikus csillagozással. Nem tökéletes, de a szemét 90%-át kiszedi.
Plusz a moderátori admin felület: bejelentéseket elrejteni, felhasználót banolni, hozzászólásokat moderálni, mindet egy UI-ban. És egy biztonsági napló, ami minden sikertelen bejelentkezést, admin műveletet és gyanús eseményt rögzít — email címeket SHA-256 hash-ként, mert a logban sem érdemes PII-t tárolni.
Amit közben megtanultam
Két dolgot.
Az AI nem az „okos" rész. A rendszer 85%-a sima CRUD, szavazás, térképi rendering, GDPR compliance és email-küldés. Az AI csak egy réteg rajta, ami olcsón elvégez négy dolgot, amit egyébként az emberre hárítanék. Ha ezt a négy dolgot veled csináltatnám meg egy bonyolultabb űrlapon, feladnád a harmadát.
A privacy nem compliance — termék. Amikor úgy építesz, hogy a pontos címet sose tárolod, a dátumot eldobod, a jelszót bcrypt-elt egyedi salttal őrzöd, a logot email-hash-sel vezeted — nemcsak a GDPR-al vagy rendben. Azt a fajta platformot építed fel, amit te magad is használni akarnál. Ez vonzza a felhasználókat, nem a pro-forma „Adatkezelési tájékoztató" link a láblécben.
Ki akarod próbálni?
Ott van élesben: zeghangja.hu. Zalaegerszegi lakóknak szól, de ha máshonnan vagy, regisztrálás nélkül is megnézheted a térképet, a toplistákat, és a körzeti áttekintőket.
A kód nyilvános, auditálható: github.com/gaberun24/zeghang. Ha a saját városod lenne a következő, fork-old, cseréld ki a GeoJSON-t és a városnevet, és kész. Ha ezzel kapcsolatban van kérdésed, tanácsod vagy (még jobb) kódoddal segítenél — itt eléredsz.
A város nem az, ami a papíron van. A város az, amit a lakói együtt látnak belőle.