← Denys KozedubCase 04 · Events by plain language
Case 04 · Volok · a city events app with an AI assistant inside

Say what you feel like. The city answers.

Volok is a map of small live events near you: padel at six, beer at a corner pub, a board-game table with two free seats, a walk along the river on Sunday. Inside the phone is an assistant for events. Type “rooftop jam on Saturday, free entry” and the event card writes itself. Ask for “something quiet in the evening nearby” and it finds matches and says why each one fits. No forms, no filters, no groups to join.

This page shows the assistant: how it reads a sentence, how it searches by meaning, and the rules it lives under. Every number and name comes from the product’s own spec and screens; the demos replay that behaviour on sample events.

Watch it workHow it works, step by step
An event, from one sentenceAbout 30 seconds
A search, answered with reasonsUnder 1.5 seconds
Languages it readsCzech · English · Ukrainian
Where your location is storedNowhere. Used once, then dropped
Fig. 1 · Create an event by typing

One sentence in. A finished event card out.

The phone on the left is what a user sees. The column on the right is what the assistant does underneath, line by line. Pick a sentence or let it play.

Type
Or
×Create event1 / 3
Describe your plan in your own words. The assistant fills the form.
What’s the plan?
0 / 5000
Generate ✨
or go step by step →

What the assistant does underneathtyping

Waiting for a sentence…
Every line here is a real step in the design: scrub, detect, check, extract, default, confirm, moderate, publish.
What you just saw

Before any model sees the text, personal data is blanked out and the text is checked for tricks. A language model then fills a strict form: title, category, when, where, how many people, tags. Whatever it is not sure about it marks in amber as a guess, so the person always knows what was theirs and what was suggested. Nothing goes on the map until the person taps Publish and a moderation check passes.

The story

Why I built it, what it is, and where it stands.

01 · The problemNew in a city, free this evening, and nobody to go with.

Every city is full of people who want company for small things: a padel game, a beer, a board game, a walk. The apps that exist want you to join a group, plan a week ahead, fill a form, or already have friends. The spontaneous version, “I’m going there at six, who’s with me?”, had no home. The map was the easy part. The hard part was removing every form between the wish and the event.

02 · The solutionA map of small live events, with an assistant that understands plain language.

Anyone can put an event on the map in half a minute: say it, check the card, publish. Anyone nearby can find it by asking in their own words, in any of three languages. Join, and you are in the event chat. Events are small on purpose, twenty people at most, and the exact spot opens only to people the host let in. The assistant does the paperwork; a person keeps every decision. The name says the rest: Venue Of Local Outings, Kin.

03 · Where it standsDesigned to the last screen, built from the foundation up.

Done: the product spec, 136 screens across 11 flows, the architecture, a 340-step build plan with a brief per task, and the backend foundation: the API, the event bus, the request guards, the full infrastructure stack. Not yet: the mobile app in the stores and the assistant’s pipeline in production code; both are specified down to prompts, limits and test sets. Next: the app, then a pilot in one city.

What the assistant does

Six things, all in plain language. Each one with a person in the loop.

What the user sees on top; what runs underneath below the line.

Smart creation

“Padel today 6pm, need 3 people” becomes a card: title, category, time, place, four seats, tags. You check it and publish.

UnderneathPresidio blanks personal data, fast-langdetect names the language, Llama 3.1 70B on Groq (gpt-4o-mini as fallback) fills a fixed form with a confidence per field. Fields under 0.6 and defaults show as amber guesses.
Search by meaning

“Something quiet in the evening nearby” returns the right events with a one-line reason on each card. Too vague, and it asks one question back.

UnderneathThe same model turns the question into a small structured query. OpenSearch runs three searches at once: words (BM25), meaning (vectors), tags. PostGIS distance and a time window filter; the model re-orders the shortlist and writes one reason per card. BM25 alone answers if the model is unavailable.
For you

A feed that learns from what you joined: “you usually play on weekday evenings, there is padel at seven nearby”.

UnderneathNo model involved: an OpenSearch weighted query over your interest tags (×3) and past categories (×2), with fixed reasons such as “matches your tags” or “starting soon”. Cold start shows what is popular within 5 km.
Moderation before publishing

Every event is read before it goes live. Clear cases are decided in a second; borderline ones get an “in review” badge and a person decides within a day.

UnderneathA ladder: banned patterns → Presidio scrub → Perspective API toxicity score (under 0.5 pass, over 0.8 reject) → the language model only for the middle band → an admin queue with a 24-hour deadline. Photos pass an image safety check before they show.
Chat translation

In a city of expats the padel group speaks three languages. Each person writes in theirs and reads the others in theirs, with a “translated from” label and the original one tap away.

UnderneathLibreTranslate, self-hosted, with DeepL as fallback and a five-second timeout on each. Every translation is cached in Dragonfly for a day, so the same message is never translated twice.
A memory of the evening

A day after the event ends, a short summary of the chat and photos appears in your archive: “Three sets, Ivan’s team won, pizza after.”

UnderneathAbout two hundred words from the language model, generated a day after the event from the chat, moderated like any other text, editable by the host.
How it works

How a sentence becomes an event. Six steps, none of them magic.

The steps advance on their own every few seconds. Click one to stop and read.

Clean the text before any model reads it

The sentence is checked for phone numbers, emails, addresses and ID numbers, and those are blanked. Then it is checked for text that tries to give the model instructions. Only then does it move on. The model never sees who you are.

you typed → board games café Thursday evening, 2 seats, text me 777 123 456
scrub → board games café Thursday evening, 2 seats, text me [phone redacted]
language → en · 0.98
injection → clean
Presidioan open-source tool that finds personal data in text by patterns and by recognising names, places and numbers
fast-langdetectguesses the language; below 70% confidence the app simply does not say
injection checka short list of patterns such as “ignore your instructions”; a hit means the sentence is treated as plain words only
Fig. 3 · How search by meaning works

Three searches at once, one model to order them, and a plain fallback.

Hover any box. The dashed lane is what happens when the model is slow, down or over budget.

Your questiontyped, or spoken on the phone
Scrub & checkpersonal data · injection
UnderstandLLM → structured query
Guard rails on the modelbudget · rate · time limit · safety
By wordsfull-text · three languages
By meaningvectors · nearest neighbours
By tagscurated list · boosts
Filterdistance · time window
Re-orderLLM rerank + reasons
Resultseach with “why this one”
fallback · words only
The whole pipeline, in one breath

A question comes in, is cleaned, is understood as a small structured query, and three searches run over the live events at once. The union is filtered by distance and time, a model orders the best matches and writes one reason per card. When the model cannot help, the words-only search answers alone.

The rules

What the assistant is never allowed to do.

Designed in from the start, not bolted on. Each one is a line of code somewhere, not a promise.

Never see who you arePhone numbers, emails, addresses and IDs are blanked before any model reads the text. Traces keep hashes, never content.
Never store your locationYour coordinates ride along with one request to find events nearby and are dropped. Only the events’ coordinates are stored.
Never publish on its ownA person taps Publish, and moderation reads it after that. The assistant prepares; it does not decide.
Never invent silentlyEvery guess is amber on screen. The description is never guessed at all.
Never run up a billA hard budget of €5 a day for paid model calls, an alarm at 80%, and a breaker that switches smart features off until midnight. Worst case is a known number.
Never break searchIf the model is slow, down or over budget, words-only search answers in under half a second with a small banner. Search never returns an error.
Never take instructions from the textThe user’s words are passed to the model as data inside markers, with a short allowed-topics list. “Book me a flight” gets a polite no.
Never let one person flood itThirty smart requests an hour and five published events a day per account; five refusals in a day pause smart search for that account.
Fig. 4 · Under the hood

One app, one API, four stores, and a model behind a gate.

Hover a box for what it does in plain words.

The appFlutter · iPhone + Android
The APIPython · FastAPI · 8 modules
The model gateStrands · Groq Llama 3.1 · OpenAI fallback
Watching it runLangfuse · Grafana · Prometheus · Loki
The event busmodules talk through messages
The databasePostgres + PostGIS · the truth
The search indexOpenSearch · words · vectors · tags
The fast memoryDragonfly · cache · limits · streams
The filesMinIO → Cloudflare R2 · photos
What each box does

The phone talks to one API. The API keeps the truth in a map-aware database, mirrors events into a search index, uses a fast memory for caches and limits, and keeps photos in object storage. Every model call goes through one gate with the budget and safety hooks. Everything that happens is traced, without personal data.

8modules in one API: auth, user, event, chat, moderation, notification, discovery, admin. They never call each other directly; they send messages.
3languages in the search index, each with its own word rules: Czech, English, Ukrainian.
≈200curated tags that events and searches share, so “board-games” in a search meets “board-games” on an event.
1 VPSto start: everything above runs on one rented server. Only when it is busy does it move to a cloud that grows on its own.
Drawn before coded

Every screen was drawn first. All 136 of them.

Before the first line of the app, every flow was sketched by hand on paper-style phone frames: every state, every error, every banner. These are a few of the real sheets. The assistant’s moments are the ones with the sparkle.

136screens across 11 flows: onboarding, map, search, event, create, chat, profile, archive, subscriptions, safety, host tools.
340build tasks written out one by one, each with its own brief, so an AI developer team can work from them.
20 weeksof plan from an empty folder to the app stores, in five months of one week each.
1 personplus a team of AI agents that follow the briefs, the rules and the hooks. That is how I build.
The tech, in plain words

Every name on this page, explained in two sentences.

Language model

A program trained on a very large amount of text that can read a sentence and answer in kind. Here it is used for narrow, checkable jobs: fill this form, order this list, decide this category. It is never asked to be creative.

Want this in your product?

A plain-language layer can sit on top of almost any app with a form in it.

Bookings, tickets, listings, support requests, orders: wherever people fill in fields, they could say it instead. This is how I would add it to yours.

Week 1The form and the questions

We pick one form and one search box. I write the strict form the model will fill, the allowed topics, and the guesses it may make. You approve the wording.

Weeks 2–3The pipeline, with guard rails

Scrubbing, the model gate with a daily budget, the fallback, the amber guesses, moderation if you publish anything. Tested on a set of real sentences before anyone sees it.

Week 4In your app, behind a switch

Wired into your screens, on for a small group first. Every request traced, every refusal counted, every guess measured against what people changed.

AfterSearch by meaning, then the rest

Once the form works, the same pieces give you search that understands questions, a feed with reasons, and translation. Each is a few weeks, not a rewrite.

What stays with you
  • The strict forms and the question texts: yours, readable, editable
  • The pipeline code and the guard rails, in your repository
  • The test set of real sentences and the expected answers
  • The budget and rate rules, as settings you can change
  • The traces: what was asked, what was answered, what it cost
  • A written explanation of every piece, like this page
Contact

Have a form your users would rather say out loud?

Tell me which one and what people type into it today. I reply by email with how I would build it and roughly how long it takes.

hello@denyskozedub.com