Why we don't let AI guess prices — deterministic lookup in WhatsApp agents
By Mohit · · 6 min read
A customer asks "how much is the 8GB VPS with NVMe?" The correct answer is one specific number from one specific row in a spreadsheet. Not a range. Not an approximation. One number.
If the LLM hallucinates even once — wrong number, wrong plan, wrong availability — the customer either overpays and churns when they find out, or you lose the sale because the real number is higher than the hallucinated one. There is no graceful failure mode for a wrong price.
The hallucination problem
When we started building AI agents for product catalogs, we ran extensive eval suites against pricing queries. Most answers were accurate — correct specs, correct plan names, faithful comparisons. But the pricing queries told a different story.
The LLM occasionally invented numbers. Not wildly — it didn't say a VPS costs a million. It would quote a number that felt right, close to the real one, but wrong. A plan that costs one amount would get quoted at a slightly different amount. Sometimes it merged attributes from two different plans into a third plan that didn't exist.
Even a high accuracy rate isn't good enough for prices. If even a small percentage of customers get quoted the wrong number, each one is either a lost sale or a support escalation. For any business processing steady inquiry volume, that adds up fast.
The bypass
The fix wasn't a better prompt. It wasn't retrieval-augmented generation with stronger context. It was removing the LLM from the equation entirely for pricing queries.
When our intent classifier detects a pricing query, it skips the LLM. Completely. It maps the query to a plan name using fuzzy matching against the catalog, pulls the exact row from the CSV, and formats a structured reply from a template. The LLM never sees the query. The response comes from the spreadsheet, not from a model.
- Response time — single-digit milliseconds instead of hundreds for an LLM round-trip
- Accuracy — the number comes from the source of truth, not a prediction
- Cost — zero LLM tokens consumed
- Hallucination risk — zero. There is no generation step to hallucinate
How it works technically
The pricing bypass is a four-step deterministic pipeline. No neural networks, no embeddings, no inference calls.
1. INTENT DETECTION
Regex patterns: "how much", "price", "cost", "rate",
"what does it cost", "pricing", "charges"
→ If matched: enter pricing bypass
2. FUZZY PLAN LOOKUP
Input: "8 gig VPS with NVMe"
Norm: "8gb vps nvme"
Match: VPS-8GB-NVMe-cPanel (score: 0.91)
→ If score > 0.75: proceed with matched plan
3. CSV ROW FETCH
Pull row from catalog CSV by plan ID
→ Returns: all attributes for that SKU
4. TEMPLATE RESPONSE
Slot plan name, specs, and pricing into
a structured WhatsApp reply template
→ Send directly. No LLM involved.
The fuzzy matching is the critical step. Customers don't type canonical plan names. They say "8 gig VPS" or "eight GB virtual server" or "the NVMe one with 8 gigs." The normalizer strips units, expands abbreviations, lowercases everything, and runs a token-overlap similarity score against every plan name in the catalog.
We've found that a similarity threshold around 0.75 works well in practice. Below that, the match is too ambiguous — the bypass defers to the full RAG pipeline. Above 0.90, we leave easy matches on the table because of minor formatting differences. The exact threshold gets tuned per deployment during the eval phase.
Why this matters at scale
In our experience, a meaningful chunk of typical product queries are simple lookups. "How much is X?" "Is Y available?" "What's the rate for Z?" These are among the most common questions any product-based business receives, and they're the queries where accuracy matters most.
Every query that the deterministic layer handles is a query the LLM doesn't touch. That saves tokens, saves latency, and frees up the LLM context window for the genuinely hard questions — the comparisons, the workload-specific recommendations, the open-ended exploratory queries where generation actually adds value.
The fallback
The bypass isn't a hard wall. When the fuzzy matcher can't find a confident match — similarity score below the threshold — the query falls through to the full RAG pipeline. The retriever searches the indexed catalog, the reranker scores relevance, the confidence gate checks if we should answer at all, and the LLM generates from retrieved context.
This fallback handles the genuinely complex questions: "which of your plans would work for a Django app with 200 concurrent users and a PostgreSQL database?" No regex will catch that intent. No CSV row will answer it. That's what the LLM is for — synthesis over unstructured context, not regurgitation of structured data.
The key insight is that the fallback sees a filtered query population. The easy, high-stakes pricing queries are already handled. What reaches the LLM is the long tail of exploratory, comparative, and advisory queries — exactly the kind of work where LLMs excel and where a small amount of creative latitude is acceptable.
The deterministic bypass is one layer of a larger architecture designed around a simple principle: the best LLM application is the one that calls the LLM least. If the answer lives in a row, serve the row. If it lives in a document, retrieve the document. Only generate when you have to.
For a technical deep-dive into the full waterfall architecture, see Inside InboxWa's automation engine.