Contractor Matcher
Подбор до трёх event-подрядчиков: детерминированные фильтры и ранжирование, а два необязательных LLM-слоя на Groq разбирают свободный текст запроса…
О проекте
Команда: Anomaly Detected
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Подбор до трёх event-подрядчиков: детерминированные фильтры и ранжирование, а два необязательных LLM-слоя на Groq разбирают свободный текст запроса и переписывают объяснения. Покрыто 95 тестами, интерфейс на RU/KZ/EN.
Как устроено
FastAPI с эндпоинтом POST /api/match. Python фильтрует по городу, категории, формату, бюджету, дате, языку и длительности и ранжирует по бюджету, уверенности в данных и релевантности. Слой 1 (intent_parser) сопоставляет свободный текст с реальными категориями и языками датасета, результат идёт в ранжирование как мягкий сигнал. Слой 2 (llm_explainer) переписывает текст объяснений уже выбранных карточек; ответ сверяется с id и полями подрядчика, при ошибке используется шаблон. Статусы none_fit и no_category сопровождаются разбивкой причин отказа и воронкой отсева.
Ключевые решения
- constraint-based recommender
- deterministic ranking
- LLM intent parsing
- LLM explanation rewriting with validation
- template fallback
Особенности
Литобзор обосновывает выбор constraint-based рекомендателя. Отдельные тесты проверяют, что AI никогда не меняет состав и порядок карточек. На фронтенде есть демо-режим на mock-ответах, работающий без бэкенда, и сравнение одного запроса на двух датах.
Стек
Python · FastAPI · Pydantic · pytest · Vanilla JavaScript · HTML/CSS
Модели
openai/gpt-oss-120b (Groq)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2cc01208-anomaly-detected.
README
README.mdContractor Matcher
Smart Contractor Matching for Event Vendors
Команда Anomaly Detected · HackAlem AI · Track 06 Creative Industries · кейс «Smart contractor matching» (Firebird)
Детерминированная фильтрация и ранжирование • AI не выбирает подрядчика • Понимание запроса и объяснения — опциональный слой Groq
Video Demo
Ссылки
| Что | Где |
|---|---|
| Репозиторий | [email protected]:BAITC-Hacks/hack-2cc01208-anomaly-detected.git |
| Контракт API | docs/api.md |
| Условие задачи | docs/task.md |
| Литературный обзор | docs/literature-review.md |
Проблема
Площадка-агрегатор event-подрядчиков в Казахстане уже показала заказчику каталог по его городу — но каталог сам по себе не решение, это просто более длинный список. Задача не «найти подрядчиков», а «помочь выбрать из уже найденных»: заказчик не должен вручную сверять бюджет, занятость на дату, формат мероприятия и язык для каждой из десятков карточек подряд.
Решение
Пользователь-заказчик мероприятия вводит параметры (город, дата, тип события, категория подрядчика, бюджет, опционально — язык, длительность и свободный текст запроса) и получает до 3 карточек подрядчиков из каталога, каждая — с конкретным объяснением, почему именно этот подрядчик подходит. Ценность решения — в объяснении, а не в самой сортировке: это прямо прописано в условии задачи.
Главный архитектурный принцип: AI не выбирает победителя. Кто подходит, в каком порядке и с каким счётом — решает детерминированный Python-код. AI (Groq) используется только там, где это не меняет результат: разбор свободного текста в структурированные метки и переписывание текста уже выбранных объяснений.
Почему такая архитектура
Коротко, три шага (полный разбор с APA-цитатами — docs/literature-review.md):
Рекомендации подрядчиков в двусторонних сервис-маркетплейсах — изученная, экономически значимая
задача (Shi, 2024). При тонком датасете без истории взаимодействий (66 профилей, без рейтингов)
знание-based / constraint-based рекомендеры — устоявшийся ответ именно на такой случай: они не
страдают от cold-start и объясняют отказ даже когда решения нет (Burke, 2000; Felfernig & Burke,
2008) — прямое обоснование, почему none_fit/no_category в этом проекте полноправные,
объяснённые статусы API, а не пустой ответ. В домене event-подрядчиков уже есть публикации, которые
считают числовой скор, но не генерируют объяснение (Singh et al., 2020) — ровно то, что кейс просит
исправить. А использование LLM именно для объяснения уже выбранной рекомендации — редкий паттерн:
всего 6 из 232 статей в обзоре 2025 года реально так делают (Said, 2025), и именно поэтому нужна
валидация вывода — LLM-текст без проверки является известным источником галлюцинаций (Ji et al.,
2023), так что Groq здесь никогда не выбирает и не переупорядочивает карточки, только переписывает
уже готовый текст, и этот текст сверяется с реальными полями подрядчика перед показом.
Related Work
| Работа | Кто решает, что подходит | Откуда объяснение | Объясняет ли нулевой результат | Защита от галлюцинаций |
|---|---|---|---|---|
| Burke (2000); Felfernig & Burke (2008) — constraint-based рекомендеры | Символьный решатель ограничений | Формируется из правил | Да, по определению подхода | Не применимо (без LLM) |
| Singh et al. (2020) — event-planning рекомендер | Гибридный контент+соц. скор | Не генерируется | Нет | Не применимо |
| Shi (2024) — матчинг в двусторонних маркетплейсах | Оптимизированная политика по конверсии | Не часть модели | Нет | Не применимо |
| Said (2025) — обзор LLM-объяснений рекомендаций | Обычно отдельная модель | Текст LLM | Почти не рассматривается в обзоре | Не унифицировано между 6 работами |
| Contractor Matcher (этот проект) | Python: детерминированный фильтр + скоринг, всегда | Groq, только текст, только для уже выбранных карточек | Да — none_fit/no_category с разбивкой excluded | Да — валидация по реальным id/полям, fallback на шаблон |
Два AI-слоя: до поиска и после
AI используется в двух разных, не пересекающихся местах пайплайна — и оба спроектированы так, чтобы не менять итог поиска, только качество взаимодействия с ним.
Слой 1 — до поиска (intent_parser.py): понимание запроса, а не поиск по ключевым словам.
Обычный keyword-поиск ищет буквальное совпадение слов. Проблема в том, что реальный пользователь
формулирует запрос не терминами каталога: он может написать «нужен человек, который снимет видео с
мероприятия» и ни разу не произнести слово «видеограф». Поиск по ключевым словам такой запрос не
поймёт — а пользователю нужно ровно это. Этот AI-слой читает свободный текст и сопоставляет его с
реальными категориями/языками/форматами из датасета (никогда не выдумывая новые), примерно так же,
как это сделал бы человек-администратор каталога, читающий заявку. Результат — мягкий сигнал для
ранжирования (или жёсткий фильтр только для явно обязательного языка, «должен говорить на
казахском»), а не решение: Python по-прежнему фильтрует и сортирует сам.
Слой 2 — после поиска (llm_explainer.py): объяснение уже готового результата.
Когда Python уже выбрал до 3 карточек, Groq только переписывает текст объяснения для каждой —
понятным, неповторяющимся языком вместо сухого перечисления полей. Состав карточек, их порядок и
цена не меняются. Ответ модели проверяется по id и реальным полям подрядчика; любая неудача
(таймаут, ошибка, невалидный JSON) откатывается на детерминированный шаблон.
Итог: один AI-слой помогает понять, что на самом деле хочет пользователь, а не механически искать слова; другой — объяснить уже честно найденный результат по-человечески. Ни один из них не может подменить решение детерминированного движка.
Возможности
- Детерминированная фильтрация по городу, категории, формату события, бюджету, занятости на дату, языку и длительности
- Детерминированное ранжирование по бюджету, уверенности в данных и релевантности категории/описания — без модели
- Опциональный слой Groq поверх уже готового результата: переписывает только текст объяснения (валидируется по id, без выдуманных чисел, с fallback на шаблон при любой ошибке/таймауте)
- Опциональный разбор свободного текста запроса («нужен фотограф и видеограф») в структурированные метки — мягкий сигнал для ранжирования, не фильтр (кроме явно обязательного языка)
- Структурированная причина отказа (
excluded) для каждого не прошедшего фильтр — всегда в ответе, включаяfound - Демо-режим на фронтенде: работает вообще без бэкенда, на заранее просчитанных mock-ответах — для показа жюри без риска сетевых сбоев
- Сравнение одного запроса на двух датах бок о бок — наглядно показывает, что разница именно в занятости
- Для
none_fit— разбивка условий по карточкам «какое условие сколько кандидатов отсеяло» и визуальная воронка отсева; дляno_category— предложение соседних городов, где категория есть - 95 тестов: 422 на невалидный ввод, детерминизм, «AI никогда не меняет состав/порядок карточек» — проверено явно, а не предполагается
🛠 Technologies We Used
The stack is deliberately simple — every piece earns its place, nothing is there for show.
-
Python — the filtering and ranking logic. This is the part that actually decides who gets recommended.
-
FastAPI — turns that Python logic into an API the frontend can call:
Frontend ↓ POST /api/match ↓ Backend (filter → rank → explain) ↓ Results -
Groq API — fast inference access to an LLM, used only where explained in Два AI-слоя above.
-
openai/gpt-oss-120b— the model behind both AI layers: understanding free-text requests and writing the explanation text (DEFAULT_MODELinbackend/llm_explainer.py, overridable viaGROQ_MODEL). -
CSV —
data/hackathon_dataset.csvstores the partner-provided contractor dataset: 66 real profiles. -
Pydantic — validates every incoming request (
backend/models.py,backend/frontend_api.py): rejects invalid dates, non-positive budgets, and out-of-range values before they ever reach the matching logic. -
Pytest — 95 automated tests covering filtering, ranking, the AI layers (with dependency-injected fakes, no real API calls needed), and the API contract itself.
-
HTML + CSS + JavaScript — the frontend, no build step, no framework.
-
Git + GitHub — how the team stores the code, collaborates across three members, and submits the project.
Архитектура
flowchart TD
U["🧑 Заказчик мероприятия"] --> F["🖥️ Frontend<br/>index.html + app.js + style.css<br/>+ Демо-режим на mock.js"]
F -->|"POST /api/match"| B["⚡ FastAPI Backend"]
F -->|"GET /api/options"| B
B --> P{"Есть query<br/>(свободный текст)?"}
P -->|"да, ENABLE_LLM_INTENT"| INT["🧭 Groq: разбор намерения<br/>услуги / язык / тип события"]
P -->|"нет"| MATCH
INT -->|"только валидные метки<br/>из датасета"| MATCH
MATCH["📋 Детерминированный движок<br/>backend/matcher.py + relevance.py"]
MATCH --> D1["Жёсткие фильтры<br/>город · категория · формат · бюджет · дата · язык · часы"]
D1 --> D2{"Есть кандидаты?"}
D2 -->|"пул пуст"| S1["status: no_category"]
D2 -->|"пул не пуст, 0 подошло"| S2["status: none_fit + excluded"]
D2 -->|"да"| RANK["Скоринг и сортировка<br/>budget · confidence · relevance"]
RANK --> TOP["Топ-3, (score desc, id asc)"]
TOP --> EXP{"ENABLE_LLM_EXPLANATIONS?"}
EXP -->|"да"| GROQ["🤖 Groq переписывает текст<br/>только для уже выбранных 3 карточек"]
EXP -->|"нет / ошибка / таймаут"| TMPL["Шаблонное объяснение<br/>из реальных полей"]
GROQ -->|"валидация: id, без выдумок"| S3
TMPL --> S3["status: found<br/>+ cards[] + excluded{}<br/>+ X-Explanations: llm|template|mixed"]
B -.reads.-> DATA[("📄 hackathon_dataset.csv<br/>66 профилей")]
S1 --> F
S2 --> F
S3 --> F
Два эндпоинта поверх одного и того же движка:
POST /match— детальный эндпоинт для разработчиков: полныйscore_breakdown,limit, полеservices.POST /api/match— адаптер под контракт фронтенда (docs/api.md): те же первые 3 результата/match, только переименованные поля и оформленный ответ. Покрыто тестами напрямую: для эквивалентных запросовcards— ровно первые 3 результата/matchв том же порядке.
Скриншоты
Пустая форма
Списки городов/категорий/форматов/языков подтягиваются с GET /api/options.
Найдены подходящие подрядчики (status: found)
Нумерованные карточки с объяснением на реальных фактах и честным счётчиком «подходят N из M кандидатов».
Прозрачность подбора
Сколько кандидатов отсеяно и по какой причине — раскрывается для любого исхода, не только для пустого.
Кандидаты есть, но никто не подошёл (status: none_fit)
Сайдбар «Заданные условия» подсвечивает, какое именно условие (дата, бюджет...) сколько кандидатов отсеяло, плюс визуальная воронка отсева по всем причинам.
Такой категории нет в этом городе (status: no_category)
Третий обязательный по условию задачи исход — явно отличим от двух других, с предложением попробовать соседний город.
Сравнение с другой датой
Один и тот же запрос, две даты рядом — наглядно видно, что разница в результате связана именно с занятостью, а не со случайностью.
Структура репозитория
hack-2cc01208-anomaly-detected
│
├── backend
│ ├── main.py — FastAPI: /health, /match, /api/match, /api/options
│ ├── models.py — Contractor, MatchRequest, ScoreBreakdown
│ ├── data_loader.py — чтение и валидация hackathon_dataset.csv
│ ├── matcher.py — жёсткие фильтры + сборка excluded{}
│ ├── relevance.py — детерминированный скоринг (budget/confidence/relevance)
│ ├── frontend_api.py — адаптер /match → контракт /api/match
│ ├── intent_parser.py — опционально: Groq извлекает метки из query
│ ├── llm_explainer.py — опционально: Groq переписывает текст объяснений
│ ├── conftest.py, test_*.py — 95 тестов
│ └── __init__.py
│
├── frontend
│ ├── index.html — форма запроса + переключатель демо-режима
│ ├── app.js — fetch к API, рендер карточек, сравнение дат, воронка отсева
│ ├── mock.js — готовые ответы для демо-режима (без бэкенда)
│ └── style.css
│
├── data
│ └── hackathon_dataset.csv — 66 профилей подрядчиков
│
├── docs
│ ├── api.md — контракт frontend ↔ backend, обе схемы (/match и /api/match)
│ └── task.md — условие кейса (копия из Google Doc организаторов)
│
├── screenshots — реальные скриншоты живого прогона (см. выше)
│
├── requirements.txt, pytest.ini, .env.example
└── README.md
API
Полный контракт — docs/api.md. Кратко для /api/match (то, что реально использует фронтенд):
POST /api/match
POST /api/match
Content-Type: application/json
{
"city": "Алматы",
"date": "2026-11-14",
"event_type": "свадьба",
"category": "Ведущий",
"budget_kzt": 2000000,
"hours": 5,
"language": "казахский"
}
Ответ (status: found):
{
"status": "found",
"message": "Подходят только 2 из 8 кандидатов: остальные заняты в эту дату — 2, не работают с этим форматом — 4.",
"cards": [
{
"id": "HK-16628",
"name": "Нобара Кугисаки",
"category": "Фотограф",
"city": "Алматы",
"price_from_kzt": 600000,
"synthetic": false,
"explanation": "Свободен 15.10.2026; цена от 600 000 ₸ при бюджете 2 000 000 ₸; работает на мероприятиях формата «корпоратив»; в описании упоминается: фотограф.",
"alternative": false
}
],
"excluded": {
"busy_on_date": 2,
"over_budget": 0,
"wrong_format": 4,
"wrong_language": 0,
"too_few_hours": 0
},
"service_intent": null,
"interpreted_intent": null
}
status принимает три значения: found (1-3 карточки), no_category (такой категории в этом
городе нет вообще), none_fit (кандидаты есть, но никто не прошёл фильтры). excluded считается
среди кандидатов нужного города и категории и мутуально исключающе: каждый отсеянный кандидат
учтён ровно один раз, по первому нарушенному правилу — счётчики не задваиваются.
Заголовок ответа X-Explanations: llm | mixed | template показывает, использовал ли Groq для
переписывания текста, и для скольких карточек.
GET /api/options
{"cities": [...], "categories": [...], "event_formats": [...], "languages": [...]}
Опционально по контракту — если недоступен, фронтенд использует захардкоженный список.
GET /health
{"status": "ok", "contractors_loaded": 66}
Данные
data/hackathon_dataset.csv — 66 профилей подрядчиков, партнёрский анонимизированный датасет.
| Категория | Профилей |
|---|---|
| Ведущий | 15 |
| Фотограф | 12 |
| Банкетный зал | 8 |
| Ресторан | 7 |
| Остальные (Лайв-бэнд, Шоу-программа, Видеограф и т.д.) | 4-5 |
| Флорист, Декоратор, Подарки и сувениры, Ведущий церемонии, Фото и видеобудки, Отель, Инструменталист | 3 каждая |
Поле synthetic сохраняется на всём пути до карточки в ответе API. Демо-режим фронтенда (mock.js)
уже включает один явно синтетический профиль («Тихиро Огино», Флорист) для показа редкой категории
без зависимости от бэкенда — с честной пометкой synthetic: true.
Детерминизм и устойчивость
| Требование условия задачи | Как реализовано |
|---|---|
| Один и тот же запрос → один и тот же порядок карточек | Сортировка (score desc, id asc); /api/match доказанно даёт те же первые 3 id, что и /match для эквивалентного запроса |
| AI не меняет состав/порядок/фильтры | Groq трогает только текст explanation уже выбранных карточек; проверено тестами test_llm_explainer.py, test_service_intent.py |
| Разные даты → разная выдача, видно, что дело в занятости | excluded.busy_on_date явно показывает причину; UI показывает обе даты бок о бок |
| Категория-площадка — тот же пайплайн, без спецкейса | «Банкетный зал» и подобные проходят через тот же matcher.py, что и любая другая категория |
| Демо не должно зависеть от доступности внешнего API | Без GROQ_API_KEY или при ENABLE_LLM_EXPLANATIONS=false — работает полностью детерминированно; фронтенд дополнительно умеет работать вовсе без бэкенда (демо-режим) |
| Невалидный ввод обрабатывается, а не роняет сервис | 422 на некорректную дату, бюджет ≤ 0, duration_hours ≤ 0, limit вне диапазона — покрыто тестами |
Тесты
pip install -r requirements.txt
pytest
95 тестов, ни один не требует реального GROQ_API_KEY — Groq-зависимости подменяются на None/фейки
через FastAPI dependency injection (get_explainer, get_intent_parser), поэтому тесты быстрые (~30 секунд) и
не зависят от сети или квоты:
test_matcher.py,test_api.py— фильтры, скоринг, детерминизм,422на невалидный вводtest_frontend_api.py—/api/matchдаёт ровно первые 3 результата/matchв том же порядке,excluded-контрактtest_intent_parser.py,test_llm_explainer.py,test_service_intent.py— опциональные Groq-слои никогда не меняют состав/порядок карточек, валидация вывода (только известные id, без выдуманных фактов), fallback на любую ошибкуtest_options.py— формаGET /api/options, согласованность с тем, что реально принимает/api/match
Воспроизводимость проверена вручную: установка зависимостей + pytest в чистом окружении → всё зелёное без ручного вмешательства.
Install & Run
Backend
pip install -r requirements.txt
cp .env.example .env # опционально: вписать свой GROQ_API_KEY
uvicorn backend.main:app --reload
Backend поднимется на:
http://localhost:8000
Swagger: http://localhost:8000/docs.
Без GROQ_API_KEY (или при ENABLE_LLM_EXPLANATIONS=false/ENABLE_LLM_INTENT=false) сервис работает
полностью детерминированно — разбора свободного текста и переписывания объяснений не будет, поведение
фильтров и ранжирования не меняется.
Environment Variables
| Переменная | Обязательна | По умолчанию | Что делает |
|---|---|---|---|
GROQ_API_KEY | Нет | — (пусто) | Без ключа оба AI-слоя выключены; сервис работает полностью детерминированно |
ENABLE_LLM_EXPLANATIONS | Нет | true в .env.example; нужен ключ | Разрешает Groq переписывать текст объяснения (слой 2) |
ENABLE_LLM_INTENT | Нет | наследует ENABLE_LLM_EXPLANATIONS | Разрешает Groq разбирать свободный текст запроса (слой 1) |
GROQ_MODEL | Нет | openai/gpt-oss-120b | Модель для обоих AI-слоёв |
GROQ_TIMEOUT_S | Нет | 8 | Таймаут на вызов Groq; при превышении — детерминированный fallback, а не ошибка |
Полный список с комментариями — в .env.example.
Frontend
cd frontend
python -m http.server 5500
Открыть http://localhost:5500. Backend должен быть уже запущен на :8000 (CORS открыт, allow_origins=["*"]).
Можно и без бэкенда вообще — включить «Демо-режим» переключателем в правом верхнем углу: все
5 обязательных по заданию сценариев отвечают из frontend/mock.js мгновенно.
Соответствие критериям оценки
| Критерий (из условия задачи) | Баллы | Что в проекте это покрывает |
|---|---|---|
| Соответствие задаче и работоспособность | 25 | Все 3 статуса, excluded всегда, детерминизм, площадки как обычная категория, 422 на невалидный ввод |
| Техническая реализация | 25 | Чёткое разделение слоёв (детерминированный движок отдельно от опциональных AI-слоёв), dependency injection для тестируемости без сети, валидация LLM-вывода против датасета, fallback на каждом AI-зависимом шаге |
| README и воспроизводимость | 25 | Этот файл: архитектура с диаграммой, контракт, данные, запуск, тесты, реальные скриншоты — весь путь пройден с чистой установкой зависимостей |
| Ценность и применимость решения | 15 | Объяснения строятся из реальных полей подрядчика; демо-режим убирает риск сетевого сбоя на самой презентации; воронка отсева и подсказки при no_category реально помогают пользователю, а не просто показывают отказ |
| Потенциал развития и оригинальность | 10 | Разбор намерения из свободного текста (service_intent, interpreted_intent) как расширяемый, но безопасный (валидируемый) AI-слой поверх детерминированного ядра; архитектурный выбор обоснован литературой, а не интуицией — см. docs/literature-review.md и таблицу сравнения с прежними работами |
Известные ограничения
- Без
GROQ_API_KEYобъяснения — детерминированный шаблон, а не естественный текст (см. заголовокX-Explanationsв каждом ответе). - Нет бронирования, заявок или уведомлений подрядчику — только рекомендация, согласно условию задачи.
- Нет сложного UI сверх формы и карточек результата — согласно прямому указанию в условии («если выбирать между интерфейсом и качеством объяснений — берите объяснения»).
Бизнес-модель
Глобальный рынок свадебных/event-услуг оценивается в $1,016.6 млрд в 2025 с прогнозом роста до $2,993.63 млрд к 2034 (CAGR 12.75%), при этом на Азиатско-Тихоокеанский регион приходится крупнейшая доля — 44% (Fortune Business Insights, 2025 — wedding services market report). Точных цифр по Казахстану в открытом доступе нет, но общий тренд (рост спроса на организованные, персонализированные event-услуги) — тот же самый, а площадка-агрегатор, о которой говорит кейс, подтверждает, что именно эта проблема (выбор из каталога) уже актуальна на локальном рынке.
| Источник дохода | Кто платит | Механика |
|---|---|---|
| Комиссия за бронирование | Подрядчик | % от суммы сделки после подтверждённого совпадения — начисляется, когда рекомендация приводит к реальному контракту |
| Платное продвижение в каталоге | Подрядчик | Более высокий приоритет в ранжировании при прочих равных условиях (бюджет/дата/формат не нарушаются) |
| Подписка для агрегатора/организатора | B2B-клиент (площадка, ивент-агентство) | Доступ к API /api/match для встраивания в собственный сайт/CRM |
| Аналитика спроса | Подрядчики и партнёры (Firebird) | Агрегированная, обезличенная статистика: какие категории/бюджеты/города спрашивают чаще всего |
| Сегмент | Потребность | Почему именно этот продукт |
|---|---|---|
| Заказчик мероприятия | Быстро выбрать 1 из N подрядчиков без ручного сравнения | Объяснение на реальных фактах вместо голого списка |
| Подрядчик | Попасть в выдачу к релевантному заказчику, а не потеряться в каталоге | Честный none_fit с указанием причины — подрядчик понимает, что изменить, чтобы попадать в выдачу |
| Площадка-агрегатор (Firebird) | Повысить конверсию каталога в бронирования без ручной модерации | Готовый матчинг-слой поверх уже существующего каталога, без переписывания его с нуля |
Команда
Anomaly Detected · HackAlem AI 2026
- Rahim Ahmed Druba — @rahim_druba17
- Samiullah Lalee — @Samiullahlaly
- Ермұқасан Төлегенұлы — @alooxaa
Авторы коммитов
История коммитов
- 2026-09-23 17:59Link demo video in READMERahim Ahmed Druba
- 2026-09-23 17:57docs: add demo videoYermukassan
- 2026-09-23 17:53Add business model tables, trilingual UI badgeRahim Ahmed Druba
- 2026-09-23 17:51Add Technologies section, Environment Variables table, explicit Problem statementRahim Ahmed Druba
- 2026-09-23 17:50fix: budget input accepts any value, i18n and style fixesYermukassan
- 2026-09-23 17:45feat: RU/KZ/EN interface languagesYermukassan
- 2026-09-23 17:45Trim literature review to fully-verified sources, add comparison table and two-AI-layers section to READMERahim Ahmed Druba
- 2026-09-23 17:39Add literature review and related-work comparison tableRahim Ahmed Druba
Все коммиты (18)
- 2026-09-23 17:37style: compact demo chips and calmer warning bannerYermukassan
- 2026-09-23 17:27Add GET /api/options and refresh README/screenshots for the merged backendRahim Ahmed Druba
- 2026-09-23 17:08style: redesign 4 result screens from design mockupsYermukassan
- 2026-09-23 17:06Merge backend-deterministic-groq: deterministic matching backend with AI intent and explanationsSULDev2024
- 2026-09-23 16:50Add deterministic contractor matching backend with AI intent and explanationsSULDev2024
- 2026-09-23 16:37merge: keep new frontend, take teammate's READMEYermukassan
- 2026-09-23 16:30Add frontend: matcher UI with explanations, transparency block, date comparison, mobile layoutYermukassan
- 2026-09-23 16:19Rewrite README with architecture diagrams and real screenshotsRahim Ahmed Druba
- 2026-09-23 15:51Implement contractor matching backend, frontend, and datasetRahim Ahmed Druba
- 2026-09-23 15:17Add repo skeleton, API contract and README structureYermukassan
Структура репозитория (55 файлов)
.env.example .gitignore README.md backend/.env.example backend/__init__.py backend/app/__init__.py backend/app/config.py backend/app/data_loader.py backend/app/explain.py backend/app/filters.py backend/app/matcher.py backend/app/ranking.py backend/app/server.py backend/conftest.py backend/data_loader.py backend/demo_queries.py backend/frontend_api.py backend/intent_parser.py backend/llm_explainer.py backend/main.py backend/matcher.py backend/models.py backend/relevance.py backend/requirements.txt backend/test_api.py backend/test_frontend_api.py backend/test_intent_parser.py backend/test_llm_explainer.py backend/test_matcher.py backend/test_options.py backend/test_service_intent.py backend/tests/__init__.py backend/tests/test_determinism.py backend/tests/test_explanations.py backend/tests/test_filters.py data/.gitkeep data/hackathon-dataset-anonymized.jsonl data/hackathon_dataset.csv docs/api.md docs/literature-review.md docs/task.md frontend/app.js frontend/i18n.js frontend/index.html frontend/mock.js frontend/style.css pytest.ini requirements.txt screenshots/date-comparison.jpg screenshots/form-empty.jpg screenshots/found-results.jpg screenshots/no-category-results.jpg screenshots/none-fit-results.jpg screenshots/transparency-panel.jpg video/Untitled Project.mp4
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.