Spis treści
Wszystko zaczęło się od drobnej sprawy. Lead dostał propozycję spotkania o 9:15, zgodził się i usłyszał, że termin jest już zajęty: inny lead przyjął tę samą godzinę cztery minuty wcześniej. Nikt nie popełnił błędu. Nasz asystent oparty na sztucznej inteligencji (AI) zaproponował obu najwcześniejszy wolny termin, a dostać go mógł tylko jeden.
Zdarzało się to za każdym razem, gdy zapytania od leadów napływały w krótkich odstępach czasu. Kampania SMS do starej bazy leadów zaczyna rozmowy z najwyżej 10 osobami na minutę (kampanie są w planie Business), więc odpowiedzi wracają falą. Tak samo jest z formularzami, które zapełniają się po starcie reklamy albo w pracowity poniedziałkowy poranek. Każda rozmowa, która doszła do kroku rezerwacji, dostawała te same informacje: najwcześniejsze wolne terminy w przestrzeni roboczej, przy czym za zajęte uznawano tylko umówione spotkania. Dlatego wszyscy dostawali propozycję na 9:00. Pierwsze „tak” rezerwowało termin, a pozostali słyszeli: „Przepraszamy, ten termin jest już zajęty, może inny?”. Właśnie na tej dodatkowej wymianie wiadomości leady odpadają. To podwójna rezerwacja, której nie pokazuje żaden kalendarz, bo kalendarz widzi tylko to jedno spotkanie, które udało się zarezerwować.
Dlatego asystent Send4 wstępnie blokuje teraz termin, który proponuje, domyślnie na dwie godziny, a następny lead dostaje propozycję kolejnego wolnego terminu. Uruchom poniższą symulację najpierw z wyłączonymi blokadami, a potem z włączonymi.
- AgataZarezerwowano Pn 09:00
- BartoszZarezerwowano Pn 10:00
- CelinaZarezerwowano Pn 11:00
- DamianZarezerwowano Pn 12:00
- EwaZarezerwowano Pn 13:00
- FilipZarezerwowano Pn 14:00
- GabrielaZarezerwowano Pn 15:00
- HubertZarezerwowano Pn 16:00
- IwonaZarezerwowano Wt 09:00
- JakubZarezerwowano Wt 10:00
Z włączonymi blokadami 10 leadów dostało 10 różnych terminów i nikogo nie trzeba było pytać ponownie.
Dlaczego oczywiste rozwiązania nie działają
Najpierw przychodzą do głowy trzy poprawki i żadna nie działa. Proponowanie dwóch terminów zamiast jednego nic nie daje, bo drugi termin też nie jest zablokowany i koliduje dokładnie tak samo. Dlatego asystent poszedł w przeciwną stronę: proponuje jeden termin w pytaniu, na które wystarczy odpowiedzieć „tak” albo „nie”. Nie pomaga też polecenie, żeby AI zapamiętała, co zaproponowała. Zaproponowany termin istniał tylko jako tekst w wiadomości, więc nic nie mogło go zarezerwować. A rezerwowanie terminu, zanim lead się zgodzi, zapełnia kalendarz spotkaniami, na które nikt się nie umówił.
Wstępna blokada, nie rezerwacja
W kalendarzach nazywa się to wstępną rezerwacją: termin czeka, aż lead się zdecyduje, i nic nie jest jeszcze zarezerwowane. W Send4 blokada jest osobnym rekordem, a nie rodzajem spotkania, bo inaczej wszystko, co odczytuje spotkania, od kalendarza po eksporty i webhooki, musiałoby się nauczyć ją pomijać.
Blokada trwa domyślnie dwie godziny. Długość ustawiasz osobno w każdym kroku rezerwacji: od 0, co wyłącza blokady, do 24 godzin. Blokada wygasa najpóźniej w chwili, od której nie można już zarezerwować spotkania z wymaganym wyprzedzeniem: jeśli spotkanie trzeba zarezerwować co najmniej godzinę przed jego początkiem, blokada terminu o 10:00, założona o 08:55, wygasa o 09:00, bo później i tak nikt nie mógłby go zarezerwować. Kolejne wiadomości jej nie przedłużają, więc gadatliwy lead nie zatrzyma terminu w nieskończoność. Blokady obowiązują w całej przestrzeni roboczej, a nie osobno dla każdego handlowca.
Blokada jest tylko wstępna, bo nie chcemy odprawiać leada gotowego do rezerwacji tylko dlatego, że ktoś inny być może się zdecyduje. Dopóki jest wolny termin bez blokady, asystent pomija terminy zablokowane dla innych leadów. Lead, który poprosi dokładnie o zablokowany termin, nadal może go jednak zarezerwować, a dostaje go ten, kto pierwszy potwierdzi. Gdy lead, dla którego termin był zablokowany, znowu napisze, asystent powie mu, że termin jest już zajęty, i zaproponuje inny. Gdy zablokowane są wszystkie wolne terminy, asystent i tak proponuje najwcześniejszy z nich, nie zakładając kolejnej blokady. Lepiej zaproponować termin, o który ktoś jeszcze się ubiega, niż nie zaproponować żadnego.
Jak uniknąć podwójnej rezerwacji: projekt systemu
Zasadę łatwo wypowiedzieć: nie proponuj terminu, nad którym ktoś inny się zastanawia. Pierwsza decyzja brzmiała: model AI nie wybiera terminów. Termin wybiera nasz kod, a asystent dostaje polecenie, żeby zaproponował dokładnie ten termin. Jeśli lead chce inny, asystent musi wywołać narzędzie (tool call), które przenosi blokadę. Dzięki temu każdy termin, który proponuje, jest rekordem, a nie tylko słowami w wiadomości, a ostatnie słowo ma narzędzie do rezerwacji. Dalsza część jest dla programistów i dla każdego, kto projektuje system rezerwacji online. Plan napisaliśmy 25 września, a funkcję zbudowaliśmy dwa dni później. Większość opisanych niżej przypadków brzegowych (edge cases) dodały dwa przeglądy planu.
Bufor czasowy między spotkaniami
Przy 30-minutowych spotkaniach i 10-minutowym buforze drugi lead dostaje 10:00, a nie 09:30. Gdyby obaj się zgodzili, 09:30 wypadłoby w buforze pierwszego spotkania i rezerwacja zostałaby odrzucona. Dlatego od końca pierwszego spotkania do początku następnego musi upłynąć co najmniej tyle czasu, ile wynosi dłuższy z dwóch buforów:
odstęp między spotkaniami ≥ max(bufor A, bufor B)
Bierzemy większy z dwóch, bo dwa kroki rezerwacji mogą mieć różne bufory. Przesuń suwaki długości i buforów poniżej i zobacz, od kiedy może się zacząć następne spotkanie.
odstęp między spotkaniami ≥ max(bufor A, bufor B)
30 min ≥ max(10, 10) = 10 min
Następny lead dostaje propozycję na 10:00. Od końca spotkania pierwszego leada do początku następnego mija 30 min, a dłuższy z dwóch buforów wynosi 10 min. Spotkanie o 09:30 zostawiłoby odstęp 0 min, mniejszy niż 10, więc rezerwacja zostałaby odrzucona.
Podobnie jest z długością. Krok rezerwacji może oferować usługi o różnej długości. Dopóki lead nie wybierze usługi, blokujemy w kalendarzu przedział czasu odpowiadający najdłuższej z nich. Przy usłudze 30-minutowej i 90-minutowej następny lead dostaje więc 11:00. Po wyborze dopasowujemy ten przedział do czasu trwania wybranej usługi. Kod wybierający termin sprawdza kolizje ze spotkaniami i z blokadami innych leadów, a potem blokuje pierwszy termin, który przejdzie tę kontrolę:
# Wybierz jeden termin dla leada i zablokuj go.
function offerTime(lead):
for time in openTimes(after: now + minimumNotice):
if clashes(time, meetings): continue
if clashes(time, holdsOfOtherLeads): continue
hold(time, lead, until: earliest(now + holdLength, time − minimumNotice))
return time
# Każdy wolny termin jest zablokowany: zaproponuj najwcześniejszy, bez blokady.
return firstOpen(openTimes, ignoring: holds)
# Dwa spotkania kolidują, gdy odstęp od końca jednego do początku drugiego jest za mały.
function clashes(a, b):
return gap(a, b) < max(a.buffer, b.buffer)
Kiedy blokada przestaje się liczyć
Blokada liczy się tylko wtedy, gdy jej rozmowa jest w kroku rezerwacji. Rozmowa, która się kończy albo przechodzi do następnego kroku, przestaje blokować termin, więc żadna ścieżka wyjścia nie musi pamiętać o sprzątaniu po sobie. Gdy ktoś z twojego zespołu przejmie rozmowę, zablokowany dla niej termin można proponować także innym leadom, a termin, który lead odrzuci, wraca do puli bez czekania.
Zanim asystent ponownie zaproponuje zablokowany termin, sprawdza go według tych samych reguł co rezerwację. Godziny pracy mogą się zmienić, ktoś może dodać dzień bez spotkań albo ręcznie umówić spotkanie. Termin, który już nie pasuje, zostaje zwolniony, a lead dostaje propozycję innego. Spóźnione „tak” działa w drugą stronę. Jeśli lead zgodzi się po trzech godzinach, a termin nadal jest wolny, dostaje ten sam termin. Asystent nie zamienia go na nowy, bo lead zgodził się na konkretny termin, a nie na cokolwiek, co jest wolne teraz.
Testy nie mają na to wszystko wpływu. Rozmowa testowa w kreatorze scenariuszy nigdy nie zajmuje terminu prawdziwego leada, a testowa rezerwacja nigdy nie zabiera terminu zablokowanego dla prawdziwego leada.
Wyścig (race condition) w naszym systemie rezerwacji
Ten problem znaleźliśmy po drodze i to podręcznikowy przypadek podwójnej rezerwacji: dwie rezerwacje tego samego pustego terminu w tej samej chwili mogły się udać obie. Kontrola przed rezerwacją zakładała zamek (lock) na spotkania zapisane już w tym terminie. Pusty termin nie ma żadnych spotkań, więc nie było na czym go założyć. Obie rezerwacje sprawdziły termin, obie nic nie znalazły i obie zapisały spotkanie. Nasza kolejka zadań AI miała dwa procesy robocze (workers), więc dwa „tak” naprawdę mogły zostać obsłużone w tej samej chwili, a tabela spotkań nie miała ograniczenia (constraint), które by to wychwyciło.
Zamknęliśmy tę lukę, budując blokady. Wybór terminu i rezerwacja korzystają teraz z tego samego zamka, jednego na konto, więc druga rezerwacja czeka, widzi pierwszą i lead dostaje propozycję innego terminu. Jeśli zamka nie da się założyć, rezerwacja przechodzi tak jak wcześniej, bo „tak” klienta nie może przepaść przez naszą pamięć podręczną (cache). Przejdź przez to poniżej krok po kroku, bez zamka i z zamkiem.
- 09:00 Agata
Krok 7 z 7
Z zamkiem rezerwacja Bartosza poczekała, znalazła spotkanie Agaty i została odrzucona. Asystent zaproponował Bartoszowi 10:00, najbliższy wolny termin.
Co widzisz w Send4
Zablokowane terminy widać w kalendarzu Send4 jako zakreskowane bloki „Offered to …” obok już umówionych spotkań. Nie są spotkaniami i nie wliczają się do ich liczby, a kliknięcie bloku otwiera rozmowę z tym leadem.
Długość blokady ustawiasz w każdym kroku rezerwacji. Jak to zrobić, opisujemy w centrum pomocy: jak działa wstępna blokada terminów.
Czego blokada nie robi
Blokada jest tylko wstępna, więc dwa leady nadal mogą sobie wejść w drogę, gdy jeden poprosi o termin zablokowany dla drugiego. Nie daje gwarancji. Zna też tylko kalendarz Send4. Czy asystent sprawdza własny kalendarz handlowca, zanim zaproponuje termin?
Jeszcze nie. Rezerwacje trafiają do kalendarza Send4.
Nie ma też osobnej dostępności dla każdego handlowca: każdy krok rezerwacji proponuje terminy wolne w jednym wspólnym kalendarzu Send4. A duża fala zapytań może zapełnić blokadami cały poranek, więc późniejsze leady dostają propozycje na popołudnie albo na następny dzień. Jeśli tak się dzieje, skróć blokadę w tym kroku rezerwacji.
Jeśli umawiasz rozmowy z leadami, tak właśnie działa w Send4 umawianie spotkań handlowych przez SMS: asystent zadaje twoje pytania, blokuje termin i umawia rozmowę w tym samym wątku.
Pytania
Co to jest podwójna rezerwacja?
Podwójna rezerwacja (double booking) to dwa spotkania w tym samym terminie albo ten sam termin obiecany dwóm osobom. Gdy asystent AI odpowiada wielu leadom naraz, częściej zdarza się to drugie, bo każdy dostaje propozycję najwcześniejszego wolnego terminu.
Jak uniknąć podwójnych rezerwacji?
Wstępnie blokuj termin, który proponujesz, zostawiaj bufor między spotkaniami i sprawdzaj termin jeszcze raz, gdy klient się zgodzi. Asystent AI Send4 domyślnie blokuje każdy zaproponowany termin na dwie godziny, więc następny lead dostaje propozycję kolejnego wolnego terminu.
Co to jest wstępna rezerwacja?
Termin zatrzymany dla kogoś na czas decyzji, ale jeszcze niezarezerwowany. W Send4 to wstępna blokada. Dopóki jest wolny termin bez blokady, asystent pomija terminy zablokowane dla innych leadów, a gdy zablokowane są wszystkie wolne terminy, proponuje najwcześniejszy z nich, nie zakładając kolejnej blokady. Lead, który poprosi dokładnie o zablokowany termin, nadal może go zarezerwować. Dostaje go ten, kto pierwszy potwierdzi.
Do czego służy bufor czasowy między spotkaniami?
Zostawia trochę czasu po jednym spotkaniu, zanim zacznie się następne. W Send4 bufor wynosi domyślnie 10 minut. Między dwoma zablokowanymi terminami, od końca pierwszego spotkania do początku następnego, mija co najmniej tyle czasu, ile wynosi dłuższy z ich dwóch buforów.
Czy AI może umawiać spotkania?
Tak. Asystent AI Send4 umawia rozmowy SMS-em, a w planie Business także e-mailem, zapisuje je w kalendarzu Send4 i wstępnie blokuje każdy proponowany termin, dopóki lead się nie zdecyduje.
Czym asystent AI różni się od systemu rezerwacji online?
W systemie rezerwacji online klient sam wybiera wolny termin w kalendarzu. Asystent AI Send4 odpowiada na zapytanie SMS-em, a w planie Business także e-mailem, zadaje twoje pytania i proponuje jeden termin. Ten termin jest wstępnie blokowany, dopóki lead się nie zdecyduje, a umówiona rozmowa trafia do kalendarza Send4.
Czy Send4 działa z Google Meet lub Microsoft Teams?
Jeszcze nie. Rozmowa jest umawiana w Send4, a link wysyłasz samodzielnie.
Czy przed zaproponowaniem terminu asystent sprawdza mój własny kalendarz?
Jeszcze nie. Rezerwacje trafiają do kalendarza Send4.
Czy zablokowane terminy trafiają do mojego kalendarza?
Nie. Wstępna blokada istnieje tylko w Send4. W kalendarzu Send4 widać ją jako zakreskowany blok i nie jest to spotkanie.
Viktoras Lava
CTO
Viktoras rozwija produkt Send4. We wrześniu 2026 roku wdrożył wstępną blokadę terminów.