尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證CampusTrace — verified visual profile of any university in ~30 s (LOCUS Hackathon 2026, Case 01)
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Проверяемый визуальный профиль университета. Вводите название вуза — получаете фотографии кампуса, общежитий, аудиторий, библиотек, спортивных объектов, лабораторий и студенческой жизни, где у каждого кадра есть кликабельный первоисточник, лицензия, автор, даты и явно названное основание, почему он связан именно с этим вузом. Там, где материалов нет, сервис так и говорит — и отдельно различает «проверено, ничего не нашлось» и «источник не ответил, проверить не успели». Кейс: LOCUS Startup Hackathon 2026, Кейс 01 «Визуальный профиль университета».檢視完整 README ↗
Проверяемый визуальный профиль университета. Вводите название вуза — получаете фотографии кампуса, общежитий, аудиторий, библиотек, спортивных объектов, лабораторий и студенческой жизни, где у каждого кадра есть кликабельный первоисточник, лицензия, автор, даты и явно названное основание, почему он связан именно с этим вузом. Там, где материалов нет, сервис так и говорит — и отдельно различает «проверено, ничего не нашлось» и «источник не ответил, проверить не успели».
Кейс: LOCUS Startup Hackathon 2026, Кейс 01 «Визуальный профиль университета».
Проблема. Абитуриент выбирает вуз по картинке, а картинки в интернете — чужие, стоковые, устаревшие или вовсе с другого кампуса. Поиск по картинкам не говорит, кто автор, можно ли это показывать и снято ли это вообще здесь.
Наше решение — не «ещё одна галерея», а проверяемый паспорт. Ключевая идея: разделять находку и доказательство. CampusTrace никогда не утверждает больше, чем может показать:
| Что мы говорим | Что за этим стоит |
|---|---|
| «Вероятно» | Файл найден в тематическом источнике вуза, лицензия есть, но конкретный корпус не доказан |
| «Городской контекст» | Это кадр города, и мы прямо говорим, что это не кампус |
| «Требует проверки» | Источник и лицензия есть, но что изображено — не удалось определить ни по тексту, ни по изображению |
| «Не проверено: источник был недоступен» | Раздел пуст не потому, что материалов нет, а потому что источник упал на середине |
Слова «подтверждено» в значении «доказано место съёмки» в интерфейсе нет вообще — потому что доказать это по открытым данным мы не можем, а притворяться нельзя.
Три вещи, которых обычно нет у аналогов:
qwen/qwen3.8-27b или xAI Grok) смотрит на пиксели и называет тип места. При расхождении слоёв статус понижается.Требуется Python 3.11+ и интернет.
cd campustrace-mvp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# ОБЯЗАТЕЛЬНО: в .env замените [email protected] на реальный контакт.
# Nominatim и Wikimedia требуют, чтобы клиент себя идентифицировал.
set -a; source .env; set +a
uvicorn app.main:app --host 127.0.0.1 --port 8765
Откройте http://127.0.0.1:8765. API-документация: /docs. Health-check: /api/health.
Все ключи опциональны. Без единого ключа работает: поиск вуза, сбор фотографий, лицензии, дедупликация, категории по тексту, карта, сравнение. Ключи только добавляют слои:
| Переменная | Что включает | Что будет без неё |
|---|---|---|
GROQ_API_KEY | Визуальную проверку фото (qwen/qwen3.8-27b, мозаика 2×2) и сводку по отзывам студентов | Категории только по структурированным данным и тексту; профиль прямо сообщает, что визуальная проверка не выполнена |
GROK_API_KEY | Альтернативный vision-провайдер (xAI); если задан, используется вместо Groq | — |
CEREBRAS_API_KEY | Запасной текстовый ИИ (бесплатно ~1 млн токенов/сутки): включается, когда у Groq кончился минутный или суточный лимит (~200 тыс. токенов на модель) | Когда лимит Groq исчерпан, ИИ-функции честно пишут «дневной лимит исчерпан» |
TAVILY_API_KEY | Быстрый веб-поиск отзывов и статей со сниппетами (бесплатно, без карты) | Поиск идёт через browser_search Groq — ~35 тыс. токенов за сессию |
MAPBOX_TOKEN | Второй геокодер + альтернативные изохроны | Карта работает (MapLibre + OpenFreeMap, без ключа); координата помечается «только один источник» |
BRAVE_API_KEY | Поиск обсуждений на форумах и офиц. страниц | Работает только Reddit через PullPush |
FLICKR_API_KEY | Дополнительные фото под CC-лицензиями | Только Wikimedia Commons |
REDDIT_CLIENT_ID + REDDIT_CLIENT_SECRET | Официальный Reddit API (бесплатное приложение типа script) для блока «Голоса студентов»; без него используется архив PullPush, который часто отвечает 429 | Меньше обсуждений; блок честно пишет, что найдено недостаточно |
OPENROUTESERVICE_API_KEY | Изохроны пешей доступности | Зона не рисуется (и не подменяется кругом) |
YOUTUBE_API_KEY | Видео с проверенного офиц. канала | Блок видео пуст |
Базовая карта — MapLibre GL + OpenFreeMap, без API-ключа. Это сознательный выбор: демонстрация не должна падать из-за чужой квоты во время защиты.
Сценарий рассчитан на то, что вы введёте вуз, которого нет в нашем демо-видео.
Шаг 1. Любой незнакомый вуз. Введите на главной, например, Universität Heidelberg, Kyoto University или МГУ. Поиск понимает кириллицу, короткие аббревиатуры (MIT, NYU) и локальные написания. Вуз выбирается вручную — первый результат не подставляется автоматически, потому что одноимённых организаций много.
Шаг 2. Галерея — первое, что вы увидите. Фильтры слева переключают 8 разделов кейса + «Требует проверки». Под каждым фото сразу видны автор, лицензия (ссылкой на её условия) и ссылка на источник.
Шаг 3. Нажмите «Проверить источник» на любом кадре. Откроется паспорт: категория, статус, откуда найден файл, автор, лицензия, дата загрузки и дата съёмки (это разные даты), SHA-1, перцептивный хеш, вердикт визуального классификатора и — главное — согласуются ли две независимые проверки.
Шаг 4. Проверьте честность пустот. Прокрутите до «Полнота источников». Ноль в разделе подписан либо «Проверено — материалов не найдено», либо «Не проверено: источник был недоступен». Это разные утверждения, и мы их не смешиваем.
Шаг 5. Проверьте, что мы не выдаём город за кампус. Откройте фильтр «Город» — эти кадры имеют статус «Городской контекст» и явную пометку, что связь с кампусом не утверждается.
Шаг 6. Два вуза одного города. Соберите Nazarbayev University, затем в блоке «Сравнить покрытие» найдите Astana IT University. Общая городская фотография корректно принадлежит обоим профилям (составной ключ (ror_id, asset_id) в БД). В таблице сравниваются все 8 разделов, и ноль из-за упавшего источника подписан отдельно.
Шаг 7. Отказ источника. Нажмите «Обновить данные» несколько раз подряд. Wikimedia ответит 429; профиль станет partial, предыдущая сохранённая версия останется доступной, и сервис скажет, что именно не успел проверить.
Backend: Python 3.11, FastAPI, httpx (async), SQLite, Pillow (перцептивные хеши). Frontend: ванильный JS без фреймворка и сборщика, MapLibre GL JS, COBE (глобус). Никакого npm-бандла — страница отдаётся статикой. Хранилище: SQLite (профили, материалы, кэш источников, журнал событий).
Поиск по названию
└─ ROR v2 + локальные синонимы/транслитерация ──> institution_id (ROR ID)
│ (идентичность: город, страна, алиасы, домен, Wikidata QID)
▼
Сбор кандидатов (параллельно, у каждого адаптера свой таймаут и учёт отказа)
├─ Wikidata search (haswbstatement): здания вуза (P361/P137/P127) с фото и типом P31 ← категория по типу; SPARQL — запасной путь
├─ Commons depicts: haswbstatement:P180=<вуз|здания> ← «на фото изображено»
├─ Commons geosearch у точки кампуса (P625) — только вместе с названием вуза/здания
├─ Wikimedia Commons: категория вуза + подкатегории (без коллекций/карт/сканов, ≤6 файлов из одной)
├─ Commons search: визуальные запросы по названию
├─ Wikipedia (en/ru): изображения статьи, если Commons беден
├─ Wikidata P18: официальное изображение
├─ Flickr: поиск по CC-лицензиям (опционально)
└─ Отдельно: поиск по городу — это ДРУГОЕ утверждение
▼
Фильтр лицензии ──> только открытые лицензии (CC0 / CC BY / CC BY-SA / PD / GFDL); остальное отбрасывается и считается
▼
Слой 1: текстовая классификация
(название + описание + провенанс подкатегории; стоп-слова событий;
по умолчанию НЕ «кампус», а «unknown»)
▼
Дедупликация: SHA-1 (точные копии) + dhash/Hamming≤5 (визуально похожие)
▼
Слой 2: визуальная классификация (Groq qwen3.8 или xAI Grok; миниатюры 330 px
уходят base64 мозаикой 2×2 — 4 фото за запрос; при 429 проверка
останавливается и профиль сообщает, сколько кадров не проверено)
├─ «не место» ──────────> кандидат снимается с публикации
├─ «город, не кампус» ──> переносится в раздел «Город»
├─ совпало с текстом ───> основание добавляется, статус НЕ повышается
└─ РАСХОЖДЕНИЕ ─────────> статус понижается до «не подтверждено»
▼
evidence[] + evidence_level (число независимых подтверждений) у каждого кадра
▼
Снимок профиля: complete | partial + состояние каждой из 8 категорий
(параллельно /preview отдаёт первые лицензированные кадры из Wikidata за ~3 с)
▼
SQLite: profiles + assets(ror_id, id) + source_events + research_cache
▼
Одностраничный UI: галерея → описание по источникам → полнота → карта → сравнение
Файлы:
| Файл | Ответственность |
|---|---|
app/main.py | HTTP API, бюджет времени, объединение одинаковых запросов, кэш |
app/pipeline.py | Сбор кандидатов, текстовая классификация, дедупликация, описание |
app/vision.py | Независимая визуальная классификация и правила согласования слоёв |
app/integrations.py | Адаптеры внешних API: таймаут, кэш, rate limit, учёт отказов |
app/atlas.py | География: перекрёстная проверка координат, границы OSM, изохроны |
app/discovery.py | Поиск и ранжирование организаций, транслитерация, пагинация |
app/db.py | Схема SQLite и миграция |
app/voices.py | Поиск публичных обсуждений + сводка со ссылками |
scripts/benchmark.py | Замер реального времени ожидания на выборке вузов |
Ключевые архитектурные решения:
campus был категорией по умолчанию, поэтому наградные знаки, концерт, заседание сената и выпускной попадали в «кампус». Теперь по умолчанию — unknown, а список стоп-слов событий (en/ru/et/kk) запрещает общую догадку «кампус» для кадра, в названии которого описано событие, а не место.thumb.wikimedia.org, а проверка хоста этого не знала), и 0 дублей означало «не проверяли». Теперь пользователю показывается, сколько кандидатов реально захешировано.asset_id.| Источник | Назначение | Лицензия/условия |
|---|---|---|
| ROR API v2 | Идентичность организации, алиасы, город, домен | CC0, без ключа |
| Wikidata | Commons-категория P373, координата P625, студенты P2196, канал P2397, изображения P18/P8517/P3451/P5775 | CC0, без ключа |
Wikidata Search API (haswbstatement) + Query Service как запасной путь | Здания и объекты вуза с фото и типом P31; факты вуза (тип, объединения, руководитель, девиз, адрес, сотрудники, эндаумент) | CC0, без ключа |
| Wikimedia Commons | Файлы, метаданные прав, структурированные данные depicts (P180), геопоиск | Лицензия у каждого файла своя; требуется корректный User-Agent |
| Openverse API | Открытые фото (в основном Flickr) с автором и ссылкой на первоисточник; берутся только лицензии CC BY, CC BY-SA, CC0, PDM; название вуза обязано быть в названии или тегах, категория — только из названия | Без ключа: 20 запросов/мин и 200/сутки; ответы кэшируются на 24 ч |
| Wikipedia REST summary | Блок «Об университете»: краткое описание (ru, иначе en) с атрибуцией CC BY-SA | Без ключа |
| GDELT DOC 2.0 | «В новостях»: заголовки за 12 месяцев, где вуз назван в заголовке; только ссылки | Без ключа; не чаще 1 запроса в 5 с, кэш 6 ч; часто отвечает 429 — тогда блок скрыт |
| Официальные виджеты YouTube / Instagram / TikTok |
| Модель / сервис | Где используется | Что именно делает | Границы |
|---|---|---|---|
Groq vision, qwen/qwen3.8-27b (по умолчанию) или Grok vision (xAI) при наличии GROK_API_KEY | app/vision.py | Смотрит на изображение и называет тип места из фиксированного словаря (кампус снаружи, общежитие, аудитория, библиотека, лаборатория, спорт, студенческая жизнь, город-не-кампус, не относится) | Никогда не определяет, какой это вуз — только вид места. Ответ вне словаря отбрасывается. Низкая уверенность не даёт права ни публиковать, ни удалять |
Groq, openai/gpt-oss-20b (ИИ-разбор подписей) | app/triage.py | Один пакетный запрос на профиль: читает только заголовок и описание файла и отвечает на два закрытых вопроса — о чём текст (этот вуз / другая организация / человек или событие / город / неясно) и какой тип места назван. Работает на любом языке (эстонский «ühiselamu» → общежитие, японский 図書館 → библиотека) | Не считается независимым подтверждением — модель читает те же слова, что и человек, только лучше. Может заполнить unknown и понизить подпись о другой организации или человеке; не повышает достоверность; структурированное подтверждение (Wikidata, depicts) она не отменяет. Словарь ответов закрыт, ответ вне словаря отбрасывается, вердикты кэшируются |
Groq, openai/gpt-oss-20b | app/voices.py | Нейтральная сводка только по переданным фрагментам публичных постов | Каждый вывод обязан ссылаться на номера источников; вывод без ссылки отбрасывается |
| Groq compound-mini (web search) | app/voices.py | Поиск обсуждений | Принимаются только URL из результатов инструмента; ссылки, придуманные моделью в тексте, игнорируются (есть регрессионный тест) |
Лимит vision честно ограничен. Бесплатный Groq даёт ~7000 входных токенов в минуту, а одно изображение стоит ~1600–1800 токенов независимо от размера. Поэтому 4 миниатюры склеиваются в одну мозаику 2×2 (≈1600 токенов на четыре кадра) и получают четыре независимых вердикта. Вердикты кэшируются на 30 дней по asset_id; при исчерпании квоты профиль пишет «проверено N из M».
Ни одна модель не генерирует изображения, не описывает кампус «своими словами» и не выставляет оценок вузу.
| Источник | Назначение |
|---|---|
| Mapbox | Второй геокодер для перекрёстной проверки координаты; альтернативные изохроны |
| openrouteservice | Изохроны пешей/вело доступности |
| Flickr | Фото с CC-лицензиями (только id 4,5,9,11,12) |
| Brave Search | Ссылки на официальные страницы и форумы |
| YouTube Data API | Видео проверенного официального канала |
Ключи хранятся только в переменных окружения. .env в .gitignore. В репозитории нет ни одного секрета — проверьте git log -p | grep -i "api_key".
| Компонент | Версия | Происхождение | Лицензия | Как используется |
|---|---|---|---|---|
| FastAPI | 0.115.x | Sebastián Ramírez / open source | MIT | HTTP-слой, автодокументация |
| uvicorn | 0.30.x | encode | BSD-3 | ASGI-сервер |
| httpx | 0.27.x | encode | BSD-3 | Асинхронные HTTP-запросы к источникам |
| Pillow | 10.4.x | Python Imaging Library fork | HPND/MIT-подобная | Перцептивный хеш (dhash) миниатюр |
| MapLibre GL JS | 5.8.0 | MapLibre (форк Mapbox GL JS v1) | BSD-3 | Интерактивная карта; поставляется локально в static/vendor/ |
| COBE | bundled | shuding/cobe | MIT | WebGL-глобус на главной; static/vendor/cobe.bundle.mjs |
| box-loader | — | CSS-анимация загрузки, написана для проекта | — | static/box-loader.css |
| SQLite | встроен в Python | D. R. Hipp | Public domain | Хранилище |
Что написано нами: весь код в app/ и scripts/, весь static/app.js, static/atlas.js, static/globe.js, static/style.css, схема БД, правила классификации и согласования слоёв, промпты.
Использование AI при разработке: код писался совместно с ассистентами (Claude, GPT). Дизайн-направление главной страницы проработано с помощью design-taste-frontend, промпт сохранён рядом с проектом в CAMPUS_TRACE_ASTRA_PROMPT.md. Все архитектурные решения, правила достоверности и формулировки статусов проверены и приняты командой вручную; каждое изменение сопровождается тестом.
Данные OSM-слоёв (data/atlas_nu.json, data/atlas_upenn.json) — снимок открытых данных OpenStreetMap от 16 сентября 2026, каждый объект содержит ссылку на исходный OSM-объект, атрибуция ODbL. Это подтверждает наличие объекта в OSM, а не его официальный статус.
Жюри оценивает полное время: от ввода названия до полезного результата, лимит 30 секунд. Поэтому:
PROFILE_BUDGET_SECONDS (по умолчанию 24 с) плюс 3 с страховочного таймаута; остальное оставлено на поиск, передачу и отрисовку./api/profiles/{id}/preview (изображения Wikidata вуза и его зданий, один запрос к Commons) и показываются прямо на экране загрузки.timings (по стадиям), elapsed_ms и time_to_first_asset_ms.# 1) сервер в отдельном терминале
uvicorn app.main:app --port 8765
# 2) холодный прогон (единственный честный)
python3 scripts/benchmark.py --base http://127.0.0.1:8765 --refresh \
--out docs/benchmark.json --markdown docs/benchmark.md
Выборка по умолчанию — 15 вузов: Казахстан, Европа, Северная Америка, Азия, Африка, Южная Америка; большие и малые; латиница и кириллица; короткая аббревиатура (MIT); два вуза одного города. Скрипт считает p50/p95 для submit → первое фото и submit → полный профиль, число неполных профилей и число превышений 30 секунд.
Стенд: https://campustraceapp.vercel.app (Vercel, функция Python), запросы из Казахстана, --refresh, вузы по очереди, версия пайплайна 0.8.0, бюджет сервера 21 с. Ключей ИИ на проде в момент замера не было, поэтому столбец Vision = 0. 14 из 15 сборок прошли без единого попадания в кэш источников.
| Запрос | Определён как | Материалов | Разделов | submit→первое фото | submit→полный профиль | Из кэша источников | Vision | Статус |
|---|---|---|---|---|---|---|---|---|
| Nazarbayev University | Nazarbayev University | 20 | 4 | 8.9 с | 8.9 с | 0/16 | 0 | complete |
| Astana IT University | Astana IT University | 14 | 5 | 6.9 с | 6.9 с | 0/8 | 0 | complete |
| Al-Farabi Kazakh National University | Al-Farabi Kazakh National University | 12 | 2 | 1.5 с | 9.7 с | 0/16 | 0 | complete |
| University of Tartu | University of Tartu | 40 | 3 | 1.3 с | 15.8 с | 0/20 | 0 | partial |
| МГУ | Lomonosov Moscow State University | 83 | 5 | 2.4 с | 16.7 с | 1/19 | 0 | complete |
| MIT | Massachusetts Institute of Technology | 63 | 7 | 2.9 с | 13.0 с | 0/17 | 0 | complete |
| University of Pennsylvania | University of Pennsylvania | 60 | 6 | 2.3 с | 13.7 с | 0/18 | 0 | complete |
| Universität Heidelberg | Heidelberg University |
Замер: 2026-09-19 10:10:44+0500. Вузов: 15, с материалами: 15, пустых: 0, не найдено в реестре: 0, ошибок: 0, недоступен реестр: 0, неполных профилей: 4. Сборок без единого попадания в кэш источников: 14. Стенд: https://campustraceapp.vercel.app.
submit → первое фото: p50 2180 мс, p95 8936 мс. submit → полный профиль: p50 12979 мс, p95 16693 мс, максимум 16935 мс, превысили 30 с: 0.
Итог: у всех 15 вузов есть фото с открытой лицензией (в среднем 42,8), ошибок нет, ни один вуз не превысил 30 секунд; первое фото — p50 2,2 с; полный профиль — p50 13,0 с, максимум 16,9 с. Сырые строки — docs/benchmark-prod.json.
Стенд: MacBook, домашняя сеть в Казахстане, локальный сервер с пустой SQLite (кэш источников холодный), --refresh, вузы по очереди. Код — коммит 36790f9, запущенный с REQUEST_TIMEOUT_SECONDS=6 (в самом коммите значение по умолчанию уже 8 с — почему, см. ниже; воспроизвести: REQUEST_TIMEOUT_SECONDS=6 DATABASE_PATH=/tmp/cold.sqlite3 uvicorn app.main:app --port 8766). «Из кэша источников» — сколько ответов внешних API пришло из кэша: это поиск и предпросмотр того же вуза, запущенные браузером одновременно со сборкой. «submit→первое фото» — момент, когда ответил /preview с кадрами (так же, как это видит пользователь); если предпросмотр пуст, первое фото приходит вместе с полным профилем. Vision — сколько кадров проверено по изображению за эту сборку (бесплатный лимит Groq — примерно 4 мозаики в минуту).
| Запрос | Определён как | Материалов | Разделов | submit→первое фото | submit→полный профиль | Из кэша источников | Vision | Статус |
|---|---|---|---|---|---|---|---|---|
| Nazarbayev University | Nazarbayev University | 21 | 4 | 14.1 с | 14.1 с | 1/9 | 8 | complete |
| Astana IT University | Astana IT University | 14 | 5 | 9.2 с | 9.2 с | 1/7 | 0 | complete |
| Al-Farabi Kazakh National University | Al-Farabi Kazakh National University | 10 | 2 | 23.7 с | 23.7 с | 0/11 | 4 | partial |
| University of Tartu | University of Tartu | 38 | 4 | 5.6 с | 26.5 с | 0/13 | 4 | complete |
| МГУ | Lomonosov Moscow State University | 0 | 0 | 4.3 с | 19.1 с | 2/12 | 0 | partial |
| MIT | Massachusetts Institute of Technology | 51 | 6 | 4.9 с | 24.6 с | 1/12 | 4 | complete |
| University of Pennsylvania | University of Pennsylvania | 36 | 3 | 2.8 с | 22.5 с | 1/12 | 4 | complete |
| Universität Heidelberg | Heidelberg University |
Замер: 2026-09-18 21:14:38+0500. Вузов: 15, с материалами: 14, пустых: 1, не найдено в реестре: 0, ошибок: 0, недоступен реестр: 0, неполных профилей: 3. Сборок без единого попадания в кэш источников: 5. Стенд: http://127.0.0.1:8766.
submit → первое фото: p50 4648 мс, p95 14093 мс. submit → полный профиль: p50 22208 мс, p95 24621 мс, максимум 26497 мс, превысили 30 с: 0.
Что видно из замера — честно:
partial: часть этапов была пропущена по бюджету времени, и профиль прямо это говорит.partial. После этого таймаут запроса вернули к 8 с; при повторной сборке МГУ даёт 60 материалов, но в таблицу это не вписано, потому что это уже не холодный замер.docs/benchmark.json.Соблазн очевиден: в Instagram и Threads больше «живых» фото кампуса, чем в Wikimedia Commons. Мы сознательно не берём оттуда фотографии, и вот почему:
Что мы делаем вместо этого. Публичные обсуждения (Reddit, студенческие форумы, студенческие издания) используются как текстовое свидетельство: заголовок, короткая цитата, ссылка на первоисточник и нейтральная сводка. Фотографии оттуда не встраиваются. Для изображений источники остаются лицензионно чистыми: Wikimedia Commons и Flickr с фильтром по CC-лицензиям.
Это сознательный размен recall на правовую чистоту. Если вуз захочет более богатую галерею — «карта пробелов» в профиле показывает, каких именно материалов не хватает, и вуз может передать их под открытой лицензией.
Заполните перед сабмитом. Кейс требует явного указания ролей; выдумывать их за команду нельзя.
| Участник | Роль | Зона ответственности |
|---|---|---|
<ФИО> | Backend / пайплайн достоверности | app/pipeline.py, app/vision.py, правила согласования слоёв |
<ФИО> | Frontend / UX | static/, дизайн-система, доступность |
<ФИО> | Данные и интеграции | app/integrations.py, лицензии, OSM-слои |
<ФИО> | Продукт, презентация, демо | README, слайды, видео, коммуникация с жюри |
Контакт для жюри: <email>. Промокод сабмита на aistartify.com: LOCUSCASE1.
В репозитории лежат готовые конфигурации; выберите одну.
vercel.json описывает один сервис FastAPI с точкой входа api/index.py (она создаёт схему SQLite при импорте: на Vercel файловая система только для чтения, кроме /tmp). Деплой: vercel link --yes --project campustrace, затем vercel deploy --prod --yes; репозиторий подключён, поэтому каждый push в main выкатывается сам. Переменные окружения проекта: CAMPUS_TRACE_USER_AGENT, DATABASE_PATH=/tmp/campustrace.sqlite3; GROQ_API_KEY добавляется владельцем вручную (vercel env add GROQ_API_KEY production).
Ограничения этого хоста, о которых нужно знать: кэш SQLite живёт в /tmp каждого инстанса и теряется при холодном старте; внутрипроцессный лимитер Commons и объединение одинаковых запросов действуют в пределах одного инстанса; Openverse отвечает 401 анонимным запросам из дата-центров — фото оттуда включаются после регистрации бесплатного клиента (OPENVERSE_CLIENT_ID/SECRET).
render.yaml подхватится автоматически.CAMPUS_TRACE_USER_AGENT с реальным контактом. Остальные ключи — по желанию.https://<app>.onrender.com/api/health.На бесплатном плане инстанс засыпает при простое. Откройте ссылку за 5 минут до защиты, чтобы жюри не попало на холодный старт поверх 30-секундного бюджета.
fly launch --no-deploy # подхватит fly.toml
fly volumes create campustrace_data --size 1
fly secrets set CAMPUS_TRACE_USER_AGENT="CampusTrace/0.6 (contact: [email protected])"
fly secrets set GROK_API_KEY="..." # опционально
fly deploy
New Project → Deploy from GitHub repo. railway.json укажет на Dockerfile. Переменные задаются во вкладке Variables.
docker build -t campustrace .
docker run -p 8000:8000 -v campustrace-data:/data \
-e CAMPUS_TRACE_USER_AGENT="CampusTrace/0.6 (contact: [email protected])" \
campustrace
Чек-лист после деплоя:
/api/health отвечает {"ok": true}/api/integrations показывает, какие ключи реально подключеныpython3 -m unittest discover -s tests -v # 88 тестов
python3 -m compileall -q app
node --check static/app.js
Тесты закрывают именно те свойства, которые легко потерять при доработке:
thumb.wikimedia.org (регрессия на реальный дефект);cmcontinue;MIT) находится, но не срабатывает внутри слова Summit;P2196 (число студентов) может быть без года или без первичного источника; мы показываем год и ссылку и предупреждаем об этом.| Запрещено кейсом | Как это исключено у нас |
|---|---|
| Выдавать стоковые/чужие фото за конкретный кампус | Кадр без лицензии не публикуется; городские кадры имеют отдельный статус и прямую пометку; фото из соцсетей не встраиваются вообще |
| Скрывать ручной подбор под видом автопоиска | Ручных подборок фото нет. Единственные ручные данные — OSM-слои трёх вузов и локальные синонимы поиска; и то и другое описано выше |
| Подделывать источники, скорость, достоверность | Клик по любому фото открывает первоисточник; таблица скорости — живой замер с датой, стендом и долей ответов из кэша; «показатель достоверности» — не процент, а уровень (высокая / средняя / низкая), вычисляемый по числу независимых видов подтверждений; понижается при расхождении текста и изображения, неопределённой категории и городском контексте, никогда не повышается; каждое подтверждение раскрывается ссылкой |
| Нарушать лицензии источников | Жёсткий фильтр лицензии; атрибуция на карточке и в паспорте; соблюдение лимита Nominatim и требований User-Agent; ODbL-атрибуция карт |
| Публиковать секретные ключи | .env в .gitignore; все ключи только через переменные окружения; в коде нет ни одного литерала ключа |
| Требование кейса | Состояние |
|---|---|
| 1. Поиск по названию + разрешение неоднозначности | Есть: ROR + локальные синонимы, транслитерация, пагинация, выбор вручную |
| 2. Кампус, общежитие, аудитории, библиотека, город | Есть; полнота ограничена доступностью открытых материалов, пробелы показаны явно |
| 3. Автоматическая классификация | Есть: два независимых слоя — текстовый и визуальный |
| 4. Удаление одинаковых, похожих и нерелевантных | Есть: SHA-1 + dhash + снятие нерелевантных визуальным классификатором |
| 5. Проверка связи фото с вузом/объектом/городом | Есть на уровне оснований и статусов; геодоказательства не заявляются |
| 6. Кликабельный источник и дата | Есть: источник, автор, лицензия, дата загрузки и дата съёмки раздельно |
| 7. Краткое описание кампуса из источников | Есть: собирается только из найденных объектов, со ссылками |
| 8. Фильтры общежитие/спорт/лаборатории/студенческая жизнь | Есть, плюс отдельный раздел «Требует проверки» |
Ограничение — 3 минуты. Сценарий (показывать экран, говорить коротко):
| Время | Что показать | Что сказать |
|---|---|---|
| 0:00–0:20 | Главная, глобус | «Абитуриент выбирает вуз по картинкам, но не знает, чьи это картинки и тот ли это кампус. CampusTrace показывает только то, что может подтвердить источником» |
| 0:20–0:50 | Ввод незнакомого вуза, сборка профиля | «Поиск понимает кириллицу и аббревиатуры. Организация выбирается вручную — одноимённых много. Полная сборка укладывается в бюджет 30 секунд» |
| 0:50–1:25 | Галерея, фильтры, карточка с автором и лицензией | «Восемь разделов кейса. Автор и лицензия — сразу под кадром, а не спрятаны» |
| 1:25–2:00 | «Проверить источник» → паспорт | «Вот главное: две независимые проверки — текст источника и визуальный классификатор. Если они расходятся, мы понижаем уверенность, а не выбираем удобный вариант» |
| 2:00–2:25 | Полнота источников: пустой раздел и раздел с упавшим источником | «Ноль ноль рознь. "Проверено, не нашли" и "источник не ответил" — разные утверждения, и мы их различаем» |
| 2:25–2:45 | Фильтр «Город» + сравнение двух вузов одного города | «Городской кадр никогда не выдаётся за кампус. Сравниваются все восемь разделов, а ноль из-за отказа источника подписан отдельно» |
| 2:45–3:00 | Финал | «Мы не доказываем место съёмки — по открытым данным это невозможно. Мы показываем, что именно проверено, а что нет. Это и есть продукт» |
Технически: экран 1920×1080, запись без звука окружения, голос поверх, показывать реальный развёрнутый сайт (не localhost), не ускорять таймер сборки. Вуз в видео должен отличаться от того, который вы покажете на защите.
GET /api/health
GET /api/integrations
GET /api/search/suggest?q=НУ&page=1
GET /api/institutions?q=Nazarbayev%20University
GET /api/profiles/{ror_id} # ?refresh=true — принудительная пересборка
GET /api/profiles/{ror_id}/preview # первые лицензированные кадры из Wikidata за ~3 с
GET /api/profiles/{ror_id}/ask?q=... # ответ только по источникам профиля, с номерами ссылок
GET /api/profiles/{ror_id}/news # заголовки GDELT, где назван вуз
GET /api/profiles/{ror_id}/extras # кэшируется на час
GET /api/profiles/{ror_id}/student-voices
GET /api/assets/{asset_id}?ror_id={ror_id}
GET /api/atlas/{ror_id}
GET /api/atlas/{ror_id}/isochrone?mode=walking&minutes=15
GET /api/compare?left={ror_id}&right={ror_id}
Полезные поля ответа профиля: evidence и evidence_level у каждого кадра, rejected_license_count, institution.buildings, profile_status, category_status, unclassified_count, vision, rejected_by_vision, timings, time_to_first_asset_ms, duplicate_count, license_eligible_count, warnings, source_events, from_cache.
| «Соцсети вуза»: аккаунты из Wikidata (P2003, P2397, P7085, P2013, P3185…), плеер канала и публичные посты, найденные поиском |
| Контент остаётся на платформе; в проверку и категории фото не входит |
| Simple Icons | Логотипы платформ в ленте источников и карточках отзывов | CC0; static/brand-icons.js |
| Wikipedia API | Изображения статьи вуза, когда Commons беден | CC BY-SA, без ключа |
| OpenAlex | Ряд публикаций по годам | CC0, без ключа |
| Open-Meteo | Погода города | Без ключа, некоммерческое использование |
| Nominatim / OSM | Кандидатная точка карты | ODbL; максимум 1 запрос/с, обязателен контакт в User-Agent |
| OpenFreeMap | Базовая карта | Данные OSM, ODbL, без ключа |
| PullPush | Индекс публичных постов Reddit | Без ключа, выдача заведомо неполная |
| 49 |
| 3 |
| 2.1 с |
| 13.4 с |
| 0/17 |
| 0 |
| complete |
| Politecnico di Milano | Politecnico di Milano | 41 | 6 | 1.9 с | 10.7 с | 0/15 | 0 | complete |
| Université de Montréal | Université de Montréal | 55 | 4 | 1.9 с | 13.7 с | 0/17 | 0 | complete |
| National University of Singapore | National University of Singapore | 32 | 5 | 11.3 с | 11.3 с | 0/16 | 0 | complete |
| Universidad de Buenos Aires | Universidad de Buenos Aires | 28 | 3 | 1.9 с | 8.4 с | 0/14 | 0 | partial |
| Uppsala universitet | Uppsala University | 40 | 5 | 2.2 с | 15.8 с | 0/20 | 0 | complete |
| University of Cape Town | University of Cape Town | 70 | 6 | 3.0 с | 16.9 с | 0/19 | 0 | partial |
| Kyoto University | Kyoto University | 35 | 3 | 2.2 с | 9.8 с | 0/15 | 0 | partial |
| 38 |
| 3 |
| 5.0 с |
| 24.3 с |
| 1/11 |
| 4 |
| partial |
| Politecnico di Milano | Politecnico di Milano | 30 | 6 | 3.5 с | 16.1 с | 0/10 | 0 | complete |
| Université de Montréal | Université de Montréal | 49 | 4 | 4.6 с | 20.9 с | 1/13 | 4 | complete |
| National University of Singapore | National University of Singapore | 28 | 4 | 3.7 с | 23.9 с | 1/12 | 2 | partial |
| Universidad de Buenos Aires | Universidad de Buenos Aires | 28 | 3 | 4.4 с | 18.7 с | 0/9 | 0 | complete |
| Uppsala universitet | Uppsala University | 39 | 4 | 3.8 с | 23.7 с | 1/14 | 4 | complete |
| University of Cape Town | University of Cape Town | 55 | 6 | 5.5 с | 22.2 с | 0/12 | 0 | complete |
| Kyoto University | Kyoto University | 26 | 4 | 4.1 с | 12.7 с | 1/9 | 0 | complete |