<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonald Penpillo</title>
    <description>The latest articles on DEV Community by Jonald Penpillo (@jonaldpenpillo).</description>
    <link>https://dev.to/jonaldpenpillo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4225285%2Faca1178a-0652-419a-86a6-e920ff2cb4ff.jpg</url>
      <title>DEV Community: Jonald Penpillo</title>
      <link>https://dev.to/jonaldpenpillo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonaldpenpillo"/>
    <language>en</language>
    <item>
      <title>Why our Messenger booking bot doesn't use an LLM to decide what to say (and went from 55% to 71% on real guest messages)</title>
      <dc:creator>Jonald Penpillo</dc:creator>
      <pubDate>Wed, 04 Nov 2026 03:18:06 +0000</pubDate>
      <link>https://dev.to/jonaldpenpillo/why-our-messenger-booking-bot-doesnt-use-an-llm-to-decide-what-to-say-and-went-from-55-to-71-on-47ak</link>
      <guid>https://dev.to/jonaldpenpillo/why-our-messenger-booking-bot-doesnt-use-an-llm-to-decide-what-to-say-and-went-from-55-to-71-on-47ak</guid>
      <description>&lt;p&gt;&lt;em&gt;By Jonald Penpillo, AI developer and software engineer in General Santos City, Philippines.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most guests of a sports venue in the Philippines don't open a booking page. They open Facebook Messenger and type something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;magkano ang driving range&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's Tagalog for "how much is the driving range?". The next guest writes in English, the one after in Bisaya, and plenty write in Taglish, switching languages mid-sentence. They expect an instant answer, at any hour, and an exact price.&lt;/p&gt;

&lt;p&gt;This is the story of &lt;strong&gt;Range AI&lt;/strong&gt;, the assistant I built for &lt;strong&gt;Brigadahub&lt;/strong&gt;, the booking and payments platform of Brigada Hub in General Santos City: a 30-bay golf driving range, pickleball courts with open play, and a kids playground. Range AI answers guests on the website chat and on Facebook Messenger, and it can take a guest from a first question all the way to a &lt;strong&gt;paid booking inside the chat&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On real guest messages, its accuracy went from &lt;strong&gt;55% to 71%&lt;/strong&gt;. Here's how it's built, and why the most important design decision was &lt;em&gt;not&lt;/em&gt; letting a language model decide what to say.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule: never invent a price
&lt;/h2&gt;

&lt;p&gt;A venue chatbot that hallucinates is worse than no chatbot. If it quotes ₱200 when the rate is ₱250, the front desk has an argument and management loses trust in the whole system. A generic "LLM with a system prompt" is fluent, but fluency is exactly the problem: it will confidently produce a plausible wrong number.&lt;/p&gt;

&lt;p&gt;So the system is split into two jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Deciding what the answer is.&lt;/strong&gt; This is a classification problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saying it naturally.&lt;/strong&gt; This is where a language model helps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only the second job uses an LLM, and only behind a guard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: classify the intent
&lt;/h2&gt;

&lt;p&gt;Every incoming message goes through an &lt;strong&gt;intent classifier&lt;/strong&gt;: word-level and character-level &lt;strong&gt;TF-IDF&lt;/strong&gt; features feeding a &lt;strong&gt;calibrated LinearSVC&lt;/strong&gt; (scikit-learn), covering &lt;strong&gt;about 94 intents&lt;/strong&gt;, from driving-range rates and club rental to pickleball court rental vs. open play, opening hours and the playground.&lt;/p&gt;

&lt;p&gt;Why a classic model instead of an LLM?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Character n-grams handle Filipino chat.&lt;/strong&gt; Guests misspell, abbreviate and mix languages. Character-level features catch "mgkano", "magkano" and "how much" landing on the same intent far more robustly than word features alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibration gives an honest confidence score.&lt;/strong&gt; A calibrated classifier's probability means something, so "I'm sure this is a pricing question" and "I'm guessing" look different in the numbers instead of being the same raw score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's fast, cheap and testable.&lt;/strong&gt; Every prediction can be checked against a labelled set of real messages, so a change either improves the score or it doesn't ship.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: answer from approved replies only
&lt;/h2&gt;

&lt;p&gt;Each intent maps to a &lt;strong&gt;management-approved reply&lt;/strong&gt;: the venue's own wording, with the real rates and links. For the driving-range question above, the reply is fixed text with the public rates (₱250 for 1.5 hours with unlimited balls, club rental ₱150, extension ₱150 per hour) and a follow-up question about the date and time of the visit.&lt;/p&gt;

&lt;p&gt;The bot never writes a price. It retrieves one that management already signed off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: let the LLM reword, behind a fact guard
&lt;/h2&gt;

&lt;p&gt;Fixed replies can sound robotic, especially when the guest wrote in Bisaya and the reply bank is in English. So the approved reply is passed to &lt;strong&gt;Gemini Flash (via OpenRouter)&lt;/strong&gt; to reword it naturally and match the guest's language: English, Tagalog, Taglish or Bisaya.&lt;/p&gt;

&lt;p&gt;Then a &lt;strong&gt;fact guard&lt;/strong&gt; checks the reworded text against the approved original and blocks any rewrite that changed a &lt;strong&gt;price, an amount or a link&lt;/strong&gt;. The LLM improves the tone; it never gets the final say on facts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: book and pay inside the chat
&lt;/h2&gt;

&lt;p&gt;Answering questions is half the job. When a guest wants to book, Range AI turns the conversation into a booking flow: it asks whether it's a court rental or open play, offers &lt;strong&gt;live slot chips&lt;/strong&gt; from real availability, shows the &lt;strong&gt;waiver&lt;/strong&gt; and a &lt;strong&gt;quote&lt;/strong&gt;, and ends with a &lt;strong&gt;pay-now&lt;/strong&gt; button that completes the booking through &lt;strong&gt;PayMongo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the website, the same platform runs a five-step booking flow with live bay and court availability and PayMongo checkout, so guests can book whichever way they prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: know when to hand off
&lt;/h2&gt;

&lt;p&gt;When the bot shouldn't handle a conversation, Range AI &lt;strong&gt;hands it to staff&lt;/strong&gt; and sends an &lt;strong&gt;email alert&lt;/strong&gt;. A fast "let me get a teammate" beats a confident wrong answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring on real messages: 55% → 71%
&lt;/h2&gt;

&lt;p&gt;Accuracy was measured on &lt;strong&gt;real guest messages&lt;/strong&gt; from an anonymized chat dataset, not on examples we wrote ourselves. That distinction matters: test phrases you write yourself are clean and predictable, while real guests misspell, abbreviate and switch languages mid-sentence.&lt;/p&gt;

&lt;p&gt;The first version scored &lt;strong&gt;55%&lt;/strong&gt; on those real messages. Iterating on the classifier and its training data against that same real-world set brought it to &lt;strong&gt;71%&lt;/strong&gt;. Every change was judged by one question: does the score on real guest messages go up?&lt;/p&gt;

&lt;p&gt;71% is not 100%, and that's fine, because the system is designed around the classifier sometimes being wrong. Staff handoff catches what the bot shouldn't answer, and the fact guard means a reworded reply can never come out with a changed price.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell anyone building a business chatbot
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Separate deciding from wording.&lt;/strong&gt; Use the LLM where fluency matters and something deterministic where correctness matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure on real user messages.&lt;/strong&gt; Your own test phrases are always easier than what customers actually type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibrate your classifier.&lt;/strong&gt; A confidence score is only useful if it's honest; calibration is what makes it mean something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guard the facts.&lt;/strong&gt; Prices, amounts and links should be checked mechanically, never trusted to a model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finish the job.&lt;/strong&gt; The win isn't answering "how much?". It's ending the conversation with a confirmed, paid booking.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; React 19, TypeScript, Express 5, Supabase, PayMongo, Python, FastAPI, scikit-learn, Gemini via OpenRouter, Messenger API.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jonald Penpillo, an AI developer and software engineer in the Philippines. I build AI assistants and chatbots (RAG, LLM agents, Messenger bots) and the booking, payment and business systems they run in. If you need a developer to build yours, see &lt;a href="https://www.penpillo-jonald.online/hire-a-developer/" rel="noopener noreferrer"&gt;penpillo-jonald.online&lt;/a&gt; or the full &lt;a href="https://www.penpillo-jonald.online/?view=project&amp;amp;p=golf-range" rel="noopener noreferrer"&gt;Range AI case study&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatbot</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
  </channel>
</rss>
