
Most of the email that comes into Events in Vacaville isn't particularly important. There's spam, newsletters, and plenty of messages that don't require a response.
But mixed into all of that noise are emails that matter. An event organizer might have a correction to an event I've listed. Someone might have a question about the website. A local business might send me information about an event they'd like added.
And I missed some of those.
By the time I noticed them, my response was late. I felt terrible about it.
Events in Vacaville may have software, automations, and AI running behind it, but there's an actual person running the site: me. That's important to me. If someone takes the time to contact EIV, I want them to know there's a person on the other side who is responsive, trustworthy, and does what he says he's going to do.
So the problem I wanted to solve wasn't really:
How can AI manage my email?
It was:
How do I make it harder for an important email to disappear into the noise?
That distinction shaped almost everything I built afterward.
What would this look like if it were easy?
Before writing any code, I thought about what I actually wanted the experience to be.
My existing process was completely manual. I'd open the inbox, read a message, figure out what it was, then delete it, move it to spam, reply to it, or decide that I needed to do something else.
I didn't necessarily need a system that could do all of that for me. What I wanted was to stop having to discover which emails deserved my attention.
Ideally, an email would arrive and a system would:
Read it → classify it → apply a label → determine whether it needed a response → prepare a draft → notify me
Then I could open Slack and already know:
There's something in the EIV inbox that needs you.
That sounded much easier. So that's what I built.
Version one was one agent
The first version used a single agent responsible for the entire workflow. It would read an incoming message and decide what kind of email it was: spam, a newsletter, a question about the website, a bug or issue, an organizer requesting a correction, or someone submitting a new event.
Once it classified the email, it could label it, determine what should happen next, draft an appropriate response when necessary, and notify me.
And it did okay.
I don't have an evaluation dataset or a percentage I can give you for this system. That's intentional to mention, because I don't want to pretend every AI project I've built has a benchmark attached to it.
This one doesn't.
What I did have was a growing concern about the design.
The prompt started looking like code I wouldn't want to maintain
As I reviewed the instructions for the agent, something started bothering me. There was a lot of branching.
Conceptually, the prompt was starting to look something like this:
Read the email.
If it's spam:
do this...
If it's a website question:
follow these instructions...
use this information...
draft this kind of response...
If it's a bug:
follow these other instructions...
draft this response...
notify me with higher priority...
If it's an event submission:
extract these fields...
follow this schema...
draft this response...
If it's an event correction:
do something else...
Then apply the appropriate label.
Then determine whether I need to be notified.
Then...
I've been developing software for a long time. If I saw that much conditional branching accumulating inside one function, I'd start asking whether that function had too many responsibilities.
Why should an LLM prompt be any different?
There was another problem too. Suppose the website-question responses weren't quite right. I could adjust that portion of the prompt, turn the knob a little, add instructions, or clarify something.
But now I had to wonder:
What else did I accidentally change?
Those new instructions were going into the same context responsible for classification, bugs, event submissions, organizer corrections, spam, newsletters, and everything else. Improving one behavior could potentially affect another.
The system was becoming coupled, and that made me uncomfortable.
Just because it fits in the context window doesn't mean it belongs there
Around this time, I was taking some classes that discussed LLM context-window management. One of the ideas that stuck with me was that filling a model's context with more and more information doesn't necessarily make it better.
Context has a cost.
The more unrelated instructions, rules, examples, and responsibilities I gave one agent, the more I was asking the model to figure out which parts mattered for the task in front of it.
That reinforced something I was already feeling while looking at my giant prompt.
Maybe this shouldn't be one agent.
So I started researching what Flue Framework supported and discovered I could use sub-agents. The architecture became obvious almost immediately:
Separate the responsibilities.
One agent didn't need to know everything
Instead of one agent understanding every possible email workflow, I could give the first agent a much smaller job:
What kind of email is this?
That's it. Classify it and route it.
Then hand the message to a specialist that knows how to handle that particular kind of email.
Conceptually, the system became:
┌─────────────────┐
Incoming email ────▶│ Categorizer │
└────────┬────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
Questions Bug / Issue Event
Agent Agent Submission
Agent
Each specialist could have a smaller set of instructions, a narrower responsibility, and only the context it needed to do its job.
It felt much more like how I'd design conventional software.
And building the first specialist immediately exposed another problem I hadn't expected.
The first sub-agent made me improve the website
I started with the Questions Agent. Its job sounded simple: someone asks a question about Events in Vacaville, the agent reads the question, prepares a useful answer, and I review the draft.
But then I had to ask:
Where does the correct answer come from?
If I wanted the agent to reliably answer questions about EIV, it needed a source of truth.
And I realized I didn't have one.
The website didn't have a proper FAQ.
So before I could make the AI better, I needed to make the product better.
I created an FAQ that covered the kinds of questions I expected from both sides of EIV. For people using the website, that meant questions like how to find events happening this weekend, how search works, and what kinds of events are listed.
For organizers, it meant things like whether submitting an event is free, how to add an event, and what information they need to provide.
Now the website had useful information that visitors could read directly, and the Question Agent had something trustworthy to work from.
Sometimes improving an AI system has nothing to do with improving the AI.
Sometimes you need to improve the information underneath it.
I didn't add RAG because I didn't need RAG
Once I had an FAQ, I had another architecture decision to make: how should the agent access it?
I could build a retrieval system. I could break the FAQ into chunks, create embeddings, store them, search for the relevant passages for each incoming question, and feed only those passages to the model.
There are absolutely situations where that architecture makes sense.
This wasn't one of them.
My FAQ is small. It fits in the prompt.
So I put it in the prompt.
That's V1, and so far, it works.
If the FAQ eventually becomes large enough that context size becomes a problem, or if I start seeing answer-quality issues because the model can't reliably find the relevant information, then I can revisit retrieval. Maybe that's RAG. Maybe it's something else.
I'll solve that problem when I actually have that problem.
Until then, adding a retrieval system would give me another piece of infrastructure to build, test, debug, and maintain without demonstrating that it makes EIV any better.
Complexity has to earn its way into the system.
The Bug Agent has a different definition of success
The next specialist was the Bug/Issue Agent.
This agent isn't trying to fix bugs. That's an important distinction.
If someone emails EIV because something on the website isn't working, the immediate problem isn't:
Can an AI agent autonomously debug my application?
The immediate problem is:
Someone is having a bad experience. Make sure I know about it.
So the Bug Agent has a very narrow job. It reads the message and drafts a polite response thanking the person for taking the time to report the problem. Then it notifies me in Slack with an important indicator so the message stands out.
I investigate. I triage. I decide what needs to happen next.
Could I build a system that automatically searches logs, reproduces issues, creates tickets, proposes code changes, and maybe even attempts a fix? Sure.
But that wasn't the problem I needed to solve.
The problem was visibility.
So I solved visibility.
Event submissions need a different specialist
Another type of email I receive is from organizers sending event information directly. Those messages have a completely different job associated with them.
I don't just need to respond. I need to translate an unstructured email into the structured event schema used by Events in Vacaville.
This is similar to the Flyer Intake Agent I wrote about previously.
The Event Submission Agent tries to capture as much information as possible from the organizer's email: event name, organizer, date, time, location, price, description, and the other fields EIV expects.
It creates structured JSON for me to review. Then it drafts a thank-you response to the organizer and sends me a Slack notification telling me that the data and response are ready.
From there, I use my normal intake workflow to review what was captured. If everything is there, great. If something important is missing, I can edit the drafted email and ask the organizer for the additional information I need.
Again, the system prepares.
I decide.
A missing field is still better than an invented one
This carries over directly from the lesson I learned building the Flyer Intake Agent.
If an organizer doesn't tell me when an event ends, I don't want the agent deciding that it probably ends at 9 PM. I'd rather have a missing field.
Missing information is visible. Invented information can look authoritative.
My existing intake process can catch missing data and give me an opportunity to resolve it.
So the goal of the Event Submission Agent isn't:
Produce a perfectly complete JSON object at all costs.
It's:
Capture as much trustworthy information as possible and reduce the amount of manual preparation I have to do.
That distinction matters.
The agents can read. They can't send.
One of the more important design decisions in this system has nothing to do with prompts or models.
It's permissions.
The email system can read the inbox. It can read from the flyer directory. It can write email drafts.
It cannot send email.
That boundary is deliberate.
The Question Agent can prepare an answer. The Bug Agent can prepare an acknowledgment. The Event Submission Agent can prepare a thank-you or a request for additional information.
But before any of those messages goes to another person, I see it. I can change it, add context, or decide not to send it.
That's exactly where I want the boundary today.
I could automate the spam too. I don't.
Remember what originally caused this project? Important emails were disappearing among spam, newsletters, and everything else.
Now the important messages get surfaced.
So what value would I gain by giving an agent permission to automatically delete or move everything else?
Not much.
I'd be expanding the system's permissions to solve a problem that isn't particularly important anymore. Spam can sit there. Newsletters can sit there. I can clean them up when I want.
The important part is that an organizer asking for help no longer looks the same to me as everything surrounding it.
The system is doing something more useful than simply reducing the number of messages in an inbox.
It's prioritizing my attention.
This system is intentionally boring
There's something else I should be transparent about.
I don't know how many hours this system saves me. EIV doesn't receive enough legitimate email today for me to make that claim.
And that's actually one of the reasons I deliberately kept V1 simple.
If Events in Vacaville were receiving hundreds of actionable emails every day, I'd probably be designing a very different system.
It isn't.
So I'm not going to build infrastructure for an imaginary version of EIV.
There's no complicated retrieval system, autonomous sending, AI support ticket platform, agent cleaning my entire mailbox, or automated bug-fixing pipeline.
There are a handful of small agents with narrowly defined jobs. They read, classify, prepare, and notify.
Then I take over.
Because if I spend more time building and maintaining the automation than I would spend handling the email myself, the automation has failed.
Even if the architecture looks impressive.
The goal wasn't to automate the inbox
Looking back, I think that's the most interesting thing about this project.
I started because I missed some emails. Not because there were too many of them, and not because responding was consuming hours of my day.
A real person had reached out to Events in Vacaville and I didn't respond as quickly as I should have.
That bothered me.
The technology I built afterward wasn't intended to put more distance between me and the people using EIV. It was intended to do the opposite.
The Categorizer makes sure I notice what matters. The Question Agent helps me prepare a useful answer. The Bug Agent makes sure problems get my attention. The Event Submission Agent removes the tedious work of translating someone's email into structured data.
Slack tells me when I'm needed.
And then I step in.
The agents don't replace my attention. They make sure my attention goes to the right place.
What I learned
This project doesn't have the clean quantitative story of some of my other experiments. There's no 79% → 98% benchmark. There's no calculation showing that I saved five hours.
But there are still a few engineering lessons I think are worth keeping.
Start with the failure you're actually trying to prevent
My problem wasn't email volume. It was missing an important message.
Those lead to very different systems.
Design the ideal workflow before choosing the technology
I knew what I wanted before I built the agent:
Read → classify → route → prepare → surface → human decides
The technology was chosen to support that workflow.
LLM prompts can have coupling problems too
A giant prompt full of conditional branches made me uncomfortable for the same reason a giant function full of conditional branches would.
Changing one responsibility shouldn't make me nervous about accidentally affecting five others.
Give each agent less to think about
The Categorizer doesn't need the website FAQ. The Question Agent doesn't need instructions for extracting event JSON. The Bug Agent doesn't need to know how to answer every organizer question.
Smaller responsibilities make the system easier for me to reason about and adjust.
Sometimes the AI exposes a product problem
Building the Question Agent forced me to realize that EIV needed a proper FAQ.
That FAQ made the agent better. It also made the website better for humans.
That's a much better outcome than hiding important product knowledge inside an AI prompt.
Don't add infrastructure until you need it
Could I use RAG for the FAQ?
Absolutely.
Do I need it?
No.
That's enough reason not to build it yet.
Limit permissions to the job
My agents can draft email.
They can't send it.
That isn't a limitation I'm waiting to fix.
It's a design decision.
Automation has a maintenance cost
An automation that takes more effort to build, operate, and maintain than the work it removes isn't helping me.
The system has to earn its complexity.
Make it easier to be human
There's a tendency to talk about AI automation in terms of how much human involvement we can eliminate.
That's not what I'm trying to do with Events in Vacaville.
I want people to know there's a human running it. If an organizer sends me a correction, I want to fix it. If someone finds a bug, I want to know. If someone has a question, I want to answer it. If a local business takes the time to send me an event, I want to follow through.
The computer doesn't need to replace any of that.
It just needs to make it harder for me to drop the ball.
I didn't build an AI-powered inbox so I could spend less time dealing with people.
I built it so I could do a better job showing up for them.