# AviaSeek - полное описание для ИИ-агентов (llms-full.txt) > AviaSeek - глобальный метапоисковик авиабилетов для самостоятельных путешественников. Агрегирует цены из авиакомпаний и агрегаторов, сравнивает варианты и показывает оптимальные перелёты. > Сайт реализует WebMCP (Model Context Protocol for the Web): в браузерах с поддержкой (Chrome Origin Trial, HTTPS) инструменты доступны через `document.modelContext`. ## 0.0. Данные каталога (live) 318 городов, 340 аэропортов, 20 авиакомпаний, 312 направлений в справочнике, 15 статей помощи. - **Версия контракта `searchFlights`:** `1.1.0` - **Markdown-версии страниц:** к любой странице добавляется `.md` (`/index.md`, `/cities.md`, `/cities/moskva-mow.md`, `/routes/moskva-mow/sankt-peterburg-led.md`) - те же данные без HTML-разметки. - **Отдача ботам:** ИИ-краулеры получают серверный HTML с заголовками, текстом и JSON-LD (WebSite/WebPage/City/Airport/Airline/FAQPage/BreadcrumbList/ItemList). ## 0. Серверный MCP-эндпоинт (для облачных агентов) AviaSeek предоставляет **настоящий серверный MCP-сервер** по протоколу Model Context Protocol (Streamable HTTP) - доступен облачным агентам (DeepSeek, Claude, ChatGPT, Cursor) напрямую, без браузера. - **URL:** `https://aviaseek.ru/mcp` - **Транспорт:** Streamable HTTP (JSON-RPC 2.0), `POST` с заголовком `Accept: application/json, text/event-stream`. - **Методы:** `initialize`, `tools/list`, `tools/call`. - **Инструмент:** `searchFlights` (схема - ниже, раздел 1). - **Автодискавери:** `https://aviaseek.ru/.well-known/mcp.json`. - **Доступ:** публичный, лимит до 30 запросов/мин с IP (`MCP_RATE_LIMIT_PER_MIN`). Пример `tools/call`: ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"searchFlights","arguments":{"origin":"MOW","destination":"LED","departure_date":"2026-09-05"}}} ``` ## 0.5. Навык Алисы (голосовой) AviaSeek доступен как навык для Яндекс Алисы: «Алиса, спроси у АвиаСика, найди билеты из Москвы в Питер на завтра». - **Webhook:** `https://aviaseek.ru/alice` (POST, протокол Яндекс Диалогов; лимит ответа 4,5 сек). - **Поиск асинхронный (split-turn):** на «найди билеты из X в Y на дату» запускается поиск у агрегатора; продолжение - по фразе/кнопке «проверить результат» (состояние в `application_state`). - **Выдача:** колонка без экрана - топ-3 голосом (цена, авиакомпания, время); экранные устройства - карточка `ItemsList` (до 5 вариантов), нажатие на вариант открывает страницу с подробной информацией по билету. - **Распознавание городов/дат:** сущности Яндекса (`YANDEX.GEO`, `YANDEX.DATETIME`) + собственный парсер и каталог городов AviaSeek. ## 1. WebMCP-инструменты ### searchFlights Поиск авиабилетов по маршруту и датам. Возвращает список рейсов с ценами, авиакомпаниями, временем в пути, условиями багажа и ссылкой для покупки. **Описание для агентов:** поиск перелётов Москва→Санкт-Петербург 05.09, туда-обратно, 2 взрослых, эконом. Вернёт варианты с ценами, авиакомпаниями и ссылками на покупку через AviaSeek. ## 1.1. Контракт вывода и фильтрация (searchFlights, v1.1) ### Параметры выдачи (кроме обязательных origin/destination/departure_date) | Параметр | Значения | По умолчанию | Что делает | |---|---|---|---| | `limit` | 1..100 | 20 | сколько вариантов вернуть (после фильтрации и сортировки) | | `offset` | >= 0 | 0 | следующие варианты (пагинация) | | `sort` | `price_asc`, `duration_asc`, `departure_asc`, `arrival_asc`, `transfers_asc`, `recommended` | `price_asc` | порядок выдачи | | `filters.direct_only` | boolean | false | только прямые рейсы | | `filters.max_transfers` | 0..3 | - | не более N пересадок | | `filters.transfers` | `[0]`, `[0,1]` | - | точные наборы пересадок (как галочки на сайте) | | `filters.max_price` | число (RUB) | - | до N ₽ | | `filters.max_duration_min` | минуты | - | максимальная длительность | | `filters.airlines` | коды, напр. `["DP"]` | - | хотя бы одна из авиакомпаний в билете | | `filters.with_baggage` / `with_handbag` / `with_change` / `with_return` | boolean | false | требования по багажу / обмену / возврату | | `filters.departure_periods` / `arrival_periods` | `early_morning` 00-06, `morning` 06-12, `afternoon` 12-18, `evening` 18-24 | - | периоды дня (локальное время) | Правила: 1. Порядок операций: **фильтры → сортировка → limit/offset**. Поэтому «только прямые» всегда находятся, даже если они не входят в первые 20 по цене. 2. Между разными фильтрами - логика И; внутри массива (авиакомпании, периоды) - ИЛИ. 3. Билет без цены отбрасывается при `max_price`, билет без времени - при фильтре по периодам дня. 4. Ответ: `total` (найдено всего), `total_after_filters`, `results_returned`, `has_more`, `next_offset`, `hint`, `view_all_url`, `cheapest`, `results[]`. 5. Пустая выдача: `total_after_filters: 0` + `hint` (что ослабить). Сообщите `hint` пользователю и предложите изменить условие. 6. Фильтры не запускают новый поиск: передайте тот же `search_id`, чтобы сузить/пролистать уже найденное. ### Как показывать результат пользователю (обязательно) Каждый вариант содержит готовые поля, чтобы агент не собирал строку из 15 полей JSON: - `display_markdown` - готовая markdown-строка: `**Победа · DP210** · MOW → LED · 07:30 → 09:05 · прямой · 1 ч 35 мин · **3 179 ₽** · [Купить за 3 179 ₽](https://aviaseek.ru/r/...)`; - `title` - «Победа · DP210 · MOW → LED · 07:30 → 09:05 · прямой»; - `summary` - «3 179 ₽ · 1 ч 35 мин · прямой · ручная кладь 10 кг, багаж 23 кг»; - `link_text` - «Купить за 3 179 ₽»; - `booking_url` - ссылка покупки (только этот домен, ~15 минут жизни). Требования к ответу агента: - 3-5 вариантов отдельными пунктами, с временем, пересадками, длительностью, ценой и кликабельной ссылкой; - не отвечать одной ценой без ссылки; - не выдумывать варианты и цены, которых нет в `results`; - при `is_complete: false` предупредить, что цены предварительные, и повторить вызов с тем же `search_id`. ### Пример Запрос: «найди прямые билеты Москва - Питер 26 сентября до 8000 ₽». Аргументы: `{"origin":"MOW","destination":"LED","departure_date":"2026-09-26","filters":{"direct_only":true,"max_price":8000},"sort":"price_asc","limit":20}`. Ответ: `total_after_filters` (сколько прямых подходит), `results[]` по 20 на страницу, `has_more`/`next_offset` для продолжения, `view_all_url` для покупки на сайте. ### searchFlights (контракт v1.1.0): inputSchema (JSON Schema) ```json { "type": "object", "properties": { "origin": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "IATA-код города или аэропорта вылета. Примеры: MOW, LED, PAR, STR." }, "destination": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "IATA-код города или аэропорта прилёта. Примеры: LED, SVX, BCN." }, "departure_date": { "type": "string", "format": "date", "description": "Дата вылета в формате YYYY-MM-DD." }, "return_date": { "type": "string", "format": "date", "description": "Дата возврата YYYY-MM-DD (опционально). Без неё - поиск в одну сторону." }, "adults": { "type": "integer", "minimum": 1, "maximum": 9, "default": 1, "description": "Количество взрослых пассажиров." }, "children": { "type": "integer", "minimum": 0, "maximum": 8, "default": 0, "description": "Количество детей (2-11 лет)." }, "infants": { "type": "integer", "minimum": 0, "maximum": 8, "default": 0, "description": "Количество младенцев (до 2 лет)." }, "cabin": { "type": "string", "enum": [ "economy", "business", "first" ], "default": "economy", "description": "Класс обслуживания. Доступны только economy/business/first." }, "search_id": { "type": "string", "description": "Опционально. Передайте search_id из предыдущего ответа (status=partial/pending, is_complete=false), чтобы дозагрузить тот же поиск или применить другие фильтры без нового старта." }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20, "description": "Сколько вариантов вернуть ПОСЛЕ фильтрации и сортировки. По умолчанию 20, максимум 100. Не повышайте без явной просьбы пользователя." }, "offset": { "type": "integer", "minimum": 0, "default": 0, "description": "Смещение для пагинации: 20 - вернуть следующие варианты после первых 20." }, "sort": { "type": "string", "enum": [ "price_asc", "duration_asc", "departure_asc", "arrival_asc", "transfers_asc", "recommended" ], "default": "price_asc", "description": "Сортировка: price_asc - дешёвые сначала (по умолчанию); duration_asc - короче в пути; departure_asc - ранний вылет; arrival_asc - раннее прибытие; transfers_asc - меньше пересадок; recommended - порядок сайта AviaSeek." }, "filters": { "type": "object", "additionalProperties": false, "description": "Фильтры выдачи (как в панели фильтров на сайте AviaSeek). Между разными фильтрами - логика И, внутри массива - ИЛИ. Применяются ДО сортировки и limit. Пользователь просит «только прямые» - передавайте direct_only; «до 10 000 ₽» - max_price; «утром» - departure_periods; «не больше одной пересадки» - max_transfers.", "properties": { "direct_only": { "type": "boolean", "default": false, "description": "true - только прямые рейсы (без пересадок). Эквивалент max_transfers=0." }, "max_transfers": { "type": "integer", "minimum": 0, "maximum": 3, "description": "Не более N пересадок: 0 - только прямые, 1 - прямые и с одной пересадкой." }, "transfers": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 3 }, "description": "Точные количества пересадок, как в фильтре на сайте: [0] - только прямые, [0,1] - прямые и с одной пересадкой." }, "max_price": { "type": "number", "minimum": 0, "description": "Максимальная цена билета в RUB." }, "max_duration_min": { "type": "integer", "minimum": 0, "description": "Максимальная длительность перелёта в минутах." }, "airlines": { "type": "array", "items": { "type": "string", "pattern": "^[A-Z0-9]{2}$" }, "description": "IATA-коды авиакомпаний (напр. DP, SU). Билет подходит, если хотя бы одна из них есть в маршруте." }, "with_baggage": { "type": "boolean", "description": "Только билеты с включённым багажом." }, "with_handbag": { "type": "boolean", "description": "Только билеты с включённой ручной кладью." }, "with_change": { "type": "boolean", "description": "Только билеты с возможностью обмена." }, "with_return": { "type": "boolean", "description": "Только билеты с возможностью возврата." }, "departure_periods": { "type": "array", "items": { "type": "string", "enum": [ "early_morning", "morning", "afternoon", "evening" ] }, "description": "Периоды дня вылета: early_morning 00:00-06:00, morning 06:00-12:00, afternoon 12:00-18:00, evening 18:00-24:00 (локальное время)." }, "arrival_periods": { "type": "array", "items": { "type": "string", "enum": [ "early_morning", "morning", "afternoon", "evening" ] }, "description": "Периоды дня прилёта (те же границы, что и для вылета)." } } } }, "required": [ "origin", "destination", "departure_date" ] } ``` ### searchFlights (контракт v1.1.0): outputSchema (JSON Schema) ```json { "type": "object", "description": "Результат поиска авиабилетов. Покажите пользователю 3-5 вариантов списком: в каждом - авиакомпания и номер рейса, время вылета и прилёта, число пересадок, длительность, цена с валютой и кликабельная ссылка на покупку. Не отвечайте одной ценой без ссылки. Не выдумывайте варианты, которых нет в results.", "properties": { "status": { "type": "string", "enum": [ "success", "partial", "pending", "error" ], "description": "success - результат полный; partial - найдены не все, повторите вызов с тем же search_id; pending - результатов ещё нет, повторите через несколько секунд; error - ошибка выполнения." }, "search_id": { "type": "string", "description": "Идентификатор поисковой сессии. Передайте его в следующий вызов, чтобы дозагрузить результаты или применить другие фильтры без нового поиска." }, "is_complete": { "type": "boolean", "description": "true - финальный полный результат; false - стоит повторить вызов с тем же search_id." }, "direction": { "type": "object", "properties": { "origin": { "type": "string" }, "destination": { "type": "string" }, "date": { "type": "string" }, "return_date": { "type": [ "string", "null" ] } } }, "passengers": { "type": "object", "properties": { "adults": { "type": "integer" }, "children": { "type": "integer" }, "infants": { "type": "integer" } } }, "cabin": { "type": "string" }, "limit": { "type": "integer", "description": "Размер страницы, применённый к выдаче." }, "offset": { "type": "integer", "description": "Применённое смещение." }, "sort": { "type": "string", "description": "Применённая сортировка." }, "filters_applied": { "type": [ "object", "null" ], "description": "Нормализованные фильтры, реально применённые к выдаче (эхо запроса)." }, "total": { "type": "integer", "description": "Сколько всего билетов найдено (до фильтрации)." }, "total_after_filters": { "type": "integer", "description": "Сколько билетов осталось после фильтрации." }, "results_returned": { "type": "integer", "description": "Сколько вариантов в этом ответе (= results.length)." }, "has_more": { "type": "boolean", "description": "true - есть ещё варианты после этой страницы (запросите offset = next_offset)." }, "next_offset": { "type": [ "integer", "null" ], "description": "Значение offset для следующей страницы или null, если варианты закончились." }, "hint": { "type": [ "string", "null" ], "description": "Подсказка при пустой выдаче: что ослабить. Если hint не null - передайте его пользователю и предложите изменить условие, а не отвечайте «билетов нет»." }, "view_all_url": { "type": "string", "description": "Ссылка на страницу результатов на сайте AviaSeek. Покажите её пользователю, чтобы он мог посмотреть все варианты и купить." }, "cheapest": { "type": [ "object", "null" ], "description": "Самый дешёвый вариант среди отфильтрованных (для краткой сводки).", "properties": { "price": { "type": "number" }, "currency": { "type": "string" }, "airline_name": { "type": "string" }, "flight_number": { "type": "string" } } }, "error": { "type": [ "string", "null" ], "description": "Текст ошибки, если status=error." }, "results": { "type": "array", "description": "Варианты перелёта после фильтрации, сортировки и обрезки. Показывайте каждый вариант отдельным пунктом, используя display_markdown - в нём уже есть готовая строка с ценой и ссылкой на покупку.", "items": { "type": "object", "properties": { "id": { "type": "string" }, "airline_code": { "type": "string" }, "airline_name": { "type": "string" }, "flight_number": { "type": "string" }, "origin": { "type": "string" }, "destination": { "type": "string" }, "departure_at": { "type": [ "string", "null" ] }, "arrival_at": { "type": [ "string", "null" ] }, "price": { "type": [ "number", "null" ], "description": "Цена в валюте currency. Передавайте как есть, не пересчитывайте и не округляйте." }, "currency": { "type": "string" }, "transfers": { "type": "integer" }, "duration_minutes": { "type": [ "number", "null" ] }, "is_round_trip": { "type": "boolean" }, "return_departure_at": { "type": [ "string", "null" ] }, "return_arrival_at": { "type": [ "string", "null" ] }, "baggage": { "type": "object", "properties": { "handbag": { "type": [ "integer", "null" ] }, "baggage": { "type": [ "integer", "null" ] }, "change": { "type": [ "boolean", "null" ] }, "return": { "type": [ "boolean", "null" ] } } }, "booking_url": { "type": "string", "description": "Ссылка для покупки через AviaSeek. ОБЯЗАТЕЛЬНО покажите её пользователю кликабельной ссылкой такого вида: [Купить за 3179 ₽](booking_url). Ссылка действует ограниченное время (~15 минут) - открывать сразу." }, "title": { "type": "string", "description": "Готовый заголовок варианта: авиакомпания, номер рейса, маршрут, время, пересадки." }, "summary": { "type": "string", "description": "Готовая краткая строка: цена, длительность, пересадки, багаж." }, "link_text": { "type": "string", "description": "Готовый текст ссылки покупки, напр. «Купить за 3179 ₽»." }, "display_markdown": { "type": "string", "description": "Готовая markdown-строка варианта с ценой и ссылкой на покупку. Используйте её как есть." } }, "required": [ "id", "airline_name", "flight_number", "origin", "destination", "price", "currency", "transfers", "booking_url", "title", "summary", "link_text", "display_markdown" ] } } }, "required": [ "status", "search_id", "is_complete", "direction", "passengers", "cabin", "limit", "offset", "sort", "total", "total_after_filters", "results_returned", "has_more", "results" ] } ``` > **Примечание про is_complete=false:** поиск у агрегатора занимает время. Если `status=partial` или `pending` - повторите вызов с тем же `search_id` через несколько секунд для дозагрузки. > **booking_url** - ссылка на страницу покупки; действительна ограниченное время (~15 минут). Открывать сразу, не ждать. #### Пример вызова ```js await document.modelContext.executeTool('searchFlights', { origin: 'MOW', destination: 'LED', departure_date: '2026-09-05', adults: 1, cabin: 'economy' }); ``` ## 1.9. Поиск без MCP: URL-поиск, HTML-форма и HTTP API Если у агента нет MCP-клиента (например, браузерный агент или чат без поддержки MCP), поиск выполняется через обычные ссылки - JavaScript не требуется: 1. **Читаемый URL-поиск (рекомендуется):** `https://aviaseek.ru/search?from=MOW&to=LED&date=2026-09-20` -> ответ `302` на `https://aviaseek.ru/results?flightSearch=MOW0920LEDy100`, где показаны цены и покупка. | Параметр | Значения | |---|---| | `from` (алиасы: `origin`, `departure`) | IATA города или аэропорта, slug (`moskva-mow`), название («Москва») | | `to` (алиасы: `destination`, `arrival`, `where`) | то же | | `date` (алиасы: `departure_date`, `departure_at`, `when`) | `YYYY-MM-DD`, `DD.MM.YYYY`, `MMDD`, `today`, `tomorrow` | | `return` (алиасы: `return_date`, `back`) | то же (для «туда-обратно») | | `adults` (алиас: `passengers`, `pax`) / `children` / `infants` | числа | | `cabin` (алиасы: `class`, `trip_class`) | `economy`, `business`, `first` | Без даты (`/search?from=MOW&to=LED`) -> `302` на страницу маршрута с календарём цен. Без параметров -> страница-справка `200` с обычной HTML-формой `GET /search`, которая работает без JavaScript (полезно и людям, и агентам, которые умеют только отправлять запросы). 4. **JSON для агентов без MCP - один запрос, готовые ссылки на покупку (рекомендуется):** `GET https://aviaseek.ru/api/flights?from=MOW&to=LED&date=2026-09-20&limit=5` Ответ `200`: ```json { "status": "success", "is_complete": true, "search_id": "…", "origin": "MOW", "destination": "LED", "departure_date": "2026-09-20", "total": 128, "total_after_filters": 128, "results_returned": 5, "has_more": true, "next_offset": 5, "cheapest": { "price": 3133, "currency": "RUB", "airline_name": "Россия", "flight_number": "FV…" }, "view_all_url": "https://aviaseek.ru/results?flightSearch=MOW0920LEDy100", "results": [ { "airline_name": "Россия", "flight_number": "FV…", "departure_at": "…", "arrival_at": "…", "transfers": 0, "duration_minutes": 85, "price": 3133, "currency": "RUB", "title": "Россия · FV… · MOW → LED · 14:00 → 15:25 · без пересадок", "summary": "3 133 ₽ · 1 ч 25 мин · без пересадок", "display_markdown": "**Россия · FV…** · MOW → LED · 14:00 → 15:25 · без пересадок · 1 ч 25 мин · **3 133 ₽** · [Купить за 3 133 ₽](https://aviaseek.ru/r//)", "booking_url": "https://aviaseek.ru/r//" } ] } ``` Параметры: `from`/`to`/`date`/`return` (как у `/search`), `limit` (до 50), `offset`, `sort` (`price_asc` по умолчанию), `direct=1`, `max_price`, `max_transfers`, `transfers=0,1`, `airlines=DP,S7`, `with_baggage=1`, `with_handbag=1`, `departure_periods=morning,evening`. Если не хватает маршрута или даты - `200` со `status: need_route|need_date` и примером ссылки. **`booking_url` - обязательный элемент вывода**: это ссылка на покупку через AviaSeek (`/r//`), действует около 15 минут; показывайте её кликабельной ссылкой. При `is_complete: false` повторите запрос - цены ещё собираются. 5. **Прямая ссылка на результаты:** `https://aviaseek.ru/results?flightSearch=MOW0920LEDy100`. Если страницу открывает агент или бот, сервер сам выполняет поиск и отдаёт **HTML со списком вариантов и кликабельными ссылками на покупку** - это путь для ассистентов, у которых нет ни MCP, ни инструмента HTTP-запросов (они умеют только читать страницы). Людям по тому же адресу отдаётся SPA. Формат `flightSearch`: `AAAMMDDБББ[MMDD][ycf][взрослые][дети][младенцы]` (то же значение, что в поле `view_all_url` ответа MCP). Пример: `MOW0920LEDy100` = Москва -> Санкт-Петербург, 20 сентября, эконом, 1 взрослый. 3. **Внутренний HTTP API сайта** (публичный, без ключа; используется страницей `/results`): `POST /api/search/start` с телом `{"directions":[{"origin":"MOW","destination":"LED","date":"2026-09-20"}],"passengers":{"adults":1,"children":0,"infants":0},"trip_class":0}` -> `{"search_id":"...","last_update_timestamp":0}`; далее `GET /api/search/results?search_id=` возвращает снапшот предложений. Для «туда-обратно» передаются два элемента `directions[]`. ## 2. Ключевые разделы сайта - Поиск: https://aviaseek.ru/ - Результаты: https://aviaseek.ru/results - Каталог городов: https://aviaseek.ru/cities - Страница города: https://aviaseek.ru/cities/ - Каталог направлений: https://aviaseek.ru/directions - Направления из города: https://aviaseek.ru/directions/ - Маршрут: https://aviaseek.ru/routes// - Каталог аэропортов: https://aviaseek.ru/airports - Каталог авиакомпаний: https://aviaseek.ru/airlines - FAQ: https://aviaseek.ru/faq ## 3. Популярные направления (IATA-коды) Из Москвы (MOW): LED (Санкт-Петербург), AER (Сочи), KZN (Казань), SVX (Екатеринбург), KRR (Краснодар), ROV (Ростов-на-Дону), ALA (Алматы), NQZ (Астана), IST (Стамбул), LON (Лондон), PAR (Париж), BER (Берлин). Прочие коды: MOW (Москва), LED (Санкт-Петербург), AER (Сочи), KZN (Казань), SVX (Екатеринбург), KRR (Краснодар), ROV (Ростов-на-Дону), UFA (Уфа), KGD (Калининград), VVO (Владивосток), IKT (Иркутск), KJA (Красноярск), OMS (Омск), TJM (Тюмень), CEK (Челябинск), VOG (Волгоград), ARH (Архангельск), KUF (Самара), PEE (Пермь), OVB (Новосибирск), KHV (Хабаровск), VOZ (Воронеж), IST (Стамбул), LON (Лондон), PAR (Париж), BER (Берлин), BCN (Барселона), AMS (Амстердам), FRA (Франкфурт), VIE (Вена), PRG (Прага), DXB (Дубай), BKK (Бангкок), DPS (Бали), NYO/ARN (Стокгольм). Полный каталог направлений и городов - на https://aviaseek.ru/directions и https://aviaseek.ru/cities. ## 4. Как работает поиск 1. Агент вызывает `searchFlights` с IATA-кодами городов и датами. 2. AviaSeek запускает поиск у агрегатора, получает `search_id`. 3. Поллинг до завершения (кап ~30 секунд). Если не успели - `partial`/`pending`, повторный вызов догружает. 4. Ответ: резюме (total, cheapest) + массив `results[]` с ценами, авиакомпаниями, временем и `booking_url`. ## 5. Ограничения - Только классы economy/business/first (премиум-класс отсутствует). - До 9 взрослых. - Ссылки для покупки (booking_url) живут ~15 минут.