ТойМатч
Сервис подбора до трёх event-подрядчиков под той, свадьбу или корпоратив: у каждой карточки объяснение фактами, а если подходящих меньше трёх…
О проекте
Команда: AND?
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Доведённый продукт
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Сервис подбора до трёх event-подрядчиков под той, свадьбу или корпоратив: у каждой карточки объяснение фактами, а если подходящих меньше трёх, диагностика и подсказка, что изменить в запросе.
Как устроено
Запрос из формы получает LLM-агент, который через tool calling вызывает search_contractors. Дальше детерминированный Python: жёсткие фильтры (город, категория, занятость на дату, формат, бюджет, часы, язык), скоринг с фиксированными WEIGHTS, топ-3 и диагностика _diagnose. Затем LLM пишет объяснения по посчитанным фактам, не меняя порядок. Цикл агента хранит состояние в БД (Run.messages, Run.pending) и поддерживает паузу на подтверждение человеком. Без ключа работает mock-режим.
Ключевые решения
- function/tool calling
- жёсткие фильтры + взвешенный скоринг
- диагностика пустой выдачи
- mock-LLM режим
- eval-прогон кейсов
Особенности
Ядро агента не привязано к предметной области: домен подключается одним файлом в core/domains/ и одной переменной в .env. Порядок карточек детерминирован, LLM только объясняет. Есть журнал шагов агента в UI, «плашки честности» и готовые подсказки вида «сдвиньте одно условие».
Стек
Python 3.12 · Django 5.2 · SQLite · gunicorn · Next.js 16 · React 19 · TypeScript · Docker Compose
Модели
OpenAI gpt-5.4-mini · OpenAI-совместимый API
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-769bc54e-and.
README
README.mdТойМатч
ТойМатч подбирает event-подрядчиков под той, свадьбу или корпоратив: три карточки из каталога, у каждой своя причина, почему именно она. Название - от «той» и «match».
Команда: AND · Задача: #79-lite «Умный подбор подрядчиков» · Трек: Креативные индустрии
Коротко
- Что решает. Заказчик мероприятия задаёт город, дату, тип события, категорию подрядчика и бюджет - сервис возвращает до трёх подходящих подрядчиков из каталога, и у каждого объяснение фактами, почему он здесь. Если подходящих меньше трёх или нет совсем, говорит почему и что изменить в запросе.
- Как запустить.
docker compose up --buildв корне клона - без.envи ключей, интерфейс на http://localhost:3000. Без Docker:python scripts/dev.py setup, затемbackendиfrontendв двух терминалах. Подробнее - Попробовать без установки. Публичная версия: https://frontend-production-ad7c4.up.railway.app -
та же версия, что в
main, живая модельgpt-5.4-mini, доступна минимум до 30.09.2026. Проверка по README от неё не зависит: всё ниже запускается из клона без сети и ключей. - Технологии. Python и Django, Next.js и TypeScript, SQLite; языковая модель через OpenAI-совместимый API, без ключа - mock-режим. Подробнее
- Как проверить.
python scripts/verify.pyпечатаетOK; вручную - пять запросов, по одному на каждый исход. Подробнее
На macOS и Linux интерпретатор Python обычно вызывается как python3: во всех командах
этого README подставляйте его вместо python.
Описание решения и его назначение
Сервис помогает заказчику мероприятия в Казахстане выбрать event-подрядчика из каталога: вместо длинного списка - до трёх карточек, и у каждой объяснение, почему она здесь.
Заказчик уже выбрал город, дату, тип мероприятия и категорию подрядчика и задаёт бюджет; длительность, язык и пожелания - по желанию. Сервис отсекает занятых на эту дату и не подходящих по формату, бюджету, языку и часам, ранжирует оставшихся по фиксированной формуле и объясняет каждую карточку фактами: запас по бюджету, форматы, языки, цитата из описания. Если подходящих меньше трёх или нет совсем, он говорит, что именно помешало и какое одно изменение запроса даст результат.
Отбор и порядок карточек - детерминированный код, модель только пишет объяснения. Поэтому тот же запрос всегда даёт тот же порядок (требование 5 ТЗ), а отбор работает и без ключей к модели. Модель встроена как агент: она сама вызывает инструмент поиска, читает посчитанные факты и пишет по ним объяснения. Каждый шаг виден в журнале. Механизм подтверждения человеком в ядре есть, но в этой Задаче не задействован: ТЗ исключает бронирование, заявки и уведомления подрядчику.
Описание архитектуры
форма запроса ──> LLM: вызывает search_contractors с параметрами ← агентный шаг
│
▼ чистый Python: детерминированно, без сети и ключей
жёсткие фильтры: город, категория, занятость на дату, формат, бюджет, часы, язык
▼
скоринг с фиксированными весами (WEIGHTS) ──> до 3 карточек + посчитанные факты
▼
меньше трёх? ──> диагностика: что помешало, какая дата, какой бюджет, сезон
│
▼
LLM: объяснения к карточкам по фактам, порядок не меняет
▼
интерфейс: исход, карточки, плашки честности, «почему» и «что сделать», журнал шагов
| Слой | Где | За что отвечает |
|---|---|---|
| Цикл агента | backend/agent/core/loop.py | шаг за шагом ведёт запуск, ставит на паузу, продолжает |
| Доступ к модели | backend/agent/core/llm.py | единственное место вызова LLM; здесь же mock-режим |
| Реестр инструментов | backend/agent/core/tools.py | регистрация, JSON-схемы, безопасный вызов |
| Предметная область | backend/agent/core/domains/contractors.py | фильтры _reasons, скоринг _score и WEIGHTS, диагностика _diagnose, инструменты search_contractors и diagnose_request, промпт и демо-примеры |
| Каталог | backend/agent/data/contractors.csv | 66 профилей от организатора, читается без сети |
| Системный промпт | backend/agent/core/prompts.py | общая часть + BRIEF активного домена |
| HTTP API | backend/agent/views.py, urls.py | JSON-эндпоинты под /api/ |
| Интерфейс | frontend/app/page.tsx, frontend/components/ | форма запроса, исход и карточки (Results.tsx), журнал шагов (Journal.tsx) |
| Команды разработки | scripts/dev.py, scripts/verify.py, scripts/check_llm.py | установка, запуск, тесты, сквозная проверка, проверка провайдера |
| Прогон качества | backend/agent/evals/ | набор кейсов и отчёт о результатах в docs/EVAL.md |
Состояние запуска целиком лежит в БД (Run.messages, Run.pending), поэтому
запуск можно поставить на паузу ради человека и продолжить позже.
Ядро не знает о предметной области: смена Задачи = новый файл в core/domains/
и одна строка в .env.
API
| Метод | Путь | Что делает |
|---|---|---|
| GET | /api/health | режим работы, модель, активный домен |
| GET | /api/options | словари формы из каталога: города, категории, форматы, языки |
| GET | /api/samples | готовые демо-запросы активного домена |
| POST | /api/runs {task, input} | запустить подбор: task - запрос, собранный формой, input - пусто |
| GET | /api/runs/<id> | статус и все шаги (интерфейс опрашивает раз в секунду) |
| POST | /api/runs/<id>/approve {approve: bool} | решение человека по действию, меняющему внешнюю систему; в этой Задаче таких действий нет, эндпоинт остаётся частью ядра |
Контракт продублирован в frontend/lib/types.ts - при изменении views.py правится в том же коммите.
Используемые технологии
- Backend: Python 3.12+, Django 5.2+ (без DRF), SQLite; gunicorn в контейнере.
- Frontend: Next.js 16 (App Router), React 19, TypeScript 5 в режиме
strict, react-markdown; без UI-библиотек. - Модель: любая с поддержкой tool calling через OpenAI-совместимый API.
- Запуск: Docker Compose либо локальные venv + npm.
Инструкции по установке
Вариант 1 (рекомендуемый) - Docker
git clone https://github.com/BAITC-Hacks/hack-769bc54e-and.git
cd hack-769bc54e-and
docker compose up --build
Файл .env не нужен: без ключа приложение поднимается в mock-режиме (см. «Параметры окружения»).
Вариант 2 - локально
git clone https://github.com/BAITC-Hacks/hack-769bc54e-and.git
cd hack-769bc54e-and
python scripts/dev.py setup # или, если есть make: make setup
Создаёт .env из .env.example, ставит виртуальное окружение и зависимости бэкенда,
применяет миграции и ставит зависимости фронтенда. scripts/dev.py использует только
стандартную библиотеку и работает на Windows, macOS и Linux - make ставить не нужно.
То же самое вручную:
cp .env.example .env
python -m venv backend/.venv
backend/.venv/bin/python -m pip install -r backend/requirements.txt # Windows: backend/.venv/Scripts/python.exe
backend/.venv/bin/python backend/manage.py migrate
cd frontend && npm install
Инструкции по запуску
Docker
docker compose up --build
- интерфейс - http://localhost:3000
- API - http://localhost:8000/api/health
Локально (два терминала)
python scripts/dev.py backend # терминал 1 -> http://localhost:8000
python scripts/dev.py frontend # терминал 2 -> http://localhost:3000
С make - короче: make backend, make frontend.
Если порт занят
bind: address already in use на 3000 или 8000 означает, что порт держит другой процесс,
а не что проект сломан. Найти и освободить:
# Linux / macOS
lsof -ti tcp:3000 | xargs kill
# Windows PowerShell
Get-NetTCPConnection -LocalPort 3000 -State Listen | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }
Либо запустить на других портах: docker compose up с ports: ["3001:3000"], тогда
NEXT_PUBLIC_API_URL должен указывать на фактический адрес бэкенда.
Необходимые зависимости
- Docker 24+ и Docker Compose v2.24+ - для варианта 1. Более старый Compose не понимает
необязательный
env_fileи без.envне запустится. Docker Desktop должен быть запущен. - Python 3.12+ (команда
pythonилиpython3) и Node.js 22+ - для варианта 2. - Python-пакеты -
backend/requirements.txt, Node-пакеты -frontend/package.json. - Доступ в интернет нужен для работы с настоящей моделью и для веб-шрифтов Google Fonts; без сети интерфейс переходит на системный шрифт. Основной сценарий проверяется без интернета и без ключей.
Параметры окружения
Все параметры задаются в .env в корне (шаблон - .env.example). Переменные не обязательны:
без .env используются значения по умолчанию.
| Переменная | По умолчанию | Назначение |
|---|---|---|
AGENT_DOMAIN | contractors | имя модуля предметной области из backend/agent/core/domains/ |
OPENAI_API_KEY | пусто | ключ провайдера. Пусто → автоматически включается mock-режим |
OPENAI_BASE_URL | пусто | пусто = api.openai.com. Любой OpenAI-совместимый endpoint |
OPENAI_MODEL | пусто | модель с поддержкой tool calling. Пусто → mock-режим |
LLM_MOCK | 0 | 1 - заскриптованная «модель» без сети. Включается сам, если пуст OPENAI_API_KEY или OPENAI_MODEL |
AGENT_MAX_STEPS | 12 | предел вызовов модели за один запуск |
DJANGO_SECRET_KEY | dev-only-change-me | ключ Django |
DJANGO_DEBUG | 1 | режим отладки |
NEXT_PUBLIC_API_URL | http://localhost:8000 | адрес API для фронтенда |
Проверка без наших учётных записей (п. 5.6.6 Положения). При пустом OPENAI_API_KEY
приложение работает в mock-режиме: настоящая модель заменена заскриптованным сценарием,
но отбор, порядок карточек, диагностика, вызовы инструментов и журнал - настоящие, потому
что их делает код, а не модель. Объяснения в карточках в этом режиме собирает код из
посчитанных фактов - в том же формате, что пишет модель: ведущий факт, цена относительно
бюджета, форматы, языки. Личные аккаунты и платные подписки участников для проверки не требуются.
Чтобы увидеть работу с настоящей моделью, впишите в .env OPENAI_API_KEY и OPENAI_MODEL,
поставьте LLM_MOCK=0 и перезапустите бэкенд. Перед этим стоит проверить провайдера:
python scripts/dev.py llm
Команда покажет endpoint, список доступных моделей и сделает один пробный вызов с
tool calling - именно на нём держится агент, и поддерживают его не все модели.
Смена провайдера - одна строка OPENAI_BASE_URL, код не меняется.
Это же место позволяет подключить свою модель на своём GPU: NIM-контейнер или vLLM
отдают OpenAI-совместимый /v1, поэтому достаточно указать адрес инстанса. Решение не
привязано к вендору - один и тот же агент работает на облачном API и на self-hosted модели.
При self-hosted не оставляйте OPENAI_API_KEY пустым: пустой ключ включает mock-режим,
подойдёт любая непустая строка.
Порядок проверки основного сценария работы
Автоматически
При запущенном приложении:
python scripts/verify.py # или: python scripts/dev.py verify
Скрипт (только стандартная библиотека Python) проходит весь путь: /api/health →
примеры → запуск подбора → готовый ответ. Печатает OK и возвращает код 0, если
сценарий пройден.
Ожидаемый вывод:
health ok домен=contractors, mock (без ключа)
samples ok 5 шт., берём «Ведущий на свадьбу, 15 октября»
run ok id=...
report ok шагов: 4 (thought, tool_call, tool_result, final)
note подтверждение человека в этом сценарии не потребовалось
OK: основной сценарий пройден
Тот же скрипт проверяет публичную версию, там вместо mock в первой строке будет
модель gpt-5.4-mini:
python scripts/verify.py --api https://backend-production-93859.up.railway.app
Как выглядят объяснения настоящей модели
Проверка без ключа показывает объяснения, собранные кодом. Тексты, которые пишет модель
на тех же запросах, сохранены дословно в docs/REAL_EXAMPLES.md:
пять исходов, gpt-5.4-mini, с датой и временем прогона. Их можно сверить с JSON
инструмента, который лежит рядом в журнале.
Вручную, в браузере
- Открыть http://localhost:3000 или публичную версию https://frontend-production-ad7c4.up.railway.app.
- Заполнить пять обязательных полей и нажать «Подобрать подрядчиков». Пока поле не заполнено, кнопка неактивна, а под ней написано, чего не хватает.
- Справа появятся плашка исхода и карточки; под ними свёрнутый журнал шагов агента, он раскрывается по нажатию на «Как получен результат».
Запросы, на которых видны все исходы (формат везде - свадьба, кроме последнего):
| Категория | Город | Дата | Бюджет, ₸ | Что увидите |
|---|---|---|---|---|
| Ведущий | Алматы | 15.10.2026 | 1 000 000 | «Полная выдача»: проходят 5 из 10, показаны 3 лучших - здесь видно ранжирование; объяснения и плашки честности |
| Ведущий | Алматы | 26.12.2026 | 1 000 000 | «Меньше трёх»: одна карточка, заняты 9 из 10, «Почему» и «Что можно сделать» - тот же запрос, другая дата |
| Инструменталист | Алматы | 15.10.2026 | 1 000 000 | редкая категория: все 3 профиля в выдаче |
| Отель | Алматы | 26.12.2026 | 3 000 000 | «Никто не подошёл»: причины отсева, подсказка и сезонная заметка про декабрь |
| Лайв-бэнд | Астана | 20.11.2026 | 1 500 000 | «Нет в каталоге»: в Астане такой категории нет, подсказка - где она есть; формат - корпоратив |
После выдачи доступны две кнопки. «Скрыть имена» убирает имена, категории, цены и цитаты, а упоминания имени в тексте заменяет многоточием: карточки должно быть можно различить по одним объяснениям (требование ТЗ к объяснениям). «А если другая дата?» показывает выдачу на вторую дату рядом с первой и отмечает, кто есть только на одной из них, - например, «Ведущий» на 15.10 и 26.12.
Повторный запуск того же запроса даёт тот же порядок карточек. Ответ с настоящей моделью
укладывается в 10 секунд (см. docs/EVAL.md), в mock-режиме - мгновенно.
Тесты
python scripts/dev.py check # тесты Django + tsc --noEmit
python scripts/dev.py eval # прогон кейсов, отчёт в docs/EVAL.md
docs/EVAL.md - отчёт о прогоне качества: какие проверки выполняются и что они дали
на кейсах из backend/agent/evals/cases.contractors.json. Кейсов 10, по требованиям
Definition of Done; последний прогон на настоящей модели - 10 из 10, все ответы быстрее
ориентира 10 с. Отчёт показывает качество на наборе примеров, а не на одном удачном прогоне.
Принятые допущения
Ответов от организаторов на уточняющие вопросы не будет, поэтому мы работали по тексту ТЗ
и двум присланным файлам. В двенадцати местах ТЗ допускает больше одного прочтения, и
каждое из них мы закрыли решением, а не вопросом. Ниже - что решили, почему и чем
это проверяется. Тесты лежат в backend/agent/tests.py и запускаются командой
python scripts/dev.py check.
Меняют поведение отбора
1. Бюджет - потолок на одного подрядчика.
Поле называется price_from_kzt, «цена от, ₸ за мероприятие», а вход по ТЗ - «бюджет (₸)»
в одном ряду с категорией, то есть на одну позицию. Фильтр: price_from ≤ бюджет.
Проверка: test_diagnosis_names_the_budget_that_would_help.
2. Детерминизм обязателен для порядка, текст объяснений может отличаться.
Требование 5 говорит про порядок карточек. Порядок задаёт код и от модели не зависит
вообще; формулировки пишет LLM, и между запусками они могут разниться.
Проверка: test_order_is_deterministic - пять прогонов, один порядок.
3. Язык - жёсткий фильтр, но только когда он указан.
В ТЗ язык опционален. Указан - подрядчик обязан его заявить: клиент, которому нужен
казахский, не обрадуется ведущему без казахского. Не указан - фильтр не применяется,
но широта языков даёт небольшой плюс в скоринге.
Последствие: запрос с казахским отсекает 42 профиля из 66. Это намеренно.
Длительность работает так же: указана - отсекает тех, у кого max_hours меньше запрошенного;
профили без часов проходят (допущение 7). В ТЗ (требование 6) названы только занятость,
бюджет и формат, язык и часы отсекают жёстко по нашему решению.
4. Не проходящий по бюджету в карточки не попадает, но виден среди отсеянных.
Требование 3 запрещает показывать занятых на дату. Про бюджет такого запрета нет, но
карточка - это рекомендация, а рекомендовать то, что клиенту не по карману, нечестно.
Поэтому такие кандидаты уходят в список отсеянных (rejected в ответе поиска) с точной
причиной и процентом превышения, а подсказки (suggestions) называют минимальный
достаточный бюджет.
Краевые случаи
5. Дата вне окна 23.09–31.12.2026 - предупреждение, а не отказ.
Календарь занятости в датасете ограничен этим окном. Вне его мы не знаем, кто занят,
и молча считать всех свободными значило бы соврать. Поиск отрабатывает, но ответ
содержит warning о том, что данных о занятости на эту дату нет, и интерфейс его показывает.
Проверка: test_date_outside_the_calendar_window_is_flagged.
6. «Зарубежье» - отдельный город, в выдачу по Алматы и Астане не подмешивается. В датасете такой профиль один. Клиент, ищущий подрядчика в Астане, не ждёт зарубежного. Если категории нет в его городе, подсказки честно скажут, где она есть.
7. max_hours = null проходит любой фильтр по длительности.
ТЗ прямо говорит: пусто означает, что работа не привязана к присутствию - флорист,
декоратор, сувениры. Отсекать их по часам значило бы терять правильных кандидатов.
Проверка: test_profile_without_max_hours_survives_duration_filter.
8. Категория обязательна на входе. По легенде клиент уже получил каталог по своей категории, а требование 6 предусматривает исход «в этом городе такой категории нет» - значит, категорию задаёт пользователь. Поиск «по всем категориям сразу» не поддерживается.
Данные и подача
9. Свои синтетические профили не добавляли.
ТЗ это разрешает, но 66 профилей хватает на все сценарии Definition of Done, включая
пустой результат и редкие категории. Добавление своих записей размывает границу
между данными организатора и нашими. Если добавим - только с synthetic: true
и видимой пометкой в карточке; механизм пометки уже работает на 13 синтетических
профилях из исходного датасета.
10. Интерфейс и объяснения - на русском. Описания подрядчиков в датасете русские, работаем в казахстанском контексте. Промпты и описания инструментов - на английском, как и остальной код.
11. Профиль с несколькими категориями полноправен в каждой.
categories - список, фильтр работает по вхождению.
Проверка: test_category_filter_matches_any_of_the_profile_categories.
12. Источник данных - присланный CSV, положенный в репозиторий.
ТЗ упоминает .jsonl как основной формат, но нам прислали .csv и HTML-превью.
Файл лежит в backend/agent/data/contractors.csv: проверяющий разворачивает проект
без доступа к Google Drive и без интернета.
Использованные сторонние компоненты
Раскрытие по п. 5.4.4 Положения.
| Компонент | Лицензия | Роль в проекте |
|---|---|---|
| Django, django-cors-headers | BSD-3-Clause | HTTP-слой и ORM |
| gunicorn | MIT | запуск бэкенда в контейнере |
| python-dotenv | BSD-3-Clause | чтение .env |
| openai (python SDK) | Apache-2.0 | доступ к модели |
| Next.js, React, react-dom | MIT | интерфейс |
| react-markdown | MIT | отображение объяснений модели |
| TypeScript | Apache-2.0 | типизация фронтенда |
| Golos Text, JetBrains Mono (Google Fonts) | OFL-1.1 | шрифты |
Базовые образы Docker python:3.12-slim, node:22-slim | PSF-2.0, MIT | контейнеры бэкенда и фронтенда |
| Датасет event-подрядчиков, 66 профилей | предоставлен организатором вместе с ТЗ Задачи #79-lite | каталог для подбора, backend/agent/data/contractors.csv; не изменялся, наблюдения - docs/DATASET_NOTES.md |
| Языковая модель OpenAI через API (в последнем прогоне - gpt-5.4-mini) | условия провайдера | вызов инструмента и тексты объяснений; не дообучалась |
Готовых чужих решений в проекте нет. Модель не дообучалась: ТЗ это исключает.
Что было готово до начала соревновательной части
Раскрытие по п. 5.4.4.2 и 5.4.5 Положения.
До 23.09.2026, 13:00 командой самостоятельно был подготовлен технический шаблон: каркас Django + Next.js, цикл агента, реестр инструментов, механизм подтверждения человеком, журнал шагов, mock-режим, Docker-сборка и тесты. Шаблон не содержит функциональности, относящейся к Задаче хакатона, и не является готовым продуктом.
Функциональность, решающая Задачу, целиком разработана в соревновательной части:
домен подбора backend/agent/core/domains/contractors.py, его тесты в backend/agent/tests.py
и кейсы backend/agent/evals/cases.contractors.json, эндпоинт /api/options в
backend/agent/views.py, форма запроса в frontend/app/page.tsx, исход и карточки в
frontend/components/Results.tsx и frontend/lib/results.ts, а также SCOPE.md,
TASKS.md и docs/DATASET_NOTES.md. Датасет получен от организатора в день хакатона.
История разработки - в коммитах этого репозитория.
Использование AI-инструментов
Разрешено п. 5.4.12 Положения. Команда пользовалась тремя инструментами:
- Claude Code - написание и ревью кода, тесты, документация. Коммиты, сделанные с
его помощью, помечены в истории строкой
Co-Authored-By: Claude. - Codex (OpenAI) - генерация и рефакторинг кода.
- ChatGPT - разбор ТЗ и тексты.
Правила для кодинг-агентов - в AGENTS.md: тест на каждое изменение отбора, зелёный
python scripts/dev.py check перед коммитом, неизменная архитектура «отбор - код,
объяснения - модель». Внутри самого продукта модель вызывается только через
backend/agent/core/llm.py.
Команда и вклад участников
Вклад каждого виден в истории коммитов: имя в первом столбце совпадает с автором в git.
| Участник | GitHub | Что делал |
|---|---|---|
| Damir Doskulov (капитан) | @dsklff | разбор Задачи и план; домен подбора - фильтры, скоринг, диагностика, подсказки; тесты и eval, прогон на живой модели; SCOPE.md, TASKS.md |
| Nurbek Gabdullin | @nurbekgabdullin | форма запроса, /api/options со словарями каталога, плашки честности в карточках |
| Amantur Amangeldiyev | @amangeldiyev | карточки подбора, три исхода и диагностика в интерфейсе, правки формы; допущения в README, заметки о датасете, почасовой журнал |
Известные упрощения
Заявлены честно, чтобы эксперт не тратил время на их поиск:
- фоновые потоки вместо очереди задач (для продакшена - Celery/RQ, интерфейс тот же);
- SQLite вместо полноценной СУБД;
- нет авторизации, CORS открыт,
ALLOWED_HOSTS = ["*"]- конфигурация для демонстрации; - релевантность описания считается лексически, по основам слов, а не эмбеддингами: так отбор работает без сети и ключей;
- каталог кэшируется при первом обращении - после правки CSV нужен перезапуск бэкенда;
- данные о занятости есть только на окно 23.09–31.12.2026;
- веса скоринга подобраны вручную и зафиксированы в
WEIGHTS; - длина объяснения (1–2 предложения) задана промптом и проверяется прогоном, а не кодом: в редких случаях модель пишет три предложения.
Подробнее - в SCOPE.md, раздел «Известные ограничения».
Внутренние документы
TASKS.md - доска задач команды · CLAUDE.md - разбор Задачи ·
SCOPE.md - границы решения и известные ограничения ·
AGENTS.md - правила разработки · docs/POLOZHENIE.md - Положение хакатона ·
docs/HOURLY.md - почасовой журнал работы · docs/EVAL.md - отчёт о прогоне качества: что проверяется и результаты · docs/TEAM_WORKFLOW.md - как работаем втроём ·
docs/DEMO_CHECKLIST.md - сценарий демонстрации · docs/PITCH.md - заготовка питча ·
docs/DATASET_NOTES.md - что мы нашли в датасете ·
docs/W_REVIEW.md - проверка W1–W8 и сценарии воспроизведения.
Критерии оценки → где это в решении
Пять критериев технического отбора Задачи и где эксперт найдёт каждый: в коде, в демо и в проверках. Запросы из столбца «В демо» - из раздела «Порядок проверки основного сценария работы».
| Критерий | В коде | В демо | Чем проверено |
|---|---|---|---|
| Соответствие задаче и работоспособность (25) | вход и жёсткие фильтры - search_contractors, _reasons в contractors.py; не больше трёх карточек - MAX_CARDS; исходы - поле outcome; интерфейс - Results.tsx | пять запросов из таблицы проверки: полная выдача, меньше трёх, редкая категория, никто не подошёл, нет в каталоге; «А если другая дата?» - две выдачи рядом | test_never_more_than_three_cards, test_busy_contractor_is_excluded, test_three_outcomes_are_distinguishable, test_two_dates_give_different_results в tests.py; python scripts/verify.py → OK |
| Техническая реализация, AI и agentic AI (25) | цикл агента - loop.py, реестр инструментов - tools.py, единственный вызов модели - llm.py; модель сама вызывает search_contractors и diagnose_request, а объяснения пишет по формату REPORT_FORMAT; порядок задаёт код - _score, WEIGHTS | журнал под результатом: рассуждение → вызов инструмента → результат → ответ; повторный запуск даёт тот же порядок | test_order_is_deterministic, test_equal_scores_are_ordered_by_meaning_not_by_id, test_ranking_uses_the_human_text_not_the_model_argument; 10 из 10 кейсов на настоящей модели - EVAL.md |
| README и воспроизводимость (25) | датасет в репозитории - contractors.csv; mock-режим без ключей - LLM_MOCK в llm.py; команды без make - dev.py; сквозная проверка - verify.py | docker compose up --build из чистого клона без .env, затем python scripts/verify.py | python scripts/dev.py check: тесты Django и tsc --noEmit; двенадцать допущений с тестами - раздел «Принятые допущения» |
| Ценность и применимость (15) | факты для объяснения - _score; цитата-доказательство - _quote; чем кандидат отличается от соседей - _standouts; что изменить в запросе - _suggestions, _nearby_dates, _budget_needed; сезон - _season_note; плашки честности - flags и HonestyBadges в Results.tsx | «Ведущий, 15.10»: у каждой карточки своё объяснение - цена относительно бюджета, форматы, языки - и цитата из описания; «Отель, 26.12»: почему никого нет и что сделать; «Скрыть имена» - карточки различимы по одним объяснениям | test_quote_is_always_a_real_fragment_of_the_description, test_cards_carry_comparative_facts, test_diagnosis_names_the_budget_that_would_help, test_suggestions_never_offer_what_is_already_shown, test_cards_carry_honesty_flags |
| Потенциал развития и оригинальность (10) | диагностика «сдвиньте одно условие» - _diagnose, _relaxed; смена провайдера и self-hosted модель - одна строка OPENAI_BASE_URL (раздел «Параметры окружения»); новая Задача - новый файл в domains/ | «Ведущий, 26.12» против «15.10»: одна карточка вместо трёх, подсказка перенести дату и сезонная заметка | test_diagnosis_reports_when_no_single_change_helps, test_exhausted_rare_category_says_so_instead_of_advising, test_availability_note_differs_between_dates |
Куда развивать. Релевантность описания через эмбеддинги вместо основ слов - ТЗ это разрешает, и отбор останется детерминированным, если посчитать векторы заранее. Подбор комплекта одного уровня: ведущий, фотограф и зал под общий бюджет. Для этого подбор по одной категории уже даёт всё нужное - факты, диагностику и честные исходы.
Авторы коммитов
История коммитов
- 2026-09-23 17:53docs: C9 — видео переснято с публичной версии и живой модельюamangeldiyev
- 2026-09-23 17:49час 5: финальное ревью, публичная версия на Railway, ТойМатч, демо и сверка чек-листаamangeldiyev
- 2026-09-23 17:37Correct participant name in README.mdNurbek Gabdullin
- 2026-09-23 17:36docs: C10 — публичная версия передеплоена на 5717e44amangeldiyev
- 2026-09-23 17:31docs: короткое тире в абзаце про публичную версиюDamir Doskulov
- 2026-09-23 17:31docs: ТойМатч - новое имя и слоган, README сверен и без длинных тиреDamir Doskulov
- 2026-09-23 17:25docs: C10–C11 — публичная версия на Railway, ссылка в READMEamangeldiyev
- 2026-09-23 17:21fix: финальное ревью — имена скрыты целиком, ранжирование в демо, цитаты, документыDamir Doskulov
Все коммиты (69)
- 2026-09-23 16:53fix: show every rejected contractor with all reasons (W3)Nurbek Gabdullin
- 2026-09-23 16:53fix: keep date comparison tied to its submitted request (W2)Nurbek Gabdullin
- 2026-09-23 16:53fix: preserve verbatim evidence quotes (W5)Nurbek Gabdullin
- 2026-09-23 16:53feat: explain honesty badges and verify W acceptance scenarios (W6)Nurbek Gabdullin
- 2026-09-23 16:48docs: C9 — видео-демо записано, осталось выложить по ссылкеamangeldiyev
- 2026-09-23 16:48docs: README — как выглядят объяснения без ключа после 55336adamangeldiyev
- 2026-09-23 16:47docs: C7 — сценарий демо на 2 минуты под нашу Задачу, прогон по таймеруamangeldiyev
- 2026-09-23 16:47docs: C8–C12 — чистый клон, сверка чек-листа, деплой снятamangeldiyev
- 2026-09-23 16:46feat: обязательные поля формы помечены звёздочкойDamir Doskulov
- 2026-09-23 16:38fix: полное ревью после слияния — занятость на экране, объяснения без ключаDamir Doskulov
- 2026-09-23 16:25час 4: B6–B10, ревью домена, README под проверку экспертом, чистый клон с Docker без ключейamangeldiyev
- 2026-09-23 16:24fix: третье ревью — выделения по делу, цитата у всех, готовая первая строкаDamir Doskulov
- 2026-09-23 16:20docs: README сразу отвечает на четыре вопроса эксперта и запускается на macOSamangeldiyev
- 2026-09-23 16:17feat: source options from domain vocabularyNurbek Gabdullin
- 2026-09-23 16:17feat: show collapsed rejected candidatesNurbek Gabdullin
- 2026-09-23 16:17fix: render diagnosis hints verbatimNurbek Gabdullin
- 2026-09-23 16:17feat: show response time with resultsNurbek Gabdullin
- 2026-09-23 16:17chore: verify strict frontend typesNurbek Gabdullin
- 2026-09-23 16:16chore: подтверждён допуск — чистый клон без ключей проходит сценарийDamir Doskulov
- 2026-09-23 16:11fix: второе ревью — уникальные выделения, чистый сигнал, ответ без ключаDamir Doskulov
- 2026-09-23 16:09docs: C3 — README заполнен и описывает наш продукт, а не шаблонamangeldiyev
- 2026-09-23 16:09docs: C4 — таблица «критерий → где в коде, в демо и чем проверено»amangeldiyev
- 2026-09-23 16:09docs: C6 снята — синтетические профили для демо не нужныamangeldiyev
- 2026-09-23 16:08docs: C2 — датасет прочитан глазами, заметки в docs/DATASET_NOTES.mdamangeldiyev
- 2026-09-23 16:02feat: прогон проверяет качество объяснений, а не только их наличиеDamir Doskulov
- 2026-09-23 16:00feat: add anonymous explanation modeNurbek Gabdullin
- 2026-09-23 16:00feat: compare contractor results by dateNurbek Gabdullin
- 2026-09-23 15:58час 3: форма и API, карточки, плашки, три исхода, допущения в README, правки по ревьюamangeldiyev
- 2026-09-23 15:55fix: плашки честности не приписывают команде данные организатораamangeldiyev
- 2026-09-23 15:53docs: SCOPE.md сверен с кодом после правок по ревьюDamir Doskulov
- 2026-09-23 15:53fix: внешнее ревью — честная диагностика, живые совпадения, сравнимые карточкиDamir Doskulov
- 2026-09-23 15:52docs: C1 — принятые допущения перенесены из SCOPE.md в READMEamangeldiyev
- 2026-09-23 15:46feat: B5 — три исхода различимы, пустого экрана нетamangeldiyev
- 2026-09-23 15:40feat: show contractor honesty badgesNurbek Gabdullin
- 2026-09-23 15:32feat: словарь каталога в схеме инструмента и честная реакция на чужие значенияDamir Doskulov
- 2026-09-23 15:29feat: демо закрывает требование 17, диагностика едет вместе с поискомDamir Doskulov
- 2026-09-23 15:22fix: B1 — форма принимает круглый бюджет и не засоряет подбор подписямиamangeldiyev
- 2026-09-23 15:21feat: B3 — карточки подбора вместо markdown-отчётаamangeldiyev
- 2026-09-23 15:15docs: новая волна задач A по итогам аудита доменаDamir Doskulov
- 2026-09-23 15:14feat: expose contractor catalog optionsNurbek Gabdullin
- 2026-09-23 15:14feat: add structured contractor request formNurbek Gabdullin
- 2026-09-23 15:12feat: B3 — карточки подбора вместо markdown-отчётаamangeldiyev
- 2026-09-23 15:05docs: SCOPE.md с принятыми допущениями, AGENTS.md под нашу ЗадачуDamir Doskulov
- 2026-09-23 15:05B — продукт и интерфейс - B1amangeldiyev
- 2026-09-23 15:03fix: четыре дефекта по итогам ревью доменаDamir Doskulov
- 2026-09-23 14:57chore: сквозной прогон стека на живой моделиDamir Doskulov
- 2026-09-23 14:56perf: замер моделей и гарантия подсказок на детерминированном слоеDamir Doskulov
- 2026-09-23 14:50feat: готовые подсказки «сдвиньте одно условие», сезон и проверка двух датDamir Doskulov
- 2026-09-23 14:45feat: цитата-доказательство и чистка эха запросаDamir Doskulov
- 2026-09-23 14:43feat: ответ в виде карточек и запрет общих фразDamir Doskulov
- 2026-09-23 14:38docs: TASKS.md — единая доска задач командыDamir Doskulov
- 2026-09-23 14:36feat: домен подбора подрядчиков — фильтры, скоринг, диагностикаDamir Doskulov
- 2026-09-23 14:20docs: CLAUDE.md — разбор Задачи #79-lite и план работыDamir Doskulov
- 2026-09-23 14:03Merge remote-tracking branch 'hack/main'Damir Doskulov
- 2026-09-23 14:03chore: CLAUDE.md не попадает в репозиторийDamir Doskulov
- 2026-09-23 13:08docs: путь self-hosted модели (NVIDIA Brev) и ловушка с пустым ключомDamir Doskulov
- 2026-09-23 13:01fix: пустой OPENAI_BASE_URL ломал клиента, а промпт — паузу на человекаDamir Doskulov
- 2026-09-23 12:36feat: форма ввода шире — CSV читается без горизонтальной прокруткиDamir Doskulov
- 2026-09-23 12:32feat: интерфейс под демо — время шагов, итог цифрами, фокус на журналеDamir Doskulov
- 2026-09-23 12:17fix: убран неиспользуемый листинг запусков и устаревшие подсказкиDamir Doskulov
- 2026-09-23 11:13chore: стартовый шаблон agentic-приложения для HackAlem AIDamir Doskulov
Структура репозитория (69 файлов)
.env.example .gitignore AGENTS.md CLAUDE.md Makefile README.md SCOPE.md TASKS.md backend/.dockerignore backend/Dockerfile backend/agent/__init__.py backend/agent/apps.py backend/agent/core/__init__.py backend/agent/core/domains/__init__.py backend/agent/core/domains/_contract.py backend/agent/core/domains/contractors.py backend/agent/core/domains/example.py backend/agent/core/llm.py backend/agent/core/loop.py backend/agent/core/prompts.py backend/agent/core/tools.py backend/agent/data/contractors.csv backend/agent/evals/__init__.py backend/agent/evals/cases.contractors.json backend/agent/evals/cases.json backend/agent/evals/run.py backend/agent/migrations/0001_initial.py backend/agent/migrations/0002_run_completion_tokens_run_prompt_tokens.py backend/agent/migrations/__init__.py backend/agent/models.py backend/agent/tests.py backend/agent/urls.py backend/agent/views.py backend/config/__init__.py backend/config/settings.py backend/config/urls.py backend/config/wsgi.py backend/manage.py backend/requirements.txt docker-compose.yml docs/DATASET_NOTES.md docs/DEMO_CHECKLIST.md docs/EVAL.md docs/HOURLY.md docs/PITCH.md docs/POLOZHENIE.md docs/REAL_EXAMPLES.md docs/TEAM_WORKFLOW.md docs/W_REVIEW.md frontend/.dockerignore frontend/AGENTS.md frontend/Dockerfile frontend/app/globals.css frontend/app/layout.tsx frontend/app/page.tsx frontend/components/Journal.tsx frontend/components/Results.tsx frontend/lib/api.ts frontend/lib/brand.ts frontend/lib/results.ts frontend/lib/types.ts frontend/next-env.d.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/tsconfig.json scripts/check_llm.py scripts/dev.py scripts/verify.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.