From Ticket to Draft: How Munich Automates Citizen Inquiries with AI

The City of Munich is automating citizen inquiries for the driver's licensing authority to reduce the administrative burden of repetitive requests. The city processes over five million emails annually, with a significant portion of licensing tickets consisting of status updates and general questions that can be answered via a knowledge base. To address this, the city implemented an event-driven AI pipeline integrated with ZAMAT, an open-source ticketing system.

The technical approach began with a pre-study analyzing 15,000 historical tickets using an LLM pipeline with Pydantic for structured output and confidence scores to categorize inquiries. The production architecture utilizes a Kafka broker to trigger a triage service, which determines if a ticket requires no action, a static response, or a dynamic answer generated by a Retrieval-Augmented Generation (RAG) service. This service draws data from two sources: a public knowledge base on munich.de and an internal Qdrant vector database. The system uses Jinja2 templates for dynamic prompt engineering and LangFuse for tracing and observability.

To ensure quality, the system employs a gold standard dataset to measure retrieval performance using Recall at K and Mean Reciprocal Rank (MRR), while answer quality is evaluated using an LLM-as-a-judge approach. The deployment follows a human-in-the-loop model where AI-generated responses are initially saved as drafts for human approval. The city intends to enable auto-sending only for categories that demonstrate high human acceptance rates. The entire framework is designed for reusability across other municipal domains via YAML configuration files and is hosted on OpenShift.

This description was generated by Open-Source AI using the transcript of the session and the original submission contents.

This session took place in track Natural Language Processing & Audio (incl. Generative AI NLP) and was classified suitable for intermediate domain / novice python by the speaker.

Submission

The proposal as submitted by the speaker before the conference.

3. Session Outline (30 Minutes)

I. Context & The Pre-Study | 5 min

  • The Shift: Transitioning from legacy email communication to Zammad within Munich's city administration.
  • Proving the Case: Utilizing LLMs to analyze historical ticket data to calculate automation potential and project significant time savings before development began.

II. Architecture: Integration & Pipeline | 6 min

  • Event-Driven Design: Connecting to Zammad via the city-internal Kafka message bus.
  • Real-time Processing: How new tickets are captured and routed to the AI component seamlessly.

III. The Two-Stage Process | 12 min

  • Step 1: Classification & Extraction: Analyzing thematic context through rule-based logic and LLM-powered information extraction.
  • Step 2: Response Generation: A RAG (Retrieval-Augmented Generation) approach leveraging a knowledge base maintained by subject matter experts.
  • Human-in-the-Loop: Integrating response drafts into the agent UI for review vs. automated "dark processing" for high-confidence categories.

IV. Scaling & Lessons Learned | 4 min

  • Multi-Tenant Capability: Designing for configurability and deployment across various city departments.
  • Key Benefits: Efficiency gains, response consistency, and establishing a "Single Voice of the City."

V. Q&A | 3 min

  • Open discussion on technical tooling, model selection, and legal/privacy frameworks.
Transcript (auto)

Auto-generated from the recording utilizing Open-Source AI. Speaker labels (Speaker 1, Speaker 2) reflect diarization, not identity. Timestamps refer to the recording.

Speaker 1 [00:02]

Please hurry up Okay, good afternoon all hope you're enjoying the conference so far Yeah, welcome to the session. This session will be 30 minutes long with five minutes Our Q&A at the end and questions can be asked through the platform talks.pycon.de Yes, and the session is from ticket to draft how how Munich automates citizen inquiries with AI. And please, a big round of applause and support to Leon Lucas.

Speaker 2 [00:47]

Hi, good afternoon. Thank you for your interest in how we at the City of Munich automate citizen inquiries using ticket systems and AI. First, I'm going to introduce myself and the organization. So we at the City of Munich are roughly 30,000 employees over 15 different so-called referate. You could see them as ministries of the City of Munich. One focuses on mobility, one focuses on the environment. And we at the IT Referat, we focus on IT. And the IT Referat, again, is split into two parts. The IT Referat itself, which does more of the administration part of SIDMI administration. And ITM, the company me and my colleagues work at, where we are the service provider for all the 15 Referate. And we do all the technical stuff. We have 1,300 employees there in the IT Referat, and we work providing all these workplaces for the people who work for the city, almost 30,000. We have lots of smartphones, laptops we look after, tickets we get, and over 5 million mails every year. And for AI at the City of Munich, that's seven people and one robot you can see on the right. In my role I'm the architect for AI and a special shout out to Fabian and Paul who are here in the second row which did all the coding behind this project. Another thing I have to introduce is our ticketing system ZAMAT as without this we wouldn't be able to automate anything. Xamarin is an open source ticketing system, it can take many forms of input, forms, email, even WhatsApp, but as far as I know, we only use forms at the city of Munich as an input channel and it has many advantages like you have ownership of tickets, you have group status, priority, etc. and it has a built-in knowledge base which human agents can use to reuse templates to have consistent answers, but also our AI can use to give answers. So this is important later. At the moment, we are shifting our citizen contact at the City of Munich from forms going to email mailboxes to forms going to the ticketing system. Because you all know Outlook. Outlook is a tool to work on lots of questions, but it's probably not the best tool as you can help yourself with categories and stuff and flags but in the end it gets intransparent if you have to handle a lot of requests from users. So we're shifting it all and onboarding new use cases one by one to our ticketing system and with that we are ready to scale and automate our citizen contact and automate that's the part where we build an AI system. So, now I'm going to introduce our use case as well as the pre-study we did. The use case is the driver's licensing authority, where you go if you need a new driver's license, if you lost your driver's license because you drove too fast, stuff like that. And before starting our development, we did this pre-study to find out, is it worth it to do it? Is it feasible? And stuff like that. Because to do this pre-study we analyzed over 15,000 tickets from the past year. And analyzing all these tickets by hand is kind of tiring. So this is the first step where I came in. We did an LLM driven ticket analysis where we created an llm pipeline where we prompt engineered our categorization with our taxonomy to yeah to help to let the llm help us categorize all the tickets this was an iterative process so we started with some categories the the domain subject matter experts gave us and then but they weren't right it turned out and they were missing some categories and so we work together with them and the LLM to find out what different types of inquiries we get and how we answer them because it's also important. For that we used, to get some more Python into this, we used structured output calls which I can really recommend if you build systems on top of LLMs because then you get objects out of the LLM call which you can work on in a normal programming matter. this is a part of our code and in total it was much bigger as we analyzed a lot of stuff but this shows how we used PyDantic and this is also similar to a talk I was in this morning about agents doing analysis of insurance claims that they also introduced a confidence float where the model gives out its own confidence This is kind of still a black box confidence thing, but it helps a bit. It might be interesting if some of you did the same to talk to me afterwards about that. And else we gave it the system prompt and a reasoning string so it explains what it did. And all this turns into the prompt. So if you create this Python class, it all turns into the prompt and you just have to do it in one single place. So, to some of the results, we discovered these different categories, with the biggest category being people asking about the status of their submission, which is like a problem in itself. This keeps the people from working on the submissions because they have to answer that so much. The second biggest category was question. This is a category we use for all the questions that could be answered using a knowledge base. The third biggest one was subsequent submissions of people adding in documents for a submission they already made. They said, oh, I missed giving you a photo of my ID, and so here you have it. And that was the third biggest category. And next to this analysis, we then looked at, okay is this possible to answer by ai how long does it take a human to answer this stuff and then we could calculate the total amount saved possibly saved by the system and then we yeah we notice okay it makes sense to do the system it is a it has a positive return of invest and so we started designing the architecture for the system which is generally and event driven system with a LLM pipeline. This is my first architecture slide. This is very basic. So the citizen fills out a form on munich.de, which creates a ticket in ZAMAT and the ZAMAT then asks the AI how to answer. The AI looks in the knowledge base, returns the data foundation and like this the response is drafted put back into the ticketing system and sent to the user this is a more detailed version on the left you can see everything that belongs to zamat so all the ticketing stuff including its kafka and the enterprise application integration API, which is used to communicate between the two systems. Then in the middle, you can see ZAMAT AI and all the stuff inside it. And on the right, we already had an existing rack system on unix.de for citizen information, including citizen information concerning driver's licenses. So instead of building it new, we just used this as a knowledge source. So now I'm going to go through the Samad AI part starting here with a new ticket event and how this it all goes down. So we have a Kafka broker listening to the Kafka and as soon as a ticket with our topic is placed there, the triage service is triggered, which gets the info from the API and then decides what to do with the ticket. And after deciding what to do, it triggers our action service, which then does the handling of the ticket. The action could be no action if the AI can't answer that request and a human agent has to answer it. It could be a static answer, which is just written in a config file for very basic questions. And it could be one of the questions related to the answer service. which can be answered either using the public knowledge base from munich.de or one of the special knowledge bases we built using the Samad knowledge base. Both the rack on munich.de and the Samad quadrant database get fed using like an indexer job, cron job, which runs nightly to get new knowledge and update the database. We have two databases because we have public information and then we have information we hand out if people ask us about it and that's the stuff in the quadrant database for ZAMAT, which gets fed by the knowledge base, which the people from the driving license authority can edit themselves in ZAMAT, so they can also change the way they deal with stuff, so maybe they have an internal order to do this faster than this and then they change how they can change little information and the system automatically gets the new information and response accordingly so here's just the ticket life cycle in a very simplified way it comes in from zamat the i first decide what to do then writes a draft and then in the in the final step either the the human agent approves it or we set the whole system to auto send so this this is not deployed yet it's in the three quarters finished in development and in the first month we want to check how many people send out approve the ai's request and then we have a foundation on which we can decide whether to auto send the tickets auto send all the tickets or to send some of the tickets or have a human in the loop still. So now some implementation details which might be interesting. I'm going to start with a scenario so you can all feel like what the system does. This is a real use case from an old ticket. Like a user asked us, he lost his wallet including his driver's license and now he wants to get a new one and maybe he thinks writing the contact formula is a valid way to get a new one but we have a process for that as you can see in the next slide then the ai agent the triage service notice okay this is an inquiry which needs the ai answer service and then calls that service and the service looks in the database finds the the right article on munich.de uses this as a foundation for the answer and then craft this answer and telling the citizen the way how he can get a new driver's license either by the digital way or the analog way sending a letter. Staying with this ticket from a whole different view, I'm going to introduce our tracing. Tracing is a way to look at all the LLM calls so your system isn't as black box anymore. Use length use for this and you can see the trace and the model overall it took nine seconds to generate this answer with multiple steps and you can also see some of the reasoning of the model. During development this is very important to have debugging stuff and see what the system is actually doing and during production this will be important to have an eye on the system and notice stuff like model shift and different things if you life cycle models or other parts of the system. Another cool trick we use are Jinja2 templates which are similar to the Django syntax where you can dynamically create these markdown prompts based on dictionaries so we don't have to put all the logic inside the prompt which makes it more easy for us to scale later. Because our plan for scaling which will come on later is not to program this for the second domain but just create a new config file, create some new prompts and this should in theory be enough to onboard a new use case and Jinja2 helps with that. Here you can see what it actually does. This is a pull request which isn't even merged yet from our project and you can see that all the hard-coded stuff turns into dynamic programming, so you don't have to rewrite the, you have five tools, then you add a tool, then you have to write, you have six tools. This all automatically works and makes it much more easy to maintain. Another important topic with AI systems is testing. It is a quite complicated topic. Testing the retrieval of rag systems is quite easy in my opinion. Retrieval is the part where you use embedding models to find out which documents in your database match the query best. You can just create a gold standard data set, which I will have an example of on the next slide and automatically compute your scores like Recall at K or MRR, which are scores how correct the documents are for queries inside the Skoll standard dataset. This is very useful, for example, if you have three embedding models you can use, you just run the evaluation script with the three different models, then you have three different scores and you can decide which one is the best. Evaluating answer quality is harder. It can be done using LLM as a judge. So you have an answer which you think is right, you write it in your data set and then you let an LLM check whether the response given by the LLM matches the one in the data set. And this is okay, but you still use an LLM to check an LLM, which might be most useful if you have a bigger LLM checking a smaller LLM, for example, but you still have to treat it with caution. Here's an example of the data set. So we have an idea, of course, the text where people of the inquiry, the proposed category we want it to be and the canonical answer, which you can all then automatically check if the category was right. So a bit of wrap-up overall about the project, Zamat has built-in AI features, we of course don't want to implement ourselves, we are just going to use the features by the open source project, which are ticket summary and then writing assistant, which is basically what you can do if you copy the ticket to your chat GPT, or in our case, our MOOC GPT chat front end. But I think for these use cases, it's really important to have them in your system that people are working in because how much efficiency gain will an AI ticket summarization bring? Maybe like 10 to 20 seconds and if people have to copy paste it into a different tool and back I think all that gain is lost so having it inside the system is very important and standard stuff like fixing grammar or spelling is something you just have to use at at this point in time. To give the AI, to enable the AI features in Xamarin, we just have to plug it into our AI proxy, which is our central layer for AI access inside the city of Munich. So all the components get their open API from there, where we can manage all the keys and usage. If you like a new domain at the city of Munich and want to start using Xamad AI because you get so many inquiries and you need AI to help you answer that, the first step will be to start using Xamad at all because that's the technical foundation of our stuff and a standardized way to interact. We don't want to interact with 20 different systems and using Xamad probably has more advantages than using AI. After that, we can do a pre-study with the domain, finding out whether it's possible to answer the questions and whether it's worth it to deploy a new system, which of course has running costs, et cetera. So there needs to be a big enough gain in the team. And after that, we do a ZAMAT AI project, which is hopefully just creating new configurations. Because, yeah, to not do this project over and over again, we started with the goal to have it reusable. And in theory, all you need to do is change the prompts, create a new YAML config where you configure the Kafka topic, for example, and then we spin up a new OpenShift namespace and deploy our stuff there. And this should be it here. a short extract of our config where you can, for example, configure whether to automatically publish the tickets after they are put in Xamarin or whether they should be posted as a shared draft which then are reviewed by human agents. That was it. Here is an overview of all our use cases. You can find here for example the AI search on munich.de and here if you want to connect with me. And now I'm open for questions.

Speaker 1 [21:13]

Thank you, Leon, for sharing your experience. We have lots of questions, and we're good on time. All right, I will read first the most voted. Does the input form disclose that AI is used? Yeah. Do the people get upset that AI is used to answer their inquiries? Has anyone tried to spam the system?

Speaker 2 [21:42]

So this system isn't live, it's under development right now. So we don't know if people get mad, but we will have a transparency layer there. This is also required by the EU AI Act to have transparency if you're talking to people. There will also be, like, as soon, our plan is to, if we automatically enter stuff with AI and the people respond, it won't be entered a second time by AI because then they're probably human. And your third question is if you get lots of demand, right? Overall, we get lots of requests, like for example, 16,000 in the driver slicing authority, and overall, I got the feedback from many people that since AI, they feel like a lot of the requests they get are written by AI, which makes it harder because the requests are longer, are written more often. People writing requests with AI makes us use AI to to answer them.

Speaker 1 [22:38]

Right, thank you. How do you defend against prompt injections?

Speaker 2 [22:46]

Yeah, so on the one hand, the system is pretty limited from the potential damage. So if you put a prompt injection in your request, you will just get a bad answer. Then we use lots of prompt templates and instructions to limit that. And the third one is, what is it called, like models like Purple Llama, which detect like small AI models to detect prompt injections. And then, yeah, just kill the request if it's in it.

Speaker 1 [23:27]

Next question. Do you actively collaborate or share knowledge and or outcomes with other cities or plan to open source this? Yeah, there is embraces, public money, public code, but I don't know.

Speaker 2 [23:42]

So we at the City of Munich, we follow the motto, public money, public code, because we are paid by citizens, so we just make everything open source as much as we can. This project is already open source. There are some links in the slides, and if you just Google GitHub ITM Zamat AI, you will probably find it and then you're keen on people helping us or just using it and be in contact with a lot of other cities but most other cities which are not as big as Munich don't have like the infrastructure to run this they can just maximum run yeah whole application they just have to start but having a kubernetes cluster is not that common for public administration

Speaker 1 [24:36]

Thank you. Next question. How did you anonymize the personal information in your training pre-study data?

Speaker 2 [24:46]

We did it using local models on a work machine, on a workstation in our office.

Speaker 1 [24:57]

Does it answer your question?

Speaker 2 [25:04]

We anonymize it using local models and then we run it in the cloud because the local models are limited.

Speaker 1 [25:14]

Yeah, please ask them here. Why you classify your tickets with LLM? Would it not be cheaper and more consistent fine-tuning a language model, for example, BERT?

Speaker 2 [25:31]

I used to fine-tune models and I don't think it would be more time-efficient because writing a prompt takes One minute and optimizing maybe five days and fine-tuning an LLM is or like even a bird is more complicated in my experience Especially for one-time use case like a ticket analysis

Speaker 1 [25:54]

Yeah, we still have lots of questions, and we have four more minutes. You appear to be using OpenAI products. How is that viable in the context of the U.S. Cloud Act and other data protection challenges?

Speaker 2 [26:17]

Azure AI with GPT models for public use cases. But this system will be run on our private models from a German hoster. So we use public models for public use cases because the data is already online and it's cheaper to use them and they are more powerful. But for confidential use cases, we use different models, which is still also in progress.

Speaker 1 [26:52]

How much are you mourning the loss of Linux?

Speaker 2 [26:56]

Lemux is the Linux operating system which we use in the city administration. I am sad, I think most of the IT people are sad in some way, but the people at the administration didn't really accept Linux as their operating system and so we are back to using Windows on the client but mostly open source on web applications.

Speaker 1 [27:26]

LLM answer quality is there any specific trigger for manual human checks or is it done arbitrarily for some samples?

Speaker 2 [27:36]

In the beginning everything will be human checked and then we will have statistics on what categories will have Like high acceptance rains and what don't and then we can like I showed in the config You decide category by category if we want to auto publish them, but the default will be not to auto publish them But we hope our system is good enough. It Will get to some auto publishing

Speaker 1 [28:09]

Can citizens easily get in touch with or escalate to a human?

Speaker 2 [28:15]

Yeah, as soon as you answer one time, your question won't be answered the next time by an AI.

Speaker 1 [28:25]

How does your system handle multi-question mails?

Speaker 2 [28:32]

I have to pass that question on. So far maybe. But it's a new test case we can add to our...

Speaker 1 [28:45]

Yeah, it's an idea for you. Which framework did you use for the evaluation?

Speaker 2 [28:55]

For this pre-study, we just used some long-chain OpenAI adapters and stuff and just coded ourselves the rest because there's lots of domain knowledge in it and I don't know how we do the rack evaluation if we have a framework for that. Do you know what Sebastian uses to do this? But we put it all into LangFuse to have the observability and easily shareable. So we have a custom script, I think, which then locks the data into LangFuse as well.

Speaker 1 [29:36]

Right, thank you. If humans supervise an AI system that is good enough, they will at some point stop paying attention. How will you make sure the data you collect on approval rates are meaningful given that?

Speaker 2 [29:56]

Yeah, that of course is a risk and we both

Leon Lukas

Leon Lukas has been the team lead of the AI Competence Center for two years and has played a key role in the development and implementation of AI solutions within the city administration. While he initially trained models and built systems himself, he is now responsible for the architecture and projects at it@m, the city’s IT service provider. For more information on AI in the City of Munich, visit: ki.muenchen.de.

Social card for talk: From Ticket to Draft: How Munich Automates Citizen Inquiries with AI