A useful answer needs somewhere to come from
Leaf Creme already had a catalog, a cart, and inventory rules. Adding a chat window introduced a different question: what should an assistant be allowed to say about that system?
A customer asking for a cake under 200,000 VND does not need a convincing paragraph about an imaginary product. They need a cake the store actually lists, a price they can check, and a clear next step. If availability is uncertain, the answer needs to preserve that uncertainty.
That became the starting point for Leafie, the customer-facing sales assistant. I wanted it to help people choose a cake without quietly becoming another source of product data.

The catalog belongs to the server
The browser sends the customer's message and a bounded conversation history to the backend. The backend reads a fresh public catalog, then passes that snapshot with the versioned sales prompt to DeepSeek.
I kept the catalog on the server because a chat request should not get to define its own prices or stock. Client-supplied catalog fields are ignored. The assistant receives active products, variant prices, and sellable availability from backend data.
Fresh data still has a boundary. Availability is a snapshot at the time of the question, not a reservation. For products without verified variant availability, the catalog can return an unknown state. An active product is not automatically an available product.
Those distinctions sound small until a customer asks, “Can I buy that one?” A system that only understands true or false can turn missing information into a promise.
Product cards and model prose have different jobs
Leafie returns structured product references alongside its reply. The backend checks those IDs against the catalog and builds the cards from server-owned fields. The model does not provide the card's price, size options, or destination link.
The division is straightforward: the model helps interpret a request; the application supplies the product facts and navigation.
This does not prove that every generated sentence is correct. The prompt tells Leafie to leave prices and stock quantities to the cards, but prose remains model-generated. Validating product IDs gives the application a concrete check it can enforce. Prompt instructions alone do not provide the same guarantee.
Follow-up questions expose the real interaction
“Is that cake still available?” is easy to understand when the previous answer mentioned one cake. It becomes ambiguous when the answer suggested three.
The prompt distinguishes those cases. When history uniquely identifies a catalog product, Leafie should answer directly. When several products could fit, it should ask which one the customer means. History helps identify the subject; the current catalog supplies price and availability.
I also had to think about when the conversation should stop. A thank-you does not need another set of product cards or another sales question. The current prompt includes that behavior explicitly. Small conversational decisions affect whether an assistant feels helpful or tiring.

A sales assistant does not need operational authority
Leaf Creme has order, payment, inventory, and administration workflows. Leafie does not get access to those workflows just because they share an application.
It cannot look up private orders, confirm a bank transfer, change stock, or create a discount. Existing-order questions belong on the authenticated orders page. A new purchase follows the product page, cart, and checkout.
Ingredients and allergies need an equally clear boundary. A product name or image is not evidence that a cake is safe for someone with an allergy. When the catalog does not establish an answer, the assistant should direct the customer to the store instead of filling the gap.
Debugging has a privacy trade-off
Leafie uses optional Langfuse tracing to connect requests with the model, prompt version, token usage, and public catalog context. Customer messages, history contents, and generated replies are excluded from exported traces.
That means I cannot replay a production conversation from the trace and inspect every misunderstanding. I accept that limit and use synthetic conversations for semantic evaluation. Observability is useful only when I can explain what it records and what it leaves out.
Failures also need a visible outcome. A provider failure returns an explicit error so the interface can offer a retry. A fabricated fallback answer would make the chat appear available while breaking the reason for connecting it to real data.
What I can verify today
Backend tests cover catalog visibility, validation, privacy guards, limits, and provider failures. Browser tests exercise product cards, follow-up history, retries, and desktop/mobile layout with mocked responses. The screenshots in this post come from those browser tests.
Those checks verify application behavior. They do not establish how well the model handles real Vietnamese conversations. Recommendation quality, ambiguous references, allergy questions, and attempts to override instructions need separate evaluation with a working provider.
Building Leafie made me more precise about the boundary between a helpful conversation and a business promise. The useful work was deciding where facts come from, which checks the server can enforce, and when the assistant should say it does not know.