AAYMA
RU
Войти

Alem Match

Сервис подбора до трёх подрядчиков под мероприятие (город, дата, формат, категория, бюджет) с детерминированным ранжированием и проверяемыми…

Рабочий MVP
Команда «NeuroX» 8💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: NeuroX
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP

Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор

Суть

Сервис подбора до трёх подрядчиков под мероприятие (город, дата, формат, категория, бюджет) с детерминированным ранжированием и проверяемыми объяснениями различий.

Как устроено

66 профилей из CSV организаторов → JSONL; жёсткие фильтры (занятость → формат → бюджет → язык → длительность) с кодами NO_CATEGORY_IN_CITY/NONE_PASS_CONDITIONS → скоринг: budget_fit (пик при 75% бюджета), semantic_fit по косинусу TF-IDF, language_fit и hours_fit с весами 0.30/0.50/0.10/0.10 → top-3. Факты для объяснений извлекаются правилами (дословная цитата, разница цен), генератор собирает текст, critic проверяет грамматику и числа, результат кэшируется по SHA-256. Опционально OpenAI выбирает порядок предложений и строит эмбеддинги.

Ключевые решения

  • TF-IDF
  • косинусная близость
  • взвешенный скоринг
  • generator-critic
  • SHA-256 кэш
  • подсказки по соседним датам и бюджету

Метрики

Офлайн-eval: 918 запросов, 265 объяснений, 100% прохождения critic, 0% стоп-фраз, 100% привязки к запросу; p95 5,351 мс; 31 тест

Особенности

LLM не получает доступа к скорингу и не может добавить новые числа: он только выбирает порядок заранее проверенных предложений, а critic отклоняет свободные утверждения. Полностью офлайн по умолчанию, подсказки объясняют пустую выдачу числами.

Стек

Python · FastAPI · Pydantic · Uvicorn · SQLite · PyYAML · pytest · HTML/JS · Docker

Модели

GPT-4o-mini (опционально) · OpenAI text-embedding-3-small (опционально)


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-8f409c84-neurox.

README

README.md

Alem Match

2. Краткое описание

Умный подбор подрядчиков с проверяемыми объяснениями. Проект для HackAlem AI, кейс «Умный подбор подрядчиков (#79-lite)».

Заказчик задаёт город, дату, формат, категорию и бюджет. Сервис возвращает максимум три подходящих профиля и объясняет различия: начальную цену, остаток бюджета, условия работы и конкретную деталь описания. Занятые подрядчики всегда исключаются.

Интерфейс Alem Match

3. Что реализовано

  • 66 профилей из предоставленного организаторами CSV, преобразованных в JSONL без изменения цен и календарей.
  • Строгие фильтры; язык и длительность учитываются только при заполнении.
  • До трёх карточек, отдельные бейджи синтетики и пометки о восстановленных данных.
  • Три различимых результата, числовое объяснение пустой и короткой выдачи.
  • Детерминированное ранжирование, локальные векторы, воспроизводимые объяснения и кэш.
  • Подсказки по соседним датам, достаточному бюджету и городам; сравнение двух дат.
  • FastAPI, CLI, адаптивный интерфейс без сборки, раскрываемый trace.
  • 31 автоматический тест, сценарии демо, офлайн-eval, GitHub Actions.
  • Dockerfile и Compose для воспроизводимого запуска; Docker-сборка локально не проверялась.

4. Как работает решение

flowchart TD
    A[Город, дата, формат, категория, бюджет] --> B[Валидация по метаданным]
    B --> C[Город + категория]
    C -->|0 профилей| D[NO_CATEGORY_IN_CITY]
    C --> E[Занят → формат → бюджет → язык → длительность]
    E -->|0 подходящих| F[NONE_PASS_CONDITIONS]
    E --> G[Локальный скоринг]
    G --> H[Top-3: score ↓, price ↑, id ↑]
    H --> I[Факты + различия между карточками]
    I --> J[Генератор → проверяющий → кэш]
    J --> K[MATCHED + объяснения + подсказки + trace]

MATCHED — «Подобрали N». Если прошедших меньше трёх, показываются все; summary сообщает размер исходной группы и основные причины исключения с числами.

NO_CATEGORY_IN_CITY — «В городе нет этой категории». Нет ни одного профиля запрошенной категории в выбранном городе. Отдельно указываются города, где категория есть.

NONE_PASS_CONDITIONS — «Никто не проходит по условиям». Кандидаты есть, но не проходят фильтры. Каждый исключённый учитывается один раз по порядку: занят → формат → бюджет → язык → длительность. Все дополнительные причины остаются в trace.

Занятость отражена в summary каждого ответа. Декабрьская высокая загрузка объясняется числами. Дата за пределами 23.09–31.12.2026 и неверные параметры дают HTTP 422 / INVALID_INPUT с допустимыми значениями.

Объяснения

Шесть ориентиров: конкретность, привязка к запросу, различимость, обоснованность, честное сравнение, 1–2 предложения.

Факты извлекаются правилами: цена и разницы вычисляются, цитата берётся дословно из описания. Имена и короткие представления исключаются из отличительных деталей. У карточки есть уникальная цитата либо отличающаяся цена; более дорогой вариант явно сравнивается с самым недорогим в показанной тройке.

Офлайн-генератор составляет два предложения. Опциональный OpenAI-провайдер выбирает порядок этих предложений сразу для всех карточек. Critic проверяет закрытую грамматику, стоп-фразы, форму, параметры и отличительные факты; произвольные утверждения и новые числа не принимаются. Некорректный план запрашивается повторно один раз, сетевой сбой переводит запрос на шаблонный вариант. Это ограниченный планировщик текста, а не свободный LLM-копирайтер.

Ключ кэша — SHA-256 нормализованного запроса, фактов и версии генератора. Демо-кэш хранится в data, новые ответы — в локальной SQLite-базе .cache. Удаление локального кэша может изменить порядок предложений в API-режиме; порядок карточек останется прежним.

Подсказки не добавляют карточек: например, кандидат, исключённый только по бюджету, появился бы при указанной минимальной сумме. Дата предлагается лишь после повторной проверки остальных условий. Непоказанные подрядчики в подсказках не называются.

Офлайн-оценка: 918 запросов, 265 объяснений, 100% прохождения critic, 0% стоп-фраз, 100% привязки к запросу, 100% автоматического сопоставления без имён. p95 последнего замера — 5,351 мс без времени запуска процесса. Это автоматические метрики на данном датасете, не независимая оценка качества человеком и не результат живого API.

5. Технологии

Python 3.11+, FastAPI, Pydantic, Uvicorn, httpx, PyYAML, SQLite, pytest, HTML/CSS/JavaScript. Проект разработан с помощью AI-агента Codex.

Режим по умолчанию: полностью офлайн, TF-IDF и косинусная близость. Векторы предвычислены и включены в репозиторий.

Необязательные интеграции: OpenAI text-embedding-3-small для явной пересборки векторов и gpt-4o-mini для structured-output плана объяснений. Ключ не нужен для запуска. Реальные вызовы API в этой поставке не выполнялись; подробности и официальная документация.

6. Архитектура проекта

app/
  api.py, cli.py       HTTP и консольный интерфейс
  pipeline.py         валидация, фильтры, выдача, подсказки, сравнение
  data.py, models.py  строгие модели и загрузка
  filters.py          порядок основных причин
  scoring.py          локальные векторы и детерминированный score
  facts.py            дословные детали и вычисленные контрасты
  explain/            шаблоны, critic, генератор, кэш
  llm/                интерфейсы и Offline/OpenAI-провайдеры
data/                 исходный JSONL, факты, векторы и демо-кэш
scripts/              импорт, инспекция, подготовка векторов, demo, eval
static/index.html     UI без CDN и frontend-сборки
tests/                контракт, объяснения, честность, API, кэш
docs/                 данные, метрики, демо, соответствие и pitch

Веса в config.yaml. Пусть r = price / budget:

  • budget_fit = max(0, 1 − |r − 0,75| / 0,75): максимум при использовании 75% бюджета; дешевле не всегда лучше.
  • semantic_fit = cosine(description_vector, event × category × optional_language).
  • language_fit = 1 для совпавшего заданного языка.
  • hours_fit = min(1, max_hours / hours), либо 1 при max_hours=null.
  • Итог — взвешенное среднее активных компонентов минус 0,015 за каждый флаг price_imputed / city_imputed. Веса: 0,30 / 0,50 / 0,10 / 0,10.

Незаданные опциональные компоненты удаляются вместе с их весами. После жёстких фильтров язык и часы дают нейтральный балл прошедшим; несуществующий запрос не штрафуется. Итог округляется до 4 знаков, сортируется по score ↓, цене ↑, строковому id ↑. LLM к скорингу доступа не имеет.

Векторный кэш проверяется по хэшу каталога. Если кэш не соответствует данным или нет записи, используется TF-IDF. Подробная схема.

7. Установка и запуск

Нужен установленный Python 3.11 или новее. Перейдите в корень проекта.

Windows PowerShell:

py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m uvicorn app.api:app --host 127.0.0.1 --port 8000

Linux / macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m uvicorn app.api:app --host 127.0.0.1 --port 8000

Откройте интерфейс или Swagger API. Без .env всё работает офлайн.

Для OpenAI скопируйте .env.example в .env, заполните OPENAI_API_KEY и установите LLM_PROVIDER=openai. Не добавляйте .env в Git. Запуск сервера прежний. Для замены TF-IDF на настоящие OpenAI-векторы явно выполните:

python -m scripts.build_embeddings --provider openai

Перезапустите сервер после изменения данных или векторов. Этот шаг отправляет анонимизированные описания в API и расходует квоту. Возврат к локальным векторам: python -m scripts.build_embeddings --provider tfidf.

Docker (не требует локального Python):

docker compose up --build

Compose по умолчанию запускается офлайн на порту 8000.

CLI:

python -m app.cli --city Алматы --date 2026-10-01 --event свадьба --category Фотограф --budget 1000000 --json
python -m app.cli --city Астана --date 2026-11-14 --event корпоратив --category Фотограф --budget 200000 --hours 5 --lang казахский --no-team-synthetic

На Windows вместо python можно использовать ..venv\Scripts\python.exe. Без --json CLI печатает summary, карточки, hints и trace. Неверный ввод завершает CLI кодом 2.

API: GET /health, GET /meta, POST /recommend, POST /compare-dates. Пример тела /recommend:

{"city":"Алматы","date":"2026-10-01","event":"свадьба","category":"Фотограф","budget":1000000,"hours":null,"lang":null}

Для /compare-dates добавьте поле "second_date":"2026-10-02".

Публикация на GitHub: загрузите содержимое папки проекта в репозиторий команды, выданный платформой. Если репозиторий уже существует, сначала клонируйте его и скопируйте файлы проекта в рабочую папку, сохранив его .git. Затем:

git add .
git commit -m "Add Alem Match hackathon solution"
git push

Архив не содержит ключей, виртуального окружения и локального SQLite-кэша. Адрес удалённого репозитория не задан; автоматическая публикация не выполнялась.

8. Как проверить решение

python -m pytest -q
python -m scripts.inspect_data
python -m scripts.demo
python -m scripts.eval_explanations

Последний прогон: 31 passed, одно предупреждение совместимости Starlette/AnyIO без падений. Локальная среда проверки — Python 3.12; GitHub Actions настроен на 3.11 и 3.12, но удалённый workflow ещё не запускался.

demo.py сам находит сценарии в данных:

  1. Плотная категория → MATCHED, 3 карточки из более чем трёх подходящих.
  2. Редкая → MATCHED, одна карточка в найденном сценарии, без дополнения каталога.
  3. Бюджет 1 ₸ → NONE_PASS_CONDITIONS.
  4. Две даты → изменившаяся тройка и объяснение занятости.
  5. Три повтора + новый процесс без ключа → одинаковый SHA-256 списка id.
  6. Отсутствующая категория в городе → NO_CATEGORY_IN_CITY на исходном каталоге.

Полный вывод демо, 10 полных примеров, отчёт проверки, R1–R6 → код → тесты.

Живой OpenAI-eval запускается отдельно и явно: python -m scripts.eval_explanations --live. Требуется ключ; результат сохраняется в docs/EVAL_LIVE.md. Перед ним выполните demo для подготовки запросов.

9. Данные и интеграции

Источник — предоставленный CSV hackathon dataset anonymized .csv: копия data-source.csv и типизированная версия data/hackathon-dataset-anonymized.jsonl. Импорт воспроизводится:

python -m scripts.import_csv data-source.csv

В исходных данных 66 профилей: Алматы — 50, Астана — 15, Зарубежье — 1. Организаторы пометили 13 профилей как synthetic. Без цены — 0; без max_hours — 9; восстановленный город — 8; восстановленная цена — 18. Полный отчёт.

Собственная синтетика не добавлена. При необходимости командные профили загружаются только из data/team_synthetic.jsonl с synthetic=true и source=team, выключаются через --no-team-synthetic в CLI или INCLUDE_TEAM_SYNTHETIC=false в API.

Реальных календарных интеграций нет. Внешние API не используются без явного включения OpenAI. Предоставленные данные не обогащались сетевыми источниками.

10. Ограничения

  • Это рекомендация по статическому датасету: не бронирование, не подтверждение доступности и не коммерческое предложение. Цена указана «от».
  • Календарь ограничен 23.09–31.12.2026.
  • Закоммиченные векторы — TF-IDF. OpenAI-эмбеддинги не сгенерированы; код подготовки есть, требуется ключ.
  • Не реализованы LLM-извлечение фактов и свободный LLM-генератор объяснений. Используются точные фрагменты и проверяемые предложения, LLM может выбрать их порядок.
  • Critic не является универсальным фактчекером произвольной прозы; свободные даты типа «14 ноября» не нормализует, поскольку в сгенерированных объяснениях их нет.
  • Контраст по цене реализован; отдельные превосходные сравнения по всем языкам и запасу часов, а также сравнение силы семантики не реализованы.
  • Не реализован единый critic для подсказок; их условия и числа вычисляются напрямую и проверяются тестами.
  • Живой API-eval, LLM-судья и независимое ручное слепое тестирование не проведены. Docker-сборка и удалённый CI в данной среде не запускались.
  • Не реализованы POST /parse-query, NVIDIA-провайдер и облачный деплой.
  • Нет векторной БД, обучения весов, мультиязычных объяснений, комплектов подрядчиков, реальных календарей, rate limit и аутентификации.
  • Если будущие профили неразличимы по доступным фактам, система отмечает это в trace; гарантировать уникальные объяснения для полностью одинаковых записей нельзя.

План развития и сценарий защиты.

11. Ссылка на deployed-версию

Публичного деплоя пока нет. Локальный адрес после запуска: localhost:8000.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
4
Авторы
2
Файлы
56
Ветки
2

Авторы коммитов

Qasymzhomart 2erassylls 2

История коммитов

  • 2026-09-23 16:17Create start.batQasymzhomart
  • 2026-09-23 16:02Add files via uploadQasymzhomart
  • 2026-09-23 15:14Update README.mderassylls
  • 2026-09-23 15:02Update README.mderassylls
Структура репозитория (56 файлов)
Dockerfile
Makefile
README.md
app/__init__.py
app/api.py
app/cli.py
app/config.py
app/data.py
app/explain/__init__.py
app/explain/critic.py
app/explain/generator.py
app/explain/templates.py
app/facts.py
app/filters.py
app/llm/__init__.py
app/llm/providers.py
app/models.py
app/normalize.py
app/pipeline.py
app/scoring.py
config.yaml
data-source.csv
data/demo_queries.json
data/embeddings_cache.json
data/explanations_cache.json
data/facts_cache.json
data/hackathon-dataset-anonymized.jsonl
docker-compose.yml
docs/ARCHITECTURE.md
docs/COMPLIANCE.md
docs/DATA_REPORT.md
docs/EVAL.md
docs/EXPLANATION_SAMPLES.md
docs/OPENAI.md
docs/PITCH.md
docs/ROADMAP.md
docs/TEST_REPORT.md
docs/demo_output.md
docs/screenshot.png
hack-8f409c84-neurox/README.md
pytest.ini
requirements.txt
scripts/__init__.py
scripts/build_embeddings.py
scripts/demo.py
scripts/eval_explanations.py
scripts/import_csv.py
scripts/inspect_data.py
start.bat
static/index.html
tests/conftest.py
tests/test_api.py
tests/test_cache.py
tests/test_contract.py
tests/test_explanations.py
tests/test_honesty.py
Ветки (2)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.