Inspiration
Every meal planning tool I tried started by generating. It handed me a week of recipes before it knew a single thing about my kitchen, then left me to reconcile the list against a pantry it had never asked about. The work that actually costs a household time is not generating recipes. It is the reconciliation: what do we already have, who cannot eat what, what does this week cost, and what changes when Thursday falls apart.
So I built the opposite. Amalthea interviews first and plans second. It refuses to produce a week until it knows the household.
What it does
Amalthea is a collaborative planning agent for a household kitchen. It leads the conversation instead of waiting for a perfect prompt.
- It interviews. Before it generates anything it asks which nights are busy this week, which meals the family treats as fixed, like a taco night, and what everyone is in the mood for. The household record it keeps already holds the members, the allergies, and the weekly budget, so it never asks you to re-enter what it knows.
- It builds the week. Once it has enough, it proposes a seven day plan that respects the budget, the busy nights it was told about, and the allergen exclusions in the household record.
- It explains its picks. Meals come with data backed reasons drawn from the household record: this one uses the chicken thighs already in the pantry, this one uses the tomatoes before they expire, this one is the taco night you asked for. The plan total is reported against the weekly budget.
- It rebuilds the list live. The grocery list is always computed as plan minus pantry. Swap a meal and the list recomputes in the same turn. The family cooks what it already owns and buys only the gap.
- It remembers. Preferences, pins, pantry state, and exclusions persist per household in Firestore, so the second session starts where the first one ended instead of starting over.
The demo household is synthetic: the Reyes family, with one child who has a peanut allergy, a 120 dollar weekly grocery budget, and a Tuesday taco night the family names in the interview.
How I built it
The agent runs on Gemini 3.7 Flash through Vertex AI on the global endpoint, with Gemini 3.6 Flash as the fallback model. Deployed, it authenticates with Application Default Credentials, so no key material ships with the container. The agent runtime is ADK for TypeScript, the @google/adk package. It runs on Cloud Run. Household state lives in Firestore. The demo interface is a Next.js 16 chat UI. The repository is public under the MIT license.
Three design decisions carried most of the weight.
Eight tools, and they are the only write path. The agent has exactly eight tools. Every change to household state, the pantry, the plan, the pins, and the list goes through one of them. The model cannot mutate anything by talking. That makes the whole system auditable: if the plan changed, a tool call changed it, and the tool call is in the trace.
The allergen table sits outside the model. Exclusions are a deterministic table with a hard exclusion filter that runs inside the planning tools themselves, so an excluded recipe can never enter the plan or the grocery list no matter what the model is talked into. The model suggests, the table blocks. This is an assistive filter that enforces the household's own stated list. It is not a guarantee and it does not replace reading labels.
Memory is per household and scoped. The data port is constructed server side, already bound to one household, and no tool takes a household argument, so nothing the model says can select a different household. That is what makes cross household access fail instead of succeed.
For deployment, Cloud Run scales to zero, so the public demo costs close to nothing when nobody is using it, and the public URL is protected with per IP rate limiting.
Challenges I ran into
Getting the agent to hold the interview was harder than getting it to plan. A capable model wants to be helpful immediately, and helpful looks like producing a plan from three words. I ended up making the readiness check structural rather than conversational. The set of unanswered interview questions is computed in code from the persisted answers, never guessed by the model, and the agent's instruction is rebuilt from that set on every turn. While anything is missing, the instruction says interview first. The model does not decide when it has enough. The code does.
The second one was trusting the model with exclusions at all. Early on, allergen handling lived in the prompt, and prompt based rules degrade under pressure from a long conversation. Moving exclusions to a deterministic table outside the model, applied as a filter on the output rather than an instruction on the input, removed that whole class of failure from the model's hands.
Third was the live voice path. I wanted the assistant to work over voice, and ADK's runLive session hung on me for a while before I traced it to environment handling around the credentials. A bidirectional runLive session on the same agent and tools is verified working through committed repository scripts, streaming real PCM audio and live transcription back. Speech input from a microphone is the next step. The recorded demo video is the chat path, because chat is the clearer four minute story, and I would rather show what I can show live than narrate over something I cannot.
Accomplishments that I'm proud of
The grocery list is genuinely derived, not stored. It is a function of plan minus pantry, recomputed on every change, so it cannot drift out of sync with the plan the way a list you edit by hand does.
Every recommendation carries a reason that points at household data, not at model confidence. When Amalthea says a meal fits, it names the pantry item or the budget number it is fitting.
The security work is written down rather than claimed. The repository includes docs/adversarial-testing.md with four documented attacks and their results, including an attempt to reach another household's data and a prompt injection submitted through the note field of a pin request. Both were held. The write path being limited to eight tools, with the data port bound server side to one household, is why.
The whole thing is solo, and all of it was built inside the submission window.
Findings and learnings
Four things came out of building this that I did not expect going in.
Constraints belong in tool contracts, not prompts. Every rule I moved from the system prompt into a precondition on a tool got more reliable, and it got testable at the same time. Prompt rules are suggestions to a model. Tool preconditions are checks.
Determinism where it matters, generation where it helps. The interesting part of this agent is not that the model is creative. It is knowing which decisions the model is allowed to make. Meal ideas, yes. Whether an excluded ingredient reaches the user, no.
Prompt injection arrives through user content, not just user messages. The attack that taught me the most was an instruction submitted as the note on a pinned meal, aimed at the field the agent stores and later reads back. The note landed as inert data and nothing executed, and the test is documented in the repo. Any field a user can write and the agent later reads is an injection surface, and I treat stored household content as untrusted input on the way back in.
Flash is enough for this shape of work. The interview and the planning both run on Gemini 3.7 Flash. The value came from structure around the model rather than from reaching for a larger one, and that kept the running cost near zero on a scale to zero Cloud Run service.
Data sources
No third party data feeds. The recipe corpus and the demo household are synthetic data I created for this project. Household state, pantry contents, plans, pins, and exclusions are stored in Firestore per household. Nothing in the demo comes from a real person or a real family.
Code disclosure
All code in this project was newly created during the submission period. There is no pre-existing code. Standard frameworks and libraries are used as dependencies, specifically ADK for TypeScript, Next.js, and the Google Cloud client libraries, along with an AI coding assistant during development. The repository is public under the MIT license.
What's next for Amalthea
Voice as the default entry point rather than a verified secondary path, because the interview is the part of this that most wants to happen while someone's hands are busy. Receipt and photo capture so the pantry updates itself after a shopping trip instead of by hand. Multi week planning that carries leftovers and partial ingredients forward across weeks, which is where the budget savings actually compound.
Built With
- adk-typescript
- cloud-run
- firestore
- gemini
- gemini-3.7-flash
- google-adk
- google-cloud
- next.js
- node.js
- react
- typescript
- vertex-ai
Log in or sign up for Devpost to join the conversation.