AI-автоматизация бронирования столиков и номеров
«Забронировать столик на двоих, в пятницу вечером, желательно у окна» — это стандартный запрос, который чат-бот с кнопками обрабатывает через 5–7 шагов навигации. AI-агент разбирает намерение из первого же сообщения, проверяет доступность, уточняет детали в диалоге и подтверждает бронь — всё в одном потоке. Без форм, без переключений.
AI-агент бронирования: архитектура с инструментами
Ключевое отличие от простого чат-бота — агентный loop: LLM вызывает инструменты (проверка доступности, создание брони, отправка подтверждения) и итеративно собирает информацию через диалог.
from anthropic import Anthropic
from datetime import datetime, timedelta
import json
import re
client = Anthropic()
class BookingAgent:
BOOKING_TOOLS = [
{
"name": "check_table_availability",
"description": "Проверяет доступность столиков в ресторане",
"input_schema": {
"type": "object",
"properties": {
"date": {"type": "string", "description": "Дата в формате YYYY-MM-DD"},
"time": {"type": "string", "description": "Время в формате HH:MM"},
"guests": {"type": "integer", "description": "Количество гостей"},
"preferences": {
"type": "array",
"items": {"type": "string"},
"description": "Предпочтения: window, terrace, private, bar",
},
},
"required": ["date", "time", "guests"],
},
},
{
"name": "check_room_availability",
"description": "Проверяет доступность номеров в отеле",
"input_schema": {
"type": "object",
"properties": {
"check_in": {"type": "string", "description": "Дата заезда YYYY-MM-DD"},
"check_out": {"type": "string", "description": "Дата выезда YYYY-MM-DD"},
"guests": {"type": "integer"},
"room_type": {
"type": "string",
"enum": ["standard", "superior", "deluxe", "suite", "family"],
},
},
"required": ["check_in", "check_out", "guests"],
},
},
{
"name": "create_booking",
"description": "Создаёт подтверждённое бронирование",
"input_schema": {
"type": "object",
"properties": {
"booking_type": {"type": "string", "enum": ["table", "room"]},
"slot_id": {"type": "string", "description": "ID слота из check_availability"},
"guest_name": {"type": "string"},
"guest_phone": {"type": "string"},
"guest_email": {"type": "string"},
"special_requests": {"type": "string"},
},
"required": ["booking_type", "slot_id", "guest_name", "guest_phone"],
},
},
{
"name": "send_confirmation",
"description": "Отправляет подтверждение бронирования по SMS/email/WhatsApp",
"input_schema": {
"type": "object",
"properties": {
"booking_id": {"type": "string"},
"channel": {"type": "string", "enum": ["sms", "email", "whatsapp"]},
},
"required": ["booking_id", "channel"],
},
},
{
"name": "cancel_booking",
"description": "Отменяет бронирование по номеру или имени гостя",
"input_schema": {
"type": "object",
"properties": {
"booking_id": {"type": "string"},
"guest_phone": {"type": "string"},
},
},
},
]
def __init__(self, booking_backend, notification_service):
self.backend = booking_backend
self.notifier = notification_service
self.sessions: dict[str, dict] = {}
async def _execute_tool(self, tool_name: str, tool_input: dict) -> str:
"""Выполняет инструмент и возвращает результат"""
try:
if tool_name == "check_table_availability":
slots = await self.backend.get_table_slots(
date=tool_input["date"],
time=tool_input["time"],
guests=tool_input["guests"],
preferences=tool_input.get("preferences", []),
)
if not slots:
# Предлагаем альтернативы
alternatives = await self.backend.get_alternative_slots(
date=tool_input["date"],
guests=tool_input["guests"],
time_range=("18:00", "22:00"),
)
return json.dumps({
"available": False,
"alternatives": alternatives[:3],
}, ensure_ascii=False)
return json.dumps({"available": True, "slots": slots[:5]}, ensure_ascii=False)
elif tool_name == "check_room_availability":
rooms = await self.backend.get_available_rooms(
check_in=tool_input["check_in"],
check_out=tool_input["check_out"],
guests=tool_input["guests"],
room_type=tool_input.get("room_type"),
)
return json.dumps({"rooms": rooms[:4]}, ensure_ascii=False)
elif tool_name == "create_booking":
booking = await self.backend.create_booking(**tool_input)
return json.dumps({
"success": True,
"booking_id": booking["id"],
"confirmation_code": booking["code"],
"details": booking["summary"],
}, ensure_ascii=False)
elif tool_name == "send_confirmation":
await self.notifier.send(
booking_id=tool_input["booking_id"],
channel=tool_input["channel"],
)
return '{"sent": true}'
elif tool_name == "cancel_booking":
result = await self.backend.cancel(**tool_input)
return json.dumps(result, ensure_ascii=False)
except Exception as e:
return json.dumps({"error": str(e)})
return '{"error": "unknown tool"}'
async def process_message(self, session_id: str, message: str) -> str:
"""Агентный диалог бронирования"""
session = self.sessions.get(session_id, {"history": [], "context": {}})
system = f"""Ты — агент бронирования. Сегодня: {datetime.now().strftime('%A, %d %B %Y, %H:%M')}.
Помогаешь забронировать столик или номер. Собери необходимые данные в диалоге:
- Для столика: дата, время, кол-во гостей, предпочтения (опционально), имя и телефон
- Для номера: даты заезда/выезда, кол-во гостей, тип номера (опционально), имя и телефон
Не задавай все вопросы сразу. Выясняй по одному-двум деталям за раз.
После подтверждения гостем — создай бронирование и отправь подтверждение."""
session["history"].append({"role": "user", "content": message})
messages = session["history"].copy()
# Агентный loop
while True:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=512,
system=system,
tools=self.BOOKING_TOOLS,
messages=messages,
)
if response.stop_reason == "end_turn":
reply = response.content[0].text
session["history"].append({"role": "assistant", "content": reply})
self.sessions[session_id] = session
return reply
# Tool use
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = await self._execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
Интеграция с системами бронирования
# Пример бэкенда для ресторана через iiko API
import aiohttp
class IikoBokingBackend:
def __init__(self, api_url: str, api_key: str):
self.api_url = api_url
self.headers = {"Authorization": f"Bearer {api_key}"}
async def get_table_slots(self, date: str, time: str, guests: int, preferences: list) -> list:
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.api_url}/api/0/reserve/available_tables",
headers=self.headers,
json={
"restaurantId": self.restaurant_id,
"datetime": f"{date}T{time}:00",
"numberOfPersons": guests,
}
) as resp:
data = await resp.json()
tables = data.get("reserveTables", [])
# Фильтруем по предпочтениям
if "window" in preferences:
tables = [t for t in tables if t.get("tags", {}).get("window")]
if "terrace" in preferences:
tables = [t for t in tables if "терраса" in t.get("name", "").lower()]
return [
{
"slot_id": t["id"],
"table_name": t["name"],
"capacity": t["capacity"],
"tags": t.get("tags", {}),
}
for t in tables
]
async def create_booking(self, booking_type: str, slot_id: str,
guest_name: str, guest_phone: str,
guest_email: str = None, special_requests: str = None) -> dict:
async with aiohttp.ClientSession() as session:
async with session.post(
f"{self.api_url}/api/0/reserve/add_reserve",
headers=self.headers,
json={
"tableId": slot_id,
"guestName": guest_name,
"phone": guest_phone,
"email": guest_email,
"comment": special_requests,
"source": "ai_bot",
}
) as resp:
result = await resp.json()
return {
"id": result["reserveId"],
"code": result.get("reserveCode", result["reserveId"][:6].upper()),
"summary": f"Столик {result.get('tableName', slot_id)}, {guest_name}",
}
Практический кейс: сеть ресторанов, 4 заведения
Цифры до внедрения: 35–40% броней через телефон, 25–30 минут работы хостес в вечерний пик только на бронирование. Пиковая нагрузка в пятницу-субботу — очередь из звонков.
После внедрения:
- WhatsApp-бот через 360dialog + Telegram-бот
- Интеграция с iiko для всех 4 заведений
- SMS-подтверждения через SMS.ru
Результаты:
- 68% броней переведены в автоматический режим (бот)
- Время создания брони: 3–5 мин (телефон) → 90 сек (бот)
- Ошибки при бронировании (неправильное имя, дата): снизились на 83%
- Напоминания за 2 часа до брони → no-show снизился с 18% до 9%
Проблема, с которой столкнулись: гости часто пишут неструктурированно («в субботу вечером нас будет пятеро»). Решили промптом с извлечением сущностей перед вызовом инструментов — агент сначала нормализует дату/время, потом проверяет доступность.
Сроки
- Агент бронирования (диалог + логика): 1 неделя
- Интеграция с iiko / r-keeper / 1C: 1–2 недели
- WhatsApp / Telegram каналы: 3–5 дней
- SMS/email подтверждения + напоминания: 3–5 дней
- Полная система для сети заведений: 4–6 недель







