Rubén Colmenares

Writing · July 2026

Building Martita solo: production agent patterns that survive real users

What ~7,500 lines of solo-built, revenue-generating agent code taught me about tools, memory, multi-tenancy, and the gap between demo agents and production agents.

Martita is a WhatsApp-native AI assistant for Mexican SMEs. I designed it, built it, and operate it alone — nights and weekends — and it’s live with paying customers. This post is the list of patterns I wish someone had handed me before I started: the things that separate an agent demo from an agent that survives real users.

1. Tools beat intent classification

My first instinct was the classic pipeline: classify the intent, route to a handler. It collapsed the first week of real traffic. Real customers write “hola, tienen cita mañana? y cuánto cuesta el corte?” — two intents, one message, informal Spanish, no punctuation you can trust.

The fix was to stop routing and start reasoning: give Claude a set of tools (Martita has 21 — scheduling, catalog, customer memory, business config, escalation) and let the model plan across them. A multi-intent message stops being an edge case, because the model simply calls two tools. The intent taxonomy I would have spent months maintaining doesn’t exist.

The discipline that makes this work: tools must be boring. Small, typed inputs; one clear responsibility; descriptions that say when to use the tool, not just what it does. Every production incident I’ve had with tool use traced back to a tool description that was ambiguous about when it applied.

2. Memory is the product

The difference between “a bot” and “a receptionist” is that the receptionist remembers you. Cross-session memory — recognizing a returning customer, recalling their last appointment, their preferences — is the single feature customers comment on most. It’s also invisible in a demo, which is why demo-driven agent development under-invests in it.

Practically: memory is not a vector database. For Martita it’s structured rows — customers, appointments, conversation summaries — retrieved by phone number at session start and injected as context. Deterministic retrieval, no similarity thresholds to tune, no “why did it remember the wrong thing” mysteries.

3. Multi-tenancy is an architecture decision, not a feature

Martita is one codebase serving every business, with hard per-tenant isolation of data, configuration, and conversation history. I made that call on day one, and it’s the reason a new customer can sign up, pay through Stripe, and configure themselves without me in the loop.

The rule: tenant identity is resolved once, at the boundary, and everything downstream receives it explicitly. No global state, no “current business” singleton. It’s the least glamorous code in the system and the most load-bearing.

4. The model is the cheapest part — spend accordingly

The LLM bill is a rounding error next to the cost of my time debugging bad conversations. So I optimize for debuggability: every conversation is logged with the full tool-call trace, and when something goes wrong I can replay exactly what the model saw. Most “the AI did something weird” reports turn out to be a tool that returned something weird — and without the trace, you’ll blame the model every time.

5. Ship the escalation path first

Before Martita could book a single appointment, she could say “let me get the owner” and actually do it. An agent with a reliable escape hatch can ship at 80% capability; an agent without one can’t ship at 95%. Customers forgive an assistant that knows its limits. They don’t forgive one that confidently does the wrong thing.


Martita is live at martita.ai. If you’re building agents for production and want to compare notes — email me.

← All writing