Ratusz AI went live this week at ratuszai.pl. It’s a chat and search tool for the documents a Polish town hall produces: resolutions, mayor’s orders, session minutes, roll-call votes, budgets. Ask it a question in plain Polish, get an answer with a citation back to the source PDF. It’s built for the people who work inside the office, not for residents browsing a portal.

It didn’t start as a product. It started as me indexing council resolutions for my hometown.

From “can I search these” to a subdomain per town

It started with one small Polish town whose council documents live on a BIP site (the standard Polish public-information bulletin), which means PDFs, inconsistent naming, and no search worth the name. I built a RAG and knowledge-graph pipeline to fix that: scrape the BIP, extract text (with OCR for the scanned pages), pull out which resolution repeals or amends which other one, embed everything, and answer questions with a system that cites its sources instead of guessing.

It worked well enough that turning it into something other towns could use felt like an obvious next step. Ratusz AI is that: the same pipeline, multi-tenant, one subdomain per municipality. I deployed a test instance against one municipality’s own multi-year document archive to prove it out on real documents instead of a toy dataset, which shaped the product more than any amount of guessing would have. It’s a self-run test, not yet an external pilot customer.

What “cited” actually looks like

The pitch for any RAG tool is “it answers with sources.” The part worth showing is what that looks like when the answer requires connecting several documents. I asked the test instance a question in the shape offices actually ask:

Kto głosował przeciw uchwale o funduszu sołeckim? (Who voted against the resolution on the village fund?)

It named the dissenting councillor, the resolution number, and the date, then tagged the answer with the roll-call record and session protocol it pulled the vote from, checked the other resolutions touching the same fund, and reported plainly when it didn’t have a detailed vote breakdown for one of them instead of making one up. That last part matters more than the correct answer: the system is built so that “I don’t have that” is an available output, not a failure mode to paper over.

How it’s built

Nothing exotic, deliberately. Postgres 17 with pgvector holds documents, chunks, and a knowledge-graph table of relationships (repeals, based-on-law, voted-for-against, and so on) in one database. Retrieval is hybrid, full-text and vector search combined by reciprocal rank fusion, so an exact resolution number matches as reliably as a fuzzy natural-language question. Gemini does the embeddings (gemini-embedding-001) and the chat generation (gemini-2.5-flash), with citations enforced in the prompt rather than hoped for. A nightly job at 03:00 pulls anything new from the BIP incrementally. All of it runs on a small home server with no GPU, behind a Cloudflare tunnel.

Pricing that skips the tender

Poland requires a public tender above roughly 130,000 PLN a year in procurement value. Ratusz AI’s pricing sits under that on purpose: a one-month pilot at 0 zł, then 490 zł/month for a gmina or 990 zł/month for a miasto, no per-resident pricing, since a town hall’s document volume doesn’t scale with its population the way people assume. That means a town’s own office can sign the contract directly instead of running a procurement process to buy a small SaaS subscription.

What’s actually proven and what isn’t

The domain is four days old. The first cold-email round went to eleven town councils in the region asking a direct question (how do you currently find an old resolution) with an offer to demo against their own documents. Zero replies so far. I wrote up the entire email-delivery saga that came out of just trying to send that campaign, which turned out to be a bigger project than the campaign itself.

The more interesting signal came from nobody I emailed. The original test tool, still running quietly with no marketing behind it, picked up several real visitors over one stretch who asked specific questions: the actual property tax rate, which resolutions covered aid for Ukraine, which votes weren’t unanimous and who dissented. Nobody sent them a link. That’s a better indicator that the thing is useful than any reply rate would have been, and it’s the reason I’m continuing past week one instead of calling it a failed experiment.

More on the product itself, and what’s next for it, is on the projects page.