We had an online cooking school with 1,404 recipes, weekly menus, and active members cooking every day. The problem wasn’t a lack of content: it was that the user had the menu in front of them and didn’t always know how to execute it. That’s where we decided to integrate AI into the web platform, but with one clear premise from the start: the AI had to live inside the user’s flow, not be a separate tool you visit when you remember it exists.

We didn’t do it because AI was trendy. We did it because there was a real gap between having the recipe and knowing how to cook it, and an assistant with the right context closed that gap better than any FAQ or video. This article is what we built, the decisions we made, and what we learned along the way.

The conversational cooking assistant

The user opens their menu for the week and has the recipes, the nutritional totals, and the batch-cooking guide in front of them. What they’re missing isn’t information, it’s someone to ask “can I double this for four?” or “what do I do if I don’t have ingredient X?” That’s the gap the assistant fills.

The piece that makes it work is context. When the user opens the chat from a menu, they don’t start a blank conversation: we automatically inject the context of that menu —the recipes, the nutritional totals, the batch guide— without the user having to explain anything to the model. They ask “how long does this take?” and the assistant already knows what “this” is. Without that context, the model is a generic cook from the internet; with it, it’s the assistant for your specific menu.

The history persists per menu, so this week’s conversation doesn’t blend into last week’s. And the answers adapt to the user’s program: advising someone on a low-calorie plan isn’t the same as advising someone on a therapeutic one. Same assistant, different judgment depending on who’s asking.

The silent user profile

Nobody fills out onboarding forms. You know it, I know it, and our users know it too: give them ten fields to “personalize your experience” and they bail. So we decided not to ask for the profile. We let it build itself.

The assistant learns as it talks. If the user mentions they cook for four, that they’re intolerant to something, or that they’re not very handy in the kitchen, that gets saved to their profile without them filling out a thing. Personalization grows with every conversation, with no friction and no form in the way.

How? With what we internally call hidden markers. It’s the pattern I’ve learned the most from on this project, so it’s worth explaining properly.

In the system prompt we tell the model that, when it detects certain information, it should add a special marker at the end of its response, in an exact format. The user never sees it. The response that reaches the backend looks something like this:

Sure, for 4 people you can double the quantities in the stew.
Keep in mind that with legumes it usually tastes better rested.
PROFILE_UPDATE:{"servings": 4}

The backend, in PHP, does three things: it finds the marker, extracts the JSON and merges it with the existing profile in the database, and returns only the clean text to the frontend, with no marker. The user reads a normal cooking answer; the system, in parallel, has learned they cook for four.

What’s interesting about this pattern is that it doesn’t use the API’s function calling or tool use. The model speaks in plain text and we parse that text. And it’s the model that decides when to trigger it: there’s no trigger logic on the client scanning for keywords. If the user mentions something relevant, the model detects it on its own, because the system prompt taught it to. The conversation is asymmetric: the user sees natural chat, the server sees chat plus structured commands.

It carries a real risk, and we learned it by seeing it happen: the model can hallucinate the marker at the wrong moment, or format it badly and break your parse. The defense is being obsessively explicit in the system prompt about the exact format and about when to use it. Every ambiguity you leave, the model fills in its own way.

Generating content for production

The conversational assistant is the visible face of the AI, but a good chunk of the model’s work is invisible. We also use it to generate structured content that feeds the platform’s production.

The weekly menu generator is internal: the model puts together menu proposals that are then reviewed, not something the user touches directly. The batch-cooking guide is generated by turning a menu into efficient cooking instructions —what to prep at once, what to get ahead on. And the interactive recipe guide is a step-by-step that’s built dynamically instead of being hand-written for each of the 1,404 recipes.

The PDFs come out of this too. We generate HTML on the server, built from the same menu, with the content the model provides, and that HTML is what gets printed. The challenge wasn’t the AI itself but the bridge: assembling clean, print-ready HTML from the menu and from what the model returns, so the final document comes out consistent every time. The AI supplies the content; the PDF’s structure is supplied by the HTML we control.

Image recognition: where we’re headed

This part isn’t finished yet, but the direction is clear, and I’m covering it because it’s one of the most promising things we have in hand.

The idea: the user photographs their fridge or a product label, the model identifies the ingredients, and the system searches our database for compatible recipes from what’s there. It’s the bridge between computer vision and our own recipe database, which is exactly what a generic model can’t do on its own.

The planned pattern reuses the hidden markers. If the model identifies specific ingredients in the photo, it adds a marker like SEARCH: ingredient1, ingredient2 to its response. The backend intercepts it, runs the search against the recipe database, and returns the model’s text to the frontend along with recipe cards generated by the server, as if the assistant had brought them itself. The user gets a seamless experience; underneath, it’s the same trick as before applied to vision.

It’s half-built. But the fact that it fits into a pattern we already had working is the best sign that the architecture was on the right track.

What we learned

Context is everything. A model without the nutritional and menu context is useless in this domain: it gives you generic cooking advice the user already finds on Google. All the value is in what we inject before it answers. The AI wasn’t the hard part; deciding what context to give it, and when, was.

The hidden markers turned out to be a more general pattern than we thought: a way for the model to talk to the system without the user noticing, in plain text and without tying you to a specific provider’s tool use. That last bit matters more than it seems: since the model only produces text, tomorrow we can switch models or mix several without rewriting the mechanism. In fact, the customer-support chat we have on the horizon is going to use a different model, and the pattern still holds.

And the lesson that took me the longest to internalize: AI doesn’t replace your data structure. Without the 1,404 recipes properly modeled, without the ingredient base, without the structured menus, none of this works. The model is what converses and what writes, but the real value is the database underneath. The AI makes it accessible; it doesn’t replace it.

Where we are and what’s next

We started with a platform full of content the user didn’t always know how to make use of. Now they have an assistant that knows their menu, a profile that builds itself while they cook, and content tailored to each week.

What’s next: refining how we capture user data so the profile is even more precise, finishing image recognition, and building the customer-support chat. We also want to improve the comments system. None of those pieces is a leap into the void: they all rest on what we already built, which was exactly the idea —a base to keep building on, not a demo that impresses and then doesn’t scale.