Skip to content
namiciLabby pentry
AI & Automation

A chatbot that works for one client is not a product

Everybody makes the prompt configurable. The words that stop a chatbot being resellable are the ones nobody ever thought of as a prompt - and the fix is to let an unconfigured bot say nothing at all.

namiciLab8 min read
chatbot-one-product

We built a WhatsApp assistant for a property agency. It answered questions about listings, quoted prices from their own catalogue, recognised when somebody was ready to book a viewing, and did all of it in the voice of the woman who owned the business. It worked.

Then a furniture shop wanted one, and we found out how much of that agency was buried inside the code.

The prompt was already configurable - that part everybody gets right. The problem was everything we had never thought of as a prompt.

The words were in the code, not the settings

Bot-voiced text turned out to be hiding in six places, and only the first was obvious.

The system prompt, of course. But also: the sentence a search returns when it finds nothing. The description handed to the model when a customer sends a photo with no caption. The reply to somebody's opening greeting. The short status lines the system adds each turn to say whether photos went out. The field labels on catalogue entries. And the words used to decide whether a customer had agreed to a meeting.

The search tools were the instructive case. When a lookup finds nothing it hands the model a short sentence so the model knows what happened. That feels internal - it goes to the model, not the customer. It is not internal at all. The model reads that sentence and says it out loud in its own words. It was the bot's voice, sitting in a source file, in whatever language the developer happened to be working in that week.

Nobody writes that line thinking they are writing dialogue. That is exactly why it survives every attempt to make a system configurable.

A default is a rule nobody can read

So the rule we rebuilt everything around is blunt, and the second half is the part that costs something:

Every word the bot says is a row in the database. Nothing is filled in for a blank field.

Leaving a sensible default behind each empty box is tempting. The bot always works, nobody ever sees a half-built deployment. But a default is a rule the person running the business cannot read, cannot edit, and does not know exists. When the bot says something strange, they open the settings, find an empty field, and there is nothing there to explain it.

Now an empty field contributes nothing to the prompt - no text, no heading, no trace. A search with no configured wording leaves the message out entirely rather than sending an empty one, and the model describes the miss itself, in the language of the conversation. That is right more often than a sentence we invented in advance.

An unconfigured bot should say nothing

The uncomfortable consequence of removing defaults is that a bot nobody has finished setting up has nothing to say. We decided that was correct.

If nobody has told it what to say, silence is the honest answer. A wrong voice is worse than no voice.

This is the same shape of argument as any safety threshold. The two ways of being wrong do not cost the same:

  • A bot that stays quiet costs one message. Somebody notices, checks the settings, fills in the missing piece. The failure is loud and it is cheap.
  • A bot that answers in a voice nobody chose costs trust, and you find out from the customer. It quotes a price nobody approved, in a language nobody picked, and it sounds completely confident doing it.

The number that answered as the wrong business

That principle was easy to agree on and harder to actually ship.

Each WhatsApp number points at a persona, and the persona decides everything - the voice, the rules, the product information it can reach. A number with nothing assigned used to reply anyway, borrowing whichever persona happened to be switched on. A client with a sales line and a support line who set up only one had the other quietly answering as the first, from the first's product catalogue, with nothing on any screen saying so.

We removed that fallback, tested it, and the bot still answered.

There was a second fallback one layer down, doing the same thing for the same reason, written by somebody sensible who did not want the bot to fail. Both had to go. The message a customer sends is still recorded - a real person wrote it, and it belongs in the inbox where somebody can pick it up - but nothing is sent back, and the log says which number is unassigned and which screen fixes it.

The lesson we keep relearning: when you decide a system should refuse to act, check that it actually refuses. Good intentions leave fallbacks all over a codebase, and they are invisible until the day they fire.

Greetings are settings, not patterns

Greetings get answered in kind. Somebody who opens with a salam gets the conventional reply above whatever else the bot says. Leaving that unanswered reads as rude in a way no amount of correct information makes up for, and it is far too conventional a response to leave to a model that might decide to be creative.

That behaviour lived in the code as a table of matching patterns. Moving it into the dashboard raised an obvious question: should the form accept a pattern?

It should not. A settings box that accepts raw pattern syntax is a trap - one stray bracket typed by a client throws an error on every incoming message, and the whole bot goes down. What gets stored is a pair of ordinary phrases: when they write this, reply with that. Matching handles whole words only, ignores repeated letters so one row covers "hello", "helloo" and "hellooo", and prefers the more specific phrase when two could match.

The matcher knows nothing about language, which is the entire point. A row saying assalamualaikum answers waalaikumsalam works exactly the same way as one saying bonjour answers bonjour.

Now you have a blank page

Strip out every default and you have solved a real problem and created a new one. A new client opens the dashboard and finds eighteen empty boxes and no idea what belongs in any of them.

Two things fixed that without smuggling the defaults back in. Every field shows a worked example as faint placeholder text - never saved, gone the moment they type, showing the shape of a good answer instead of describing it. And one button fills every empty field at once with suggested wording, written through the ordinary save path so the text lands in the settings where it can be read and edited like anything else. Same words as the old defaults; the difference is that now you can see them.

The one field that button will not touch is the bot's identity. Nobody else can say who this bot is, and a bot introducing itself as a template would be worse than one that refused to start.

Beside it sits a checklist of what still stops the bot working, each item naming the screen that fixes it. The case it exists for is the one that looks fine from the outside: a bot with a name, a personality, a full set of rules and no product information attached to it, answering every question politely and knowing nothing.

Say it before it starts, not after

Some failures never produce an error. An incoming message rejected for a bad signature fails deep inside a request nobody is watching. A missing search key means product information saves perfectly and can never be found again.

Both now print the moment the server starts, above the first request:

Not ready to serve customers. The WhatsApp app secret is not set, so every incoming message will be refused and the bot cannot answer anyone.

The deploy command runs the same checks against the settings file and stops before building anything. The alternative is hearing about it from a customer.

How you know it is set up

You check, rather than trust. There is a preview that shows the exact instructions the bot will be given - not a summary of them, the real thing - so a strange answer can be traced to the words that caused it. Anything left blank is visibly blank in that preview, which is the useful property: what is missing on the screen is missing in production too.

Which is the wider point, and it is not really about chatbots. A system built for one client always has that client written into it somewhere. The question worth asking of every sentence the software produces is a small one - whose words are these? If the answer is ours, it belongs in the code. If the answer is the client's, it belongs in a setting, even while there is only one client, even if the value would never change. The lines that were hardest to find were the ones nobody had ever thought of as words at all.

  • WhatsApp bots
  • AI assistants

Got an Idea?

Let's shape it into something people love to use.