Thursday 10 / 07 / 2025

タイラーRetourner

Bazza

Bazza is a restaurant platform: mobile ordering, a POS, tables, inventory, and cash. Midway through building it, the interesting problem stopped being the screens and became the assistant: something a manager can talk to on a Saturday night with their hands full, and have it actually close the register.

The context isn't in the prompt

How do you make an agent "know your app"? The usual way is to describe the app in the system prompt, but that is how a model ends up inventing product IDs and writing to the wrong tenant.

Here the tools are built with the session already inside them:

typescript
const tools = [
  ...createAnalyticsTools(session),
  ...createPosTools(session),
  ...createInventoryTools(session),
  // 19 modules, all the same shape
]
You say "cierra la caja". Better Auth resolves who you are and which store.

Every tool is a closure over the authenticated session: organization, store, role, currency, and timezone. The model never passes a store ID because there is no store ID parameter to get wrong, and because the tools call the same services the dashboard calls, there is no second version of the truth to keep in sync.

One rule on top: don't invent numbers. Any quantitative claim has to come out of a tool call.

Propose, then create

When the assistant thinks you should add a product, it doesn't add the product right away. Instead, it calls propose_product, the dashboard renders a preview card showing the fields and the reasoning behind each one, and create_product runs only after you accept it. The business profile works the same way.

A confident wrong guess becomes a card you dismiss, not a row you have to find and delete. The model gets to be wrong where being wrong is cheap. And when a value is missing, it asks, typed: an amount asks for a number, and a choice renders as options.

The close-day ritual

You say "cierra la caja". What does it do? If more than one drawer's open, it asks which, showing the expected amount next to each name. Then it computes what the drawer should hold from every order, payment and movement of the day, and tells you.

It doesn't ask you to count the cash. It only asks for a physical count if you say there's a difference. That's the rule I keep coming back to: zero questions on the default path, and a question only where the human knows something the database can't.

Underneath

Google ADK, Gemini 2.5 Flash, tools as FunctionTool with Zod parameters, inside the same Elysia server as everything else. Bun and Moon, Postgres, Drizzle, and a Vite dashboard.