Розробка AI-системи інтелектуального консьєржа для готелів

Гості часто ставлять запитання в неробочий час, і ресепшн не завжди може відповісти миттєво. Ми розробляємо AI-консьєржа, який розуміє контекст запиту та дає точні відповіді, враховуючи розташування готелю, час і вподобання гостя. Наша команда реалізує проєкт під ключ — від аудиту до впровадження та подальшої підтримки, забезпечуючи стабільну роботу системи.
Часті запитання

AI-консьєрж для готелю: система асистента гостей

Гість пише в WhatsApp о 23:40: «Де найближчий ресторан з видом на море, відкритий зараз?». Ресепшн зайнятий пізніми заїздами. Стандартний чат-бот з кнопками поверне меню навігації. Ми пропонуємо AI-консьєржа, який відповість конкретно, з урахуванням місцезнаходження готелю, поточного часу та вподобань гостя з профілю. Це рішення скорочує навантаження на front desk до 74% та підвищує NPS гостей на 12 пунктів.

За даними Hospitality Tech Report, впровадження AI-консьєржа знижує навантаження на ресепшн на 70–80%.

Як працює AI-консьєрж?

Система об'єднує кілька джерел даних: база знань готелю (послуги, правила, інфраструктура), зовнішні API (погода, ресторани, визначні пам'ятки через Google Places), PMS-система (дані про бронювання та гостя) та історія діалогів. Кожен запит збагачується контекстом — номер, дати заїзду/виїзду, мова гостя, вподобання. Використовується RAG-пайплайн: вилучення релевантних фрагментів із Chroma (колекція з embeddings від text-embedding-3-small) та генерація відповіді через Claude або GPT. Для критичних запитів (скарги, health issues) налаштовано fallback на живого консьєржа через webhook у PMS.

Retrieval-Augmented Generation дозволяє динамічно підключати зовнішні дані без перенавчання моделі.

Чому RAG, а не fine-tuning?

Fine-tuning моделі на Q&A готелю дає закріплені відповіді, але не дозволяє динамічно підключати зовнішні дані (погода, відгуки, поточний розклад). RAG з Chroma та 1536-dim embeddings обробляє 180+ запитань за <200 мс, підтримує оновлення контенту без перенавчання. Для бутік-готелю (80 номерів) витрати на інференс — $0.002 за запит, що в 10 разів дешевше за fine-tuned GPT-3.5.

Архітектура системи

Код нижче реалізує ядро асистента з інструментами вилучення інформації, отримання погоди, створення заявок. Використовується LLM з tool use — agentic loop, який сам вирішує, які інструменти викликати.

from anthropic import Anthropic
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import httpx
from datetime import datetime
import json

client = Anthropic()
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

class HotelConciergeAssistant:
    def __init__(self, hotel_id: str, hotel_config: dict):
        self.hotel_id = hotel_id
        self.config = hotel_config  # координати, мова, категорія готелю
        self.vectorstore = Chroma(
            collection_name=f"hotel_{hotel_id}_kb",
            embedding_function=embeddings,
        )
        self.conversations: dict[str, list] = {}
        self.tools = self._define_tools()

    def _define_tools(self) -> list[dict]:
        return [
            {
                "name": "search_hotel_knowledge",
                "description": "Пошук інформації про services, amenities, rules, events готелю",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "query": {"type": "string", "description": "Пошуковий запит"},
                        "category": {
                            "type": "string",
                            "enum": ["services", "dining", "spa", "transport", "policies", "events", "local"],
                        }
                    },
                    "required": ["query"],
                },
            },
            {
                "name": "get_local_recommendations",
                "description": "Рекомендації ресторанів, визначних пам'яток, магазинів в околицях",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "category": {
                            "type": "string",
                            "enum": ["restaurant", "cafe", "bar", "attraction", "museum", "shopping", "beach"],
                        },
                        "open_now": {"type": "boolean"},
                        "radius_meters": {"type": "integer", "default": 1000},
                    },
                    "required": ["category"],
                },
            },
            {
                "name": "get_weather",
                "description": "Поточна погода та прогноз",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "days_ahead": {"type": "integer", "default": 0},
                    },
                },
            },
            {
                "name": "create_service_request",
                "description": "Створює заявку на послугу: прибирання, доставка їжі, трансфер, будильник",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "service_type": {
                            "type": "string",
                            "enum": ["housekeeping", "room_service", "taxi", "wake_up_call", "luggage", "maintenance"],
                        },
                        "details": {"type": "string"},
                        "time": {"type": "string", "description": "ISO datetime або 'now'"},
                    },
                    "required": ["service_type"],
                },
            },
        ]

    async def _execute_tool(self, tool_name: str, tool_input: dict, guest_id: str) -> str:
        if tool_name == "search_hotel_knowledge":
            results = self.vectorstore.similarity_search(
                tool_input["query"],
                k=4,
                filter={"category": tool_input["category"]} if tool_input.get("category") else None
            )
            return "\n\n".join([doc.page_content for doc in results]) or "Інформація не знайдена"
        elif tool_name == "get_local_recommendations":
            # Інтеграція з Google Places API
            async with httpx.AsyncClient() as http:
                resp = await http.get(
                    "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
                    params={
                        "location": f"{self.config['lat']},{self.config['lng']}",
                        "radius": tool_input.get("radius_meters", 1000),
                        "type": tool_input["category"],
                        "opennow": tool_input.get("open_now", False),
                        "language": "uk",
                        "key": self.config["google_places_key"],
                    }
                )
                places = resp.json().get("results", [])[:5]
                return json.dumps([{
                    "name": p["name"],
                    "rating": p.get("rating"),
                    "address": p.get("vicinity"),
                    "open_now": p.get("opening_hours", {}).get("open_now"),
                    "price_level": p.get("price_level"),
                } for p in places], ensure_ascii=False)
        elif tool_name == "get_weather":
            async with httpx.AsyncClient() as http:
                resp = await http.get(
                    "https://api.openweathermap.org/data/2.5/forecast",
                    params={
                        "lat": self.config["lat"],
                        "lon": self.config["lng"],
                        "appid": self.config["openweather_key"],
                        "units": "metric",
                        "lang": "uk",
                        "cnt": 8 * (tool_input.get("days_ahead", 0) + 1),
                    }
                )
                data = resp.json()
                forecasts = data["list"][:3]
                return json.dumps([{
                    "time": f["dt_txt"],
                    "temp": f["main"]["temp"],
                    "description": f["weather"][0]["description"],
                    "wind": f["wind"]["speed"],
                } for f in forecasts], ensure_ascii=False)
        elif tool_name == "create_service_request":
            # Відправляємо в PMS через webhook
            request_id = await self._send_pms_request(guest_id, tool_input)
            return f"Заявка #{request_id} прийнята. Очікуваний час: 15–20 хвилин."
        return "Інструмент недоступний"

    async def _send_pms_request(self, guest_id: str, service: dict) -> str:
        """Відправляє заявку в Property Management System"""
        async with httpx.AsyncClient() as http:
            resp = await http.post(
                f"{self.config['pms_url']}/api/service-requests",
                headers={"Authorization": f"Bearer {self.config['pms_token']}"},
                json={
                    "guest_id": guest_id,
                    "hotel_id": self.hotel_id,
                    "service_type": service["service_type"],
                    "details": service.get("details", ""),
                    "requested_time": service.get("time", "now"),
                    "source": "ai_concierge",
                }
            )
            return resp.json().get("request_id", "N/A")

    async def chat(self, guest_id: str, message: str, guest_profile: dict) -> str:
        """Основний діалог з гостем"""
        history = self.conversations.get(guest_id, [])
        # Системний промпт з контекстом гостя
        system = f"""Ти — віртуальний консьєрж готелю «{self.config['hotel_name']}» (5 зірок, {self.config['city']}).
Гість: {guest_profile.get('name', 'Шановний гість')}
Номер: {guest_profile.get('room', '?')}
Заїзд: {guest_profile.get('check_in', '?')} — Виїзд: {guest_profile.get('check_out', '?')}
Мова: {guest_profile.get('language', 'uk')}
Поточний час: {datetime.now().strftime('%H:%M')}
Будь уважним, конкретним та проактивним. Пропонуй релевантні послуги готелю, коли доречно. Якщо гість просить щось організувати — використовуй інструменти для створення заявки."""
        history.append({"role": "user", "content": message})
        # Agentic loop з інструментами
        messages = history.copy()
        while True:
            response = client.messages.create(
                model="claude-sonnet-4-5",
                max_tokens=1024,
                system=system,
                tools=self.tools,
                messages=messages,
            )
            if response.stop_reason == "end_turn":
                assistant_text = response.content[0].text
                history.append({"role": "assistant", "content": assistant_text})
                self.conversations[guest_id] = history[-20:]  # зберігаємо 20 повідомлень
                return assistant_text
            # Обробляємо виклики інструментів
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = await self._execute_tool(block.name, block.input, guest_id)
                    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})

Як впровадити AI-консьєржа: 5 кроків

  1. Аудит інфраструктури: оцінка поточного PMS, каналів зв'язку, навантажень на ресепшн.
  2. Збір бази знань: підготовка 100–200 Q&A, описів послуг, правил, меню.
  3. Налаштування RAG-пайплайну: індексація в Chroma, створення embeddings.
  4. Інтеграція каналів: підключення WhatsApp, Telegram, веб-чату через API.
  5. Тестування та запуск: A/B-тестування на малій групі гостей, потім повний rollout.
Технічні деталі для DevOps Розгортання: Kubernetes (EKS/GKE) з Triton Inference Server для LLM, vLLM для оптимізації інференсу. Моніторинг: Weights & Biases для метрик accuracy, latency p99, токенів. CI/CD через GitLab CI з автоматичним оновленням бази знань.

Практичний кейс: наш клієнт — boutique-готель, 80 номерів

Проблема: ресепшн не справлявся з потоком однотипних запитань у месенджерах (WhatsApp + Telegram). 60–70% звернень — «о котрій сніданок», «чи є парковка», «як дістатися до центру». Співробітники витрачали до 12 хвилин на відповідь у непіковий час.

Реалізація:

  • База знань готелю: 180 Q&A, опис послуг, меню ресторану — проіндексовано в Chroma з embeddings 1536-dim.
  • Інтеграція з Google Places API для локальних рекомендацій.
  • Webhook у PMS (Opera) для створення заявок на послуги.
  • WhatsApp Business API через 360dialog.
  • Fallback на живого консьєржа при requires_human: true.

Результати за 3 місяці:

Метрика До Після
Частка автоматизованих звернень 0% 74%
Середній час відповіді 8–12 хв 45 сек
Навантаження на нічну зміну 2 год/ніч 0 год
Оцінка якості відповідей (гостями) — 4.6/5.0
NPS тих, хто взаємодіяв з AI база +12 пунктів vs ті, хто не взаємодіяв

Замовте демо-версію, щоб оцінити ефект на вашому готелі.

Що входить у роботу: deliverables

  • Повна документація архітектури та API (OpenAPI specs).
  • Вихідний код модулів (LLM, RAG, інтеграції).
  • Розгортання на bare-metal або в Kubernetes (Triton Inference Server, vLLM).
  • Моніторинг latency p99, accuracy, safety через Weights & Biases.
  • Навчання персоналу (адміністратори, оператори).
  • Технічна підтримка 30 днів після запуску.

Типові помилки при впровадженні

  • Hallucination на рідкісних питаннях: вирішується додаванням confidence threshold < 0.7 → fallback.
  • Prompt injection: гість може спробувати змінити системний промпт. Захист — строга ізоляція інструкцій та валідація input-фільтрів.
  • Overloading LLM: без rate limiting та кешування частих запитів («сніданок») вартість інференсу зростає. Наше рішення — кеш з TTL 5 хв на identical queries.

Порівняння AI-консьєржа vs звичайного чат-бота

Параметр Чат-бот з кнопками AI-консьєрж (RAG + LLM)
Розуміння природної мови Обмежене (intent) Повне (NLU)
Персоналізація Ні Так (профіль, історія)
Інтеграція з зовнішніми даними Ні Погода, Places, PMS
Створення заявок Тільки через UI Голосом/текстом
Вартість за 1К запитів $0.02 (сервер) ~$0.20 (LLM)
Час налаштування під готель 1–2 дні 1–2 тижні

Терміни орієнтовно

  • Базовий асистент з FAQ + Chroma: від 1 тижня.
  • WhatsApp/Telegram інтеграція: від 3 до 5 днів.
  • Google Places + погода: від 2 до 3 днів.
  • PMS-інтеграція (Opera, Fidelio, Apaleo): від 1 до 2 тижнів.
  • Повна система з аналітикою: від 4 до 6 тижнів.

Вартість розраховується індивідуально під конфігурацію готелю. Оцінимо проєкт за 2 робочі дні — зв'яжіться з нами для аудиту поточної IT-інфраструктури. Досвід нашої команди — 5+ років у hospitality-автоматизації та 50+ впроваджених AI-проєктів. Гарантуємо NDA для всіх даних гостей.

Напрямки AI-розробки

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    816
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142