Продукт ИИ-Хакатона Tutu поверх публичного MCP https://mcp.tutu.ru/mcp.
Живьём: http://79.137.196.134:9999/
Карта не того, куда можно доехать, а сколько часов вы там реально пробудете — посчитано назад от последней электрички домой. Поля ввода «куда» в интерфейсе нет: есть точка отправления и один циферблат «дома к».
Любой тревел-интерфейс спрашивает «куда?». Для пригородных поездок связывающее ограничение — никогда не путь туда, а последняя обратно. Люди не отказываются поехать потому, что не могут добраться. Они отказываются потому, что не могут понять, застрянут ли.
Значит запрос — это не (откуда, куда), а (откуда, «дома к X»), и ответ — географическое поле, которое сжимается при ужесточении дедлайна.
Ключевая инверсия, которую видно на карте: город на сорок минут дальше регулярно даёт больше часов на месте, потому что у него лучше обратное расписание.
| Циферблат «дома к» | Тянешь — поле сжимается или расцветает. Ни одного сетевого вызова при перетаскивании |
| Раскраска шайб | Размер и цвет — часы на земле, а не расстояние |
| Точка отправления | Любая из 38 станций узла, запоминается |
| Дата | Произвольная, не только сегодня и завтра |
| Бюджет | Потолок на билеты туда-обратно, цена едет в матрице расписаний |
| Карточка обрыва | Точная минута на платформе, два предыдущих отправления как запас |
| План с ночёвкой | 1–2 ночи: обратный рейс на дату отъезда плюс отель одним запросом на весь период |
| Лестница спасения | Возврата нет: отель у платформы, автобусы, оценка такси |
| Чат | Фраза правит параметры, план и числа считает домен. Следующая фраза правит предыдущий план |
| Навык Алисы | POST /api/alice: один факт, один ответ, сессия закрывается сразу |
cd backend
uv sync
uv run uvicorn obratno.app:app --host 127.0.0.1 --port 8099Открыть http://127.0.0.1:8099/. Фронт статический, сборка не нужна: MapLibre и GSAP лежат
в web/vendor, поэтому демо не зависит от CDN. Ключи не нужны: без LLM_API_KEY фразы разбирает
детерминированный парсер, и это видно в интерфейсе подписью «разобрано локально».
web/ статическая страница: карта, циферблат, карточка, чат
backend/src/obratno/
├── mcp_client.py тонкий клиент Tutu MCP: ретраи, потолок параллелизма
├── schedule.py пагинация, кэш, состояние окна ответа
├── ground.py математика часов на земле, tz-aware
├── chips.py разбор свободного текста: модель плюс детерминированный фолбэк
├── stations.py замороженная география, 38 станций с координатами
└── app.py HTTP: поле, SSE-веер, план, чат, Алиса
Бэкенд отдаёт расписание, клиент считает ответ. Поэтому перетаскивание циферблата идёт
в 60 FPS против API, который отвечает за секунды. Та же математика есть на сервере — для карточки
обрыва, чата и навыка Алисы. Причина и цена этого дублирования — в
docs/adr/0005-ground-time-on-client.md.
Полный лог с командами воспроизведения — docs/mcp/probe-log.md.
- Заголовок
MCP-Protocol-Versionобязателен дляtools/call, без него сервер отвечаетHTTP 500, и ошибка выглядит как проблема на стороне Tutu. - Тело обязано уходить UTF-8 байтами: кириллица в системной кодировке Windows даёт тот же
500. meta.total_matchedточен, потолок выдачи 300 офферов (page ≤ 10,page_size ≤ 30). Отсюда номер последней страницы вычисляется, а не подбирается: последняя электричка живёт именно там.- Переход через полночь живой. Мытищи в ответе на запрос за 20 августа отдают отправление
21 августа 01:22. Сравнение по времени суток напечатало бы уверенно неверное «последняя»,
поэтому время внутри домена — минуты от местной полуночи даты запроса, и 01:22 следующих
суток это
1522.
| Правило | Где видно |
|---|---|
| Ничего не выдумывать за Tutu | Пустое поле подписывается «Tutu не вернул это поле» |
| Оценка помечена как оценка | Время пешком до отеля и цена такси подписаны формулой, Tutu их не отдаёт |
| Цена отеля не умножается на ночи | price_basis: stay_total приходит уже за весь период |
| Обрезанное окно ответа отличимо от конца движения | Разомкнутое кольцо у шайбы и подпись тем же кеглем, ADR 0007 |
| География не выдаётся за грунтинг | Координаты ручные и объявлены таковыми, ADR 0003 |
| Модель не трогает числа | Она правит только видимые параметры, ADR 0006 |
Тёплый кэш на диске, прогрев при старте, SSE-веер с появлением шайб по мере ответов, потолок
параллелизма 8, экспоненциальный бэкофф на 429 и 5xx. Станция, по которой ответа нет, не
исчезает: она остаётся серой с честной подписью. Подробности —
ADR 0004.
| Файл | О чём |
|---|---|
docs/brief/ |
Задание хакатона и ТЗ продукта |
docs/adr/ |
Восемь архитектурных решений, написаны до кода |
docs/design/DESIGN.md |
Дизайн-документ: палитра, шкала часов, движение, запреты |
docs/mcp/probe-log.md |
Живые замеры Tutu MCP |