Inspiration

I'm an AI engineer based in Harare, Zimbabwe. For most of Africa connectivity and "compute" is a luxury. So to deliver intelligence for the continent's problems, my lab develops models for low resource environments: sensors on machines, models on phones, deployments in places with no reliable connection. One product, SiloSense(on-device acoustic intelligence for post harvest loss prevention), went all the way through: a 106GB public dataset cut to a 246MB verified subset, a small CNN quantized to 0.111MB, ported to Kotlin, shipped on Android at 3.47x speedup with no accuracy loss.

We wrote that path down as a twelve-step recipe, and named twenty more projects that could follow it. A recipe on paper doesn't enforce itself. Every new project re-taught the same lessons: split by source, never by window. Quantize statically, never dynamically. Verify on a real held-out set before calling it a win. Writing the rule down once was never the problem. Remembering it the second time was.

The Collaborative Partner track asks for an agent that acts, that asks clarifying questions, that captures feedback, that adapts to how you think. So we built one that does the work itself: it fetches a dataset, trains, quantizes, verifies a real model. When it hits an actual decision worth making together, it stops and asks. It's a specific question about this dataset, with real options and a recommendation, grounded in what it just found and what past projects here learned the hard way. Your answer changes what happens next.

What it does

Give Recipe Mentor a problem statement and a Kaggle dataset, from a terminal or through the hosted interface at recipe-mentor-web-186891137448.us-central1.run.app. A real Google ADK agent, running Gemini 3.5 through Vertex AI, works through a sequence of tools it calls itself: fetch the dataset, detect what kind of task it is, sample it for real findings, and, before it configures anything, puts an actual decision to you. It's a specific preprocessing or hyperparameter choice this dataset actually presents, with concrete options and a stated recommendation, grounded in what it just found and in what past projects in this lab recorded the hard way. Your answer changes what trains. Then it configures, trains, quantizes, verifies, and records, and asks one more question at the end for feedback. Neither question is scripted small talk. You can also just ask it, separately, what's been tried across every project so far and what's worth exploring next, and it answers from the real recorded history.

That's the proof: start a second, unrelated project and the agent recalls, unprompted, what tripped it up on the first one, dated and sourced. Get the split wrong on a vision task, and the audio task already knows to check for it. This works in both directions now, too. The original two hand-authored projects (a maize leaf-disease classifier, a diesel-generator fault detector) and every agent-run project share one passport, so a lesson from an autonomous run turns up in a guided session just as readily as the reverse.

Every run is real. Real Kaggle download, real training on CPU, real ONNX export, real static INT8 quantization, real FP32-vs-INT8 verification on held-out data. Two task types are supported today: image classification and audio anomaly detection, each generalizing a pipeline we built and verified by hand first.

How we built it

Recipe Mentor architecture

Persistent memory: SozoGraph's Passport, an existing open-source library, extended with zero schema changes, even for a dynamically created project. Its identity lives in passport.meta, a field the library already leaves open for exactly this.

Recall: a deterministic tag filter, now also filterable by task type. No vector search, no embeddings. The thing being recalled belongs to a known, closed structure, a recipe's own step numbers, so a plain filter over facts and observations does the job a retrieval index would do worse and more expensively.

Agency: a Google ADK agent (LlmAgent + Runner + session) with real tools, state living in the tool layer, never round-tripped through the model, so the model's job is deciding what to do. It never has to remember a file path across turns. The tools enforce the recipe's discipline themselves: there's no tool that skips verification, only ones that clamp a hyperparameter and say so.

Collaboration, made real: one tool, ask_user, genuinely blocks. It creates an asyncio.Future, and the whole agent loop waits until an actual human answer resolves it. The system instruction directs the agent to call it after sampling the dataset for real findings (class balance, dimension spread, sample-rate consistency), grounded explicitly in those findings and in what past runs recorded, with concrete options and a stated recommendation. Getting this right took two real bugs, both found by testing the live loop: answering the tool one ADK event too early (before the tool's own coroutine existed) produced a genuine hang; driving the ADK event stream from a fresh task per event broke its internal tracing context. Both fixed properly: a synchronous callback fires only once the tool is truly waiting, merged into the event stream via one persistent background task.

Hosting: a FastAPI app on Cloud Run, streaming every tool call live over Server-Sent Events, including the moment it's waiting on you. Vertex access comes from the service's own account. Kaggle access comes from whoever's using it: bring your own token, used once, never stored. Kaggle's own terms don't allow sharing one person's credentials to fetch data for a stranger.

Persistence: a dedicated Firestore database, so a project started through the browser and one run from a terminal land in the same history.

Artifacts: the passport is the durable record. The trained ONNX files and each run's report.json sit on local disk, under recipe_mentor/data/, keyed by dataset. Cloud Run's own filesystem is ephemeral, so a hosted run's model files last only as long as that container instance. The passport in Firestore is what actually survives.

Reflection: a second, lighter agent, no tools at all. Just a real Gemini conversation grounded in the passport's own recorded history, for "what have we tried, what's worth trying next." Reuses the judge's own session plumbing; the only new thing is the instruction and a real summary of every project's status, metrics, and lessons.

Pipelines: a shared quantization module (common_quant.py) used verbatim by every project, hand-built and agent-run alike. Real datasets pulled from Kaggle, Zenodo, and, for the autonomous path, whatever the person in front of it names.

Challenges we ran into

Making the collaboration real instead of cosmetic. It would have been easy to bracket an autonomous run with a check-in before and after and call it collaborative. We built the harder version instead: a tool that genuinely pauses mid-run, on a real asyncio.Future, for a decision actually worth making together, grounded in what the agent found sampling the dataset. Getting there took two real concurrency bugs, both found by testing the live loop: answering the tool one ADK event too early resolved a future that didn't exist yet and hung forever; driving the ADK event stream from a fresh task per event silently broke its internal tracing context. Both fixed properly.

A chicken-and-egg problem: cross-project recall is more useful the earlier it happens, but the agent doesn't know a run's task type until its second tool call, after the dataset's already been inspected. Solved by recalling for every known task type up front and letting the agent apply whichever turns out relevant, a small, real piece of judgment left to the model instead of the harness.

Kaggle's own terms don't allow sharing one person's API token to fetch data on a stranger's behalf, which ruled out the simplest hosted design (one shared credential, baked into the service). Each visitor brings their own, used once, in memory only, gone the moment their run ends.

Accomplishments that we're proud of

A real ADK agent, live against Gemini 3.5, called every tool in the right order on its own. It asked a real, dataset-specific question mid-run and genuinely waited for the answer, read a hyperparameter clamp back and adjusted nothing further because it didn't need to, and closed with a real paragraph grounded in its own numbers and the decision made together.

Cross-project recall proven in both directions on the same passport: an autonomous run recalling a hand-guided session's lesson, and a hand-guided session recalling an autonomous run's.

Shipped a real, public, hosted interface. Anyone can open it, name a dataset, and watch a real model get trained, quantized, and verified in front of them.

What we learned

Coarse tools beat fine ones for an agent that has to finish reliably in front of an audience. A handful of tool calls with real work inside each one gives a model far less surface area to get lost in than many calls passing state between them would.

A guardrail is more convincing when the model can see it work. Reporting a clamped hyperparameter back in the tool's own result, instead of silently substituting it, turned a safety mechanism into something the agent visibly responds to.

An honest constraint (no shared Kaggle credentials) produced a better design than the shortcut would have. Bring-your-own-token is the design that scales past one person's dataset access.

A tool that genuinely waits is a different engineering problem than a tool that returns fast, and the difference only shows up under a real async framework. A mental model of one hides it. The moment a model decides to call a tool and the moment that tool's own code actually starts running are two different events, one step apart. Treating them as the same moment is exactly where both concurrency bugs came from.

What's next for Recipe Mentor

A real local corpus for the generator detector, since the actual lever on that AUC is more real data.

A third task type, tabular anomaly detection, following the same detect-then-generalize pattern the first two proved out.

A second real decision point, later in a run. The system already allows the agent to call ask_user again if a training result comes back surprising; we just haven't seen it happen live yet. Worth deliberately provoking in rehearsal.

The pattern generalized past ML recipes: onboarding, research, any repeatable workflow with lessons worth remembering across sessions and real work worth delegating, sitting on the same pieces: a passport, a recall layer, and now a real agent with real tools.

Built With

Share this project:

Updates