Разработка AI-системы автоматизации бронирования столиков и номеров

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
Разработка AI-системы автоматизации бронирования столиков и номеров
Средняя
~1-2 недели
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

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 недель