Skip to content

Latest commit

 

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Обратно — карта времени на земле

Продукт ИИ-Хакатона 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.

Что выяснено живыми вызовами MCP

Полный лог с командами воспроизведения — 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

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages