A store's own AI sales assistant beat a paid chat widget 34 : 12 in a blind test
A US outdoor-gear brand used a paid chat widget on its online stores. The widget was billed per store and often replied to a question with a question. The brand's Russian site had one more strict rule: customer data must stay in Russia. I built the brand its own chat assistant. It answers only from the brand's catalogue and documents; this technique is called retrieval-augmented generation (RAG). It runs on Russian language models. In a blind test against the paid widget it won 34 questions to 12.
Every new store raised the bill, and the bot kept answering questions with questions
The brand runs two Shopify stores and a separate Russian site. The two stores used a paid chat widget billed per store, so the cost grew with every new store.
The team's main complaint was quality. When a customer asked whether a product suited a particular job, the widget often replied with a clarifying question. When it did answer, it pointed to whatever was in its own product feed and ignored the job the customer had described.
The Russian site had one more strict rule: customer data must not leave Russia. That ruled out foreign model providers for customer conversations, even "temporarily, just for the pilot". This rule alone narrowed the choice of ready-made chat widgets a great deal.
So the brief was an assistant that answers from the real catalogue and the real shipping and warranty terms, keeps customer data in Russia, and does at least as well as the paid widget, with numbers to prove it.
Its own knowledge base, gateway and server
I built it as a separate project with four parts: a base of product facts, search over that base, a gateway that streams the answer, and a chat window that embeds on any site. Every answer is built from facts you can point to. Quality is checked by a test against a set of known correct answers.
562 facts: catalogue, FAQ, shipping and warranty, plus wording taken from real sales conversations
Embeddings over the fact base pick out the facts that matter for each question
FastAPI with a streamed answer (SSE), one interface for three language models, token refresh, retries and spending limits
The text streams first, then a separate message brings product cards with photos
Conversations and contact requests go to log files, and a copy of every exchange goes to a person on Telegram
The facts come from the catalogue and from real conversations
The base holds 562 facts: product specs, FAQ, shipping and warranty terms. The most useful source was 335 real conversations between the sales team and customers, 131 of them about stock. From them I took the answers and also the way people actually ask about size, weight, winter use and delivery times. A bot trained on marketing copy doesn't recognise questions put that way.
One gateway for three language models
The gateway sits on top of the search and streams the answer (SSE), so the customer reads it as it's written. Behind one interface sit three providers. YandexGPT answers customers on the live site. GigaChat and DeepSeek are there for comparison tests and cheap experiments. Switching models doesn't touch the chat window. The routine but essential parts all live in the gateway:
- access-token refresh and retries that respect
Retry-After - one request at a time per session, so a single user can't start five answers at once
- a spending cut-off, so abuse can't run up the model bill
- phone numbers and emails are masked before logging, so they don't reach the conversation files
- protection from bots with Cloudflare Turnstile
- voice input: a customer can simply say the question
Product cards arrive right after the answer
The text goes out first, so the customer can start reading at once. Right after it, the gateway sends a separate message with cards for the products mentioned: photo, name and link. The advice stays easy to read, and the customer can go straight from the chat to the cart. Once the conversation gets specific, the chat also saves the customer's contact and passes it to a person.
Product cards got their photos back
For a while the cards under the bot's answers showed a placeholder letter where the product photo belonged. There were two causes at once. The daily catalogue update overwrote the file with records that had no image field, and the fixed list of image links had gone out of date after the site's links changed. Now the update takes the image straight from the store's API response, the first photo in the gallery, and checks its host the same way it checks links. Each run logs a "with photo: N of M" count, and a test on the index keeps the fix in place.
The request reaches sales with the whole conversation
A manager calls the customer back using the contact request, so the conversation behind it has to travel with the request. It used to live only in a log that was regularly overwritten. By the time a real request with a phone number came in, the conversation was already gone. Now the request carries the whole session. The two are joined by session ID, and the report labels the conversation with the name the customer gave. If the text had to be shortened, the report says so: "showing 2 of 5 turns, 3 more didn't fit".
Quality is checked by a test
The first quality check used a second model to grade the answers. I dropped it. It kept missing failures on exactly the category questions the sales team had complained about, and it passed any answer that sounded coherent. Now a fixed test does the job: for each question we know which facts must appear in the answer, and the check looks for them. On top of that there are 134 pytest tests, including grounding (every claim in an answer traces back to the fact base) and abuse protection.
The check for gaps in the knowledge base stopped marking correct answers as "evasive". The chat is not allowed to quote shipping costs or instalment rates; a manager calculates them. Such answers follow the store's own policy, yet the old rules would have sent them for repair. Now only "no data" and "not specified" count as a real gap, and "a manager will calculate it" is checked against the topic of the question. The "evasive" section is gone from the daily report; "weight not specified" still counts as a gap.
The tests needed work too. Seven of them failed with a type error, so they checked nothing. The test stand-in for the model still took two arguments after the working code had gained a third, for token-spend accounting. That left the captcha, the daily budget and replay protection untested. The same review found that one test run used up 810 of the 1,000 daily calls meant for site visitors. Local checks now have a separate counter.
Hard rules in code on top of the model
The three weeks after launch went into answer accuracy and data integrity. The store sent error reports as text and as voice notes; the voice notes are transcribed automatically and join the same queue. Two of the errors could have cost a customer money. Asked about an inflatable tent, the bot first suggested a weak 2 PSI pump that can't inflate it. It also said with confidence that a clear end wall comes in the box. It had confused the wall with the windows made of the same material, listed one line above in the specs.
Both are now fixed in code. The weak pump is filtered out of tent answers, and in the general list of pumps it comes with a note on what it's for. The end wall now has a lookup table for the three models with data on file. For every other model the bot gives one fixed answer: "no data on file, a manager will confirm".
The lesson for the next project: editing the knowledge base alone didn't help here. The model kept inferring "included" from the neighbouring spec line, as it had earlier with the stove safety rules. Where a wrong answer costs the customer money, the answer is fixed in code.
The index now contains every file it should
Six knowledge-base files were written specifically to answer the store's complaints: stock and dispatch, how to give advice, what's in the box, compatibility, use and care, delivery and payment. They were missing from the list of allowed sources, and the build skipped them without a warning. However many times the index was rebuilt, the bot knew nothing from those six files. They are now on the list.
One limitation remains: the index is stored outside the repository, and rebuilding it takes a person and a cloud key. Changes to the knowledge base reach the live chat only after that rebuild.
A step-by-step rollout on a live site
The site runs on a website builder, and every edit goes live at once. So I rolled the chat
out in steps. First, a backup. The chat code was added to the end of the page's
HEAD,
and I checked that the existing markup stayed unchanged. Then one home page, a check on the
live site, and only then the remaining pages, about sixty of them. The rollback takes three
steps and is written down next to the instructions.
Built to meet the data-residency rule
The gateway that streams answers; runs as a systemd service on its own server
562 facts plus embeddings; product cards have an index of their own
Answers customers on the live site; the data stays in Russia
Alternative models behind the same interface, for comparison tests and cheap experiments
Voice input: a customer can simply say the question
Protection from abuse without making customers solve a captcha
One snippet that embeds into a website-builder site without access to its source code
Each answer is traced from start to finish: search, model call, token spend
Services and timers on its own server, with an alert when one stops
Grounding, abuse protection and the fixed answer check that replaced the model grader
A blind test against the paid widget
store's own assistant / paid widget, 3 ties
21 of 49 for the widget, none for the store's own assistant
completeness: 3.98 and 2.90
Here is how the test was built. 49 questions were written from 174 real customer questions and spread over a grid of 7 customer types and 7 stages of buying, from "first time on the site" to "already decided, just checking the warranty". Every question went to both bots, the answers were shuffled, and the grading was blind: the grader never knew which bot gave which answer.
The biggest difference was whether the bot answered at all. On 21 of the 49 questions the paid widget replied with a question of its own, exactly what the sales team had complained about. The store's own assistant answered all 49 on the merits.
The chat is live on the site. One evening the business owner left a comment, and by morning two fixes were live: the bot stopped making up which products were popular and started going by actual sales. A fix that goes live the next day is a good sign that the system is easy to maintain.
The cost is also predictable: you pay for the tokens you use and for your own server. It doesn't grow with the number of stores, there are no licences, and the spending cut-off in the gateway keeps the bill under a set ceiling.
Where the same approach works
The underlying task is common: give advice on a narrow subject, where every answer has to rest on facts. The same setup works wherever a company has its own body of knowledge and a wrong answer is expensive:
- → Stores where choosing is hard: equipment, machinery, gear, anything a buyer needs explained
- → B2B sites with long sales cycles: the chat sorts out the enquiry and hands it to sales with the conversation attached
- → A helper for your support team: the same knowledge base suggests answers, so staff don't have to dig through policy documents
- → Any field where data must stay in the country: healthcare, finance, the public sector. The setup is the same
- → Replacing a subscription widget: when the fee grows with every location, store or brand, your own assistant can pay for itself
- A streaming gateway with one interface for several model providers: switching models doesn't touch the chat window
- The recipe for the knowledge base: catalogue, policies and real conversations
- A fixed answer test against known correct facts. In this project a model grader missed exactly the failures it was meant to catch
- A blind comparison test on questions built from real ones, spread over a grid of customer types and buying stages
- A step-by-step rollout: backup, one page, a check on the live site, then the rest, with a three-step rollback
- A copy of every conversation goes to a person on Telegram for the first few weeks, so someone sees what the bot says
If your chat widget answers questions with questions, it can be fixed
I start with the knowledge base and the way to check it: what the bot must know and how to prove that it knows it. In this project it took four weeks to go from a prototype to the live site. After that came ongoing work on reliability and accuracy.
Blog article on this topic: Booking chatbot: why it answers with questions, and how to measure it
Related cases and ready-made solutions
A voice assistant: current prices, order lookup and human handoff
Conversation scenarios connect to the catalogue and CRM through dedicated tools. Order lookup requires a…
A voice agent that answers salon calls day and night and books clients into YClients or another booking system
It answers calls at any hour, books clients into YClients or another booking system with an API, and sends an…
A voice agent for dental clinics that spots acute pain and books patients into the schedule
A patient calls or writes on WhatsApp, and the agent asks eight questions to work out how soon they need a…