跳到正文
DEV Community· Dhruv·· 2 小時前AI 評分50

No-Show Desk:TabPFN 預測汽車旅館住客取消訂房,Gemma 草擬確認訊息

No-Show Desk: TabPFN spots the motel guests likely to cancel, and Gemma asks them first

AI 導讀

No-Show Desk 結合 TabPFN 與 Gemma 4,為小型汽車旅館和民宿找出較可能取消或未入住的住客,並草擬由店主決定是否發送的確認訊息。系統以30個真實過往早晨測試,提前識別172宗取消中的111宗,同規模隨機挑選約識別64宗。Google 開放權重模型 Gemma 4 在電腦瀏覽器本機草擬訊息,程式碼則核對日期、晚數及總額。

正文

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

A small motel loses a night's room every time a guest silently doesn't turn up and when no deposit was taken, nothing comes back. No-Show Desk is a morning assistant for small motels and B&Bs. It picks the arriving guests most likely to cancel then says why in plain words and drafts a short "are you still coming?" message that the owner sends or skips. Scored on 30 real past mornings, learning only from earlier bookings, its picks caught 111 of 172 cancellations, against about 64 for random picks of the same size.

Here is the demo morning (real 2017 bookings; guest names are made up). At 8 a.m. a Telegram message lands on the owner's phone:

Good morning. 7 guests arriving in the next 3 days might not show: $1,599.20 of bookings. Start with Chloe Nguyen (74%), Daniel Okafor (67%) and Ana Silva (61%), plus 4 more.

They tap through to one page. Chloe's row opens: room only, booked 72 days ahead, and "34% of past room-only bookings cancelled, against 26% of all past bookings without a deposit." They press Draft a message. A few seconds later there's a short note asking Chloe to confirm she's still coming or to say if her plans changed. Under it, in green: Dates, nights and total match the booking. They press Send by email. The owner decides everything; nothing is sent on its own.

I'd been pitching motels a voice agent that answers the phone when nobody's at the desk. When this week's theme asked for something a small business would actually pay for, motels were the business I knew, so I built something smaller: one list and one message each morning, simple enough for a few owners to try.

  • TabPFN, a model already trained to learn from small tables, scores every upcoming booking from the motel's own past bookings.
  • Gemma 4, Google's open-weight model, writes the words on the owner's own computer, right inside the browser.
  • Plain code works out every date, night count and total, and checks every draft before the owner can send it.

The No-Show Desk owner page with seven guests who might not show and a drafted message to Chloe that the checker has confirmed

On real past bookings, outcome hidden until scoring Measured
Cancellations the desk caught ahead of time, of all that happened, over 30 test mornings 111 of 172 (random picks of the same size: about 64)
Drafts with a wrong date, night count or total 13 of 100 when Gemma worked them out, 0 of 100 when code handed them over
Seconds per message, Gemma in the browser against Ollama (a program that runs models locally) on the same Mac 3.25 against 31.4 (median of 100; two builds of the same small model)

The rule I built everything around: code computes, the model phrases.

Who pays. The owner of a small motel or B&B, who pays for a booking system but has no data team. On the demo morning, 4 of the 7 flagged guests did cancel, holding $776.60 of bookings, and a single night there is $67 to $107. The value is hearing sooner, while the room can still be resold, so the desk has to cost less than one resold night a month. Gemma costs nothing per message.

Try it live: no-show-desk.onrender.com. Every visitor gets their own copy of the demo morning. On a computer, the first Draft asks before downloading Gemma (2 GB), then writes each message in a few seconds (tested in Chrome). Phones get the pre‑written message from Gemma, checked the same way. The free server can take up to a minute to wake. The 08:00 Telegram message comes from the version that runs on my computer.

  • Today: the guests who might not show, riskiest first. A row opens into the booking, the reasons, Draft and Leave (no message, with a few seconds to undo). Edit a draft and get a fact wrong, and the desk flags it as you type.
  • Replay: pick any of 30 real past mornings, see who the desk would have messaged, then press Reveal what happened. On the demo morning, Chloe (74%) really did cancel, and so did 3 of the other 6 flagged guests; Tara, at 26%, was below the 30% line the desk uses to decide who to flag and cancelled anyway. Drag the line or press Replay all 30 mornings to add it up against random picks.

Replay of the demo morning after Reveal what happened: caught 4 of 5 cancellations, and over 30 mornings 111 of 172 cancellations caught against about 64 for random picks, with one bar per morning

The demo lives on a fixed day in early 2017 because the data is real bookings that end in 2017. Guest names and contacts are made up. Prices are the hotels' own numbers and the data doesn't name the currency (the hotels are Portuguese, so probably euros) plus the page shows them with a $ sign. Send writes to a file and says demo: not delivered.

Code

GitHub logo My-CMDhub / no-show-desk

Find out which guests won't turn up, days before they don't. A morning no-show desk for small motels: TabPFN flags risky bookings, local Gemma drafts a reconfirm message, code checks every fact.

No-Show Desk

Know who won't turn up, before they don't.

A morning assistant for small motels and B&Bs. It flags the guests most likely to cancel or not show,
says why in plain words, and drafts a short "are you still coming?" message for the owner to send.

License: MIT Python 3.12 Risk: TabPFN Drafts: Gemma 4 E2B, local

Try the live demo · your own copy of the demo morning · Gemma writes in your browser on a computer

The owner page: seven guests might not show, with Chloe's drafted message open and checked

Why

A small motel loses a night's income every time a guest silently doesn't arrive. Many of those cancellations can be heard about earlier, if someone asks. No-Show Desk picks who to ask each morning, so the owner spends a minute on it, and a room that would have stood empty can be offered to someone else.

How a morning works

  1. 08:00, a Telegram message. "7 guests arriving in the next 3 days might not show: $1,599.20 of bookings. Start with Chloe Nguyen…
  • noshow/: the app. Risk, reasons, facts, drafting, the checker, the agent, the page and the Telegram bot. tests/: 61 tests for the logic.
  • scripts/ and results/: every measured result in this post comes from a script in the first and is written to the second, including every stored draft. The few things I only observed are said as such.
  • render.yaml and results/demo_snapshot.json: the live demo.

How I Built It

The data, and the columns that give the answer away

There's no public motel data, so I used the Hotel booking demand dataset (Antonio, Almeida & Nunes, 2019): 119,390 real bookings from two Portuguese hotels, 2015 to 2017. All 1,207 no-shows are marked cancelled, so the desk predicts both. Every test works on a motel-sized slice: about 3,000 past bookings and 20 arrivals a morning.

Some columns are only filled in after the guest arrives, so they give the answer away: bookings with a parking space recorded cancelled 0.0% of the time because the space is recorded when the car turns up. I dropped six such columns and every test learns only from bookings that arrived before a date and is scored on bookings after it, the way the desk is used.

The desk only scores bookings without a deposit: a guest who prepaid and then cancels costs the owner nothing (non-refundable bookings in this data "cancel" 99.4% of the time). All the percentages below are among bookings without a deposit.

TabPFN at motel scale

A 20-room motel has a few thousand past bookings at most. TabPFN was trained in advance on a huge number of synthetic tables, so it learns a new one in a single step, with no training run of my own. I gave it, gradient boosting (the usual tool for tables) and a plain logistic regression 300, 1,000 or 3,000 past bookings, three histories each and scored the same 5,000 bookings from 2017. The score is ROC-AUC: how often a guest who cancelled is ranked above one who didn't.

Line chart of ROC-AUC by number of past bookings learned from. TabPFN 0.721 at 300, 0.766 at 1,000 and 0.778 at 3,000. Gradient boosting 0.682, 0.702 and 0.747

TabPFN ranked best in all 9 runs, with gradient boosting (default settings) furthest behind. The plain regression came much closer than I expected (0.699, 0.741 and 0.749), so the honest summary: TabPFN wins at every size, by 0.02 to 0.03 over the regression.

Checking the cut-off on a year it never saw

My first version flagged anyone over 50%. On my first demo morning it flagged 1 guest of 20, who didn't cancel, and missed all 4 who did: with only about 3 in 10 guests cancelling, few scores ever reach 50%. So I picked 30% on 30 mornings from 2017 then, because choosing and reporting on the same mornings is peeking, checked it on 30 mornings from 2016 that the choice never saw. Two measures: cancellations caught, of all that happened, and how many flagged guests really cancelled.

Flag when 2017 (picked here): cancellations caught flagged guests who did cancel 2016 (never seen): cancellations caught flagged guests who did cancel
5 riskiest 84 of 172 56% 81 of 134 54%
25% or more 129 of 172 49% 105 of 134 52%
30% or more 111 of 172 53% 92 of 134 56%
50% or more 70 of 172 65% 66 of 134 77%

On 2016, which the choice never saw, 30% still caught more than the 5-riskiest rule or the 50% line, and 50% left 2 mornings with nobody flagged. 25% catches more, but fewer of its flagged guests cancel. Replay in the demo recomputes these rows in browser and they match the results files exactly.

Bar chart. Over 30 test mornings in 2017 the desk's flags found 111 of 172 cancellations. Picking the same number of guests at random would have found 64

Is it better than a rule I could follow by hand?

All seven guests flagged on the demo day booked through the same travel agent (#9; the data numbers agents), so could an owner just follow a rule? Each rule got the same number of messages per morning as the desk:

Same number of messages each morning Cancellations caught, of 172
TabPFN, 30% line 111
Logistic regression, fitted on each morning's history 106
Online travel agent first, then longest lead time 99
Travel agent #9 first, then longest lead time 94
Longest lead time first 81

The best rule I could think of finds 99. TabPFN finds 111 by weighing everything together, 5 more than the regression.

Reasons a person can read

TabPFN gives a probability, not a reason, so the reasons come from plain counting. A guest's group (lead time, travel agent, channel, meal plan) is named only if it cancelled at least 5 percentage points more often than all bookings without a deposit, over at least 30 of them. The reasons describe the group; the ranking is TabPFN's.

Code computes, the model phrases

I gave Gemma the same 50 bookings twice, two drafts each. Once as the raw record: arrival 2017-08-23, 2 weekend nights, 5 week nights, average daily rate 160.2. Once with every fact worked out by my code: arrive Wednesday 23 August, leave Wednesday 30 August, 7 nights, $1,121.40.

Bar chart of drafts whose date, night count or amount doesn't match the booking, out of 100. Gemma working out dates and totals itself 13. Code handing Gemma the facts 0 through Ollama and 0 in the browser

From that very record, Gemma wrote to Grace: "your booking is confirmed for August 23 to August 25, 2017. That is 2 nights for a total of $320.40." It's 7 nights and $1,121.40. Another offered "cancel for free until April 0th." All of them were fluent and friendly.

A plain-Python checker reads every draft's dates, night counts and dollar amounts against the booking. If a draft is wrong, the owner sees exactly what's wrong before deciding. Three times, reading the drafts myself caught something no count showed:

  1. My checker was wrong first. Its first count said 25 bad drafts from the raw record against 1 from code's facts. I read all 26 flagged drafts, and 13 were correct drafts in formats it couldn't read yet ("2/13", "Feb 23", a total without "$"). I taught it those formats, test first, and rescored the same stored drafts: 13 against 0.
  2. The desk offered dates that had passed. The demo motel's policy (my choice; the data has none) ends free cancellation 2 days before arrival, so for almost every guest it messages, that date was gone. Code now leaves a passed date out.
  3. My own instruction leaked into a message. I had told Gemma to mention the free-cancel date "if you are given one". It wrote "You can cancel for free until [Insert Date Here]" once in 6 test drafts. Every number was right, so the checker passed it. The fix was the same principle: code decides whether that sentence exists at all and the checker now catches placeholders.

An agent that waits for a person

Each guest is one run of a small LangGraph graph, a library for agents that can stop and wait for a human. It pauses twice for the owner: Draft or Leave, then Send or Reject once the draft is checked. Leave and Reject are stored as different outcomes, because "no message needed" and "this draft was wrong" mean different things.

Gemma inside the browser

On my 8 GB Mac, Gemma through Ollama takes about 30 seconds a draft. Google also publishes a 2 GB build of the same small Gemma 4 that runs inside a web page, on the computer's graphics chip (through WebGPU). So, I gave it the same 100 drafts and the same checker.

Bar chart of median seconds per message. 3.25 in the browser on WebGPU against 31.4 through Ollama on the same 8 GB Mac

None of its 100 drafts had a wrong fact, at a median of 3.25 seconds against 31.4 (the two builds aren't identical files). Reading all found something the checker isn't built for: a few drafts leave a fact out, like the night count on a one-night stay, 6 in the browser and 5 through Ollama. None stated anything false.

So the page drafts in the browser and it asks before the 2 GB download, says what the file is and where it comes from, and shows real progress. In a browser on my Mac the download took about 3 minutes and loading about 10 seconds. Real devices changed the page twice. My iPhone 15 Pro downloaded the whole model, then Safari reloaded the page, most likely out of memory, so phones now get the recorded message instead. On my 8 GB Mac the browser froze for a while just after Gemma loaded but only on one test.

Why Does Open Innovation Matter?

The message stays with the motel. A no-show message holds a guest's name, dates and money. Gemma writes it on the owner's own computer, so none of that leaves your disk to be phrased.

There's no bill per message. The desk flagged about 7 guests a morning on the test days; with a paid writing API, that cost grows every morning. Here the model is a file on the owner's disk.

Small businesses have small data. TabPFN learns from a few thousand rows with no training run and no data scientist, which is the size of a real motel's history.

I could read and change every step that writes or checks a message. When Gemma leaked "[Insert Date Here]", I could see the exact instruction, change one line, and rerun 100 drafts.

One step isn't local yet: TabPFN scoring runs through the API of Prior Labs, so booking details (never names; this data has none) leave the computer for that one call each morning. TabPFN's v2 weights can run on computer instead and their licence allows commercial use with attribution. I checked: on my 8 GB Mac, at about 23 seconds a morning, v2 caught the same 111 of 172 on the 30 test mornings, flagging 205 guests instead of 210 (ROC-AUC 0.787 against the API's 0.792). That's the path for a paying version.

My First User

Not yet. I told a motel owner about the desk: someone I'd earlier pitched a voice agent to. He said he'd use it if he had more cancellations, and that it's handier, saves money and is easier to set up than the voice agent I'd pitched him. He doesn't have many cancellations right now, so he hasn't tried it and I can't tell you what an owner would pay. A second owner, introduced through a friend, is next. The first question I'll ask is whether a reply ever arrived early enough to resell the room.

Prize Categories

  • TabPFN. The risk step inside an agent. TabPFN scores each morning's bookings from only earlier bookings and a LangGraph run per guest acts on the score, pausing for the owner. It was tested the way it's used: on 30 later mornings it caught 111 of 172 cancellations, against about 64 at random and the 30% line held on a 2016 year it was never chosen on (92 of 134). It beat gradient boosting at every history size, a plain regression by 0.02 to 0.03 ROC-AUC and the best hand rule by 12 cancellations. Run on an 8 GB Mac with TabPFN's v2 weights, it caught the same 111, in about 23 seconds a morning.
  • Gemma. Gemma 4 E2B, the smallest of Gemma 4's five sizes, writes every message, in the visitor's browser or through Ollama on the owner's Mac. On the same bookings, wrong dates or totals fell from 13 to 0 of 100 when code handed Gemma the facts. The browser build drafted the same 100 in a median 3.25 seconds against 31.4 through Ollama on the same Mac, with no wrong fact. A checker reads every draft and the owner's own edits before send.
  • Render. Render runs the agent's front end from a render.yaml blueprint. Every visitor gets their own desk, where each guest is an agent run on the server that waits for Draft or Leave, then Send or Reject. The TabPFN scores were worked out earlier on my Mac and saved, and a test fails if the saved morning drifts from what the desk works out live.

What's real, and what isn't

  • Real: the bookings, TabPFN's scores, the reasons, Gemma's drafts and every check. The demo day is the earliest of the 30 test mornings that flagged 7 guests, the average.
  • Not measured: whether a message gets a guest to reply or a room resold. That's the part only a real motel can test and it's what I'd build next: the desk running every morning on a live booking system, with the replies counted.

來源:DEV Community · dev.to