AAYMA
RU
Войти

TaskReady

Веб-приложение, в котором бизнес превращает сырое описание задачи в карточку из 10 полей с объяснимым рейтингом готовности 0–100 и публикует её в…

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

О проекте

Команда: KnewIT Number 2
Кейс HackAlem.ai: Образование — AI Sana
Стадия: Рабочий MVP

Задача кейса: Challenge Hub: превратить задачу бизнеса в задание для студенческих команд

Суть

Веб-приложение, в котором бизнес превращает сырое описание задачи в карточку из 10 полей с объяснимым рейтингом готовности 0–100 и публикует её в каталоге. Студенческие команды откликаются на задачи, бизнес выбирает их вручную.

Как устроено

LLM находит пробелы в черновике, задаёт 3–5 уточняющих вопросов с весом «+N баллов» и собирает карточку только из слов пользователя. Evidence guard проверяет, что каждое поле подтверждено дословной цитатой из ввода. Рейтинг считает детерминированная функция compute_rating по 7 категориям на 100 баллов, без LLM. Каталог сортируется по рейтингу с пересчётом позиций. Рекомендации командам строятся по совпадению слов из навыков и интересов, только для задач с рейтингом ≥40. LLM вызывается цепочкой OpenAI→NVIDIA→stub.

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

  • детерминированный rule-based рейтинг
  • evidence guard (проверка дословных цитат)
  • цепочка фолбэков провайдеров OpenAI→NVIDIA→stub
  • keyword/stem matching для рекомендаций

Особенности

LLM запрещено придумывать факты: каждое поле карточки должно опираться на дословную цитату из ввода, а поля без подтверждения остаются пустыми с пометкой. Рейтинг полностью детерминирован и показывает, сколько баллов даст каждое недостающее поле. В репозитории есть питч-дек docs/pitch.pdf, demo-скрипт и тесты для рейтинга и рекомендаций.

Стек

Python · FastAPI · SQLite · React · TypeScript · Vite · Ant Design · pytest

Модели

OpenAI · NVIDIA (Brev)


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

README

README.md

TaskReady

Презентация и демо

Веб-приложение, которое помогает представителю бизнеса превратить сырое описание задачи в полноценную карточку, получить объяснимый рейтинг готовности 0–100 и опубликовать задачу в открытом каталоге, где студенческие команды сами выбирают, на что откликнуться.

Проблема. Бизнес формулирует задачу в одну строку: «Продажи в будние дни падают, хотим понять почему». Команда не может по такому тексту начать работу: непонятно, какие есть данные, что считать результатом и по каким признакам работу примут. Задача выглядит привлекательно, но не готова к исполнению.

Решение. ИИ находит пробелы и задаёт 3–5 вопросов по самым весомым из них, собирает карточку только из слов пользователя (каждое поле от ИИ подтверждено дословной цитатой из ввода), а код детерминированно считает рейтинг готовности по формуле кейса и показывает, чего не хватает и сколько баллов это даст.

Для кого:

  • бизнес — видит, что именно мешает задаче быть понятной, и правит её до публикации;
  • студенческие команды — открытый каталог, отсортированный по готовности, с честной пометкой, какие задачи придётся уточнять у заказчика;
  • организатор программы — задачи в каталоге сопоставимы между собой по единой шкале.

Кейс AI Sana «Рейтинг качества бизнес-задач и открытый выбор команд», хакатон HackAlem AI.


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

Обязательные модули кейса:

  • ✅ Конструктор задачи — черновик (20–3000 символов) + отрасль → ИИ извлекает уже известное и задаёт 3–5 уточняющих вопросов, у каждого вопроса виден вес «+N баллов» (backend/app/routers/tasks.py, frontend/src/pages/NewTaskPage.tsx).
  • ✅ Карточка задачи — 10 полей, полностью редактируемых человеком; под каждым полем от ИИ показан источник «Из вашего текста: «…»»; поля без подтверждения остаются пустыми и помечаются как «ИИ не стал заполнять» (frontend/src/components/TaskCardEditor.tsx, frontend/src/ui/EvidenceNote.tsx).
  • ✅ Рейтинг задачи — 7 категорий, 100 баллов, считает код без LLM; живой прогноз в редакторе подписан как прогноз, засчитанный рейтинг появляется только после ручного подтверждения; ведётся история изменений (backend/app/rating.py, frontend/src/components/RatingPanel.tsx).
  • ✅ Общий каталог — публикация по рейтингу, номер места, фильтры по отрасли и уровню, уровень «Черновик · требует уточнения» виден явно (backend/app/routers/catalog.py, frontend/src/pages/CatalogPage.tsx).
  • ✅ Отклики команд — идея, план, срок, ссылка на прототип; откликнуться можно на любую опубликованную задачу независимо от рейтинга (backend/app/routers/proposals.py, frontend/src/components/Proposals.tsx).
  • ✅ Выбор бизнеса — «Выбрать» / «Отклонить» по каждому отклику, только вручную, можно выбрать несколько команд или ни одной; автоматического назначения нет нигде в коде.

Stretch-функции:

  • ✅ Рекомендации командам — до 3 задач по совпадению навыков команды со словами задачи, только рейтинг ≥ 40; каталог рекомендации не фильтруют (backend/app/recommend.py).
  • ✅ Подтверждение этапа — бизнес подтверждает этап выбранной команде, команда получает +10 баллов, счётчик виден в шапке.

2. Сквозной сценарий

черновик → уточнение → карточка → подтверждение → рейтинг → каталог → отклик → выбор бизнеса → этап
  1. Черновик. Роль «Бизнес» → «Конструктор». Текст задачи как есть + отрасль (+ название компании). POST /api/tasks.
  2. Уточнение. ИИ раскладывает то, что уже сказано, по полям и задаёт 3–5 вопросов по самым весомым пустым полям. Рядом с каждым вопросом — сколько баллов он даст. Оценка черновика подписана как прогноз. Вопросы можно пропускать.
  3. Карточка. POST /api/tasks/{id}/answers — ИИ собирает карточку из черновика и ответов. Под каждым полем видно, из какой фразы пользователя оно взято. Бизнес правит любое поле вручную.
  4. Подтверждение и рейтинг. PUT /api/tasks/{id}/card — карточка сохраняется, код считает рейтинг, дописывает историю и пересчитывает места в каталоге. До подтверждения рейтинг не засчитывается.
  5. Публикация. POST /api/tasks/{id}/publish — задача встаёт в каталог на место по рейтингу. Низкий рейтинг публикацию не блокирует.
  6. Рост рейтинга. Блок «Что повысит рейтинг» перечисляет непройденные проверки по убыванию баллов. Бизнес дополняет карточку, подтверждает заново — рейтинг и место в каталоге обновляются, изменение показывается как «65 → 90, место #4 → #2».
  7. Отклик. Роль «Команда» → каталог → задача → «Откликнуться»: идея, план, срок, ссылка на прототип.
  8. Выбор. Роль «Бизнес» → «Мои задачи» → отклики → «Выбрать» / «Отклонить». Решение по каждому отклику независимо.
  9. Этап (stretch). У выбранного отклика — «Подтвердить этап»: +10 баллов команде.

Подробный прогон с точными формулировками и цифрами — docs/demo_script.md.


3. Формула рейтинга

Считает compute_rating(card) в backend/app/rating.py. Детерминированно, без LLM: одинаковая карточка всегда получает одинаковый балл. Каждая категория проверяет две вещи — «поле заполнено» и «поле конкретное».

Категория (key)ВесПроверки → баллы
Контекст и потребность (context_need)20context заполнен → 5; need заполнен → 5; context + need вместе ≥ 25 слов → 10
Данные и материалы (data)20data заполнено → 10; есть конкретика: цифра, формат (csv, xlsx, json, pdf) или маркер (выгруз, таблиц, баз, api, crm, 1с, excel, отчет/отчёт, лог, пример, датасет) → 10
Ожидаемый результат (expected_result)15заполнено → 8; ≥ 12 слов → 7
Критерии успеха (success_criteria)15заполнено → 8; есть число или знак процента → 7
Ограничения (constraints)10заполнено → 5; есть цифра или маркер (срок, недел, месяц, дн, дедлайн, python, доступ, nda, бюджет, стек, технолог) → 5
Пользователи (users)10заполнено → 5; ≥ 5 слов → 5
Связь с бизнесом (business_link)10contact похож на email, телефон или @username → 5; interaction_format заполнен → 5

Детали проверок:

  • «Заполнено» — после strip не меньше 3 символов и значение не из стоп-листа: «нет», «-», «не знаю», «n/a», «tbd», «?».
  • Маркеры ищутся подстрокой в нижнем регистре; слово — токен по пробелам.
  • Контакт распознаётся регулярным выражением: email, телефон от 9 цифр или @username от 3 символов.
  • У каждой проверки есть label, field, points, passed и подсказка hint для непройденной — например «Добавьте измеримый критерий: число или процент (+7 баллов)». Непройденные проверки возвращаются в missing, отсортированные по убыванию баллов, — это и есть блок «Что повысит рейтинг».
  • Название (title) в рейтинге не оценивается, но обязательно для подтверждения (3–120 символов).

Уровни готовности:

БаллыУровень (level)Подпись
0–39draftЧерновик · требует уточнения
40–69workingРабочая
70–89readyГотовая
90–100priorityПриоритетная

Поле next_level в ответе API показывает ближайший порог выше текущего балла (40 / 70 / 90) и сколько до него не хватает; при 90+ оно null.


4. Правила каталога

  • Сортировка и позиция. Каталог содержит только опубликованные задачи, отсортированные по rating.total по убыванию; при равенстве выше та, что опубликована раньше. Позиция считается всегда по полному каталогу — фильтры по отрасли и уровню её не меняют, поэтому при фильтрации места остаются сквозными (published_tasks, update_positions в backend/app/routers/catalog.py).
  • Пересчёт. Позиции пересчитываются после каждого подтверждения карточки и каждой публикации, а не по расписанию.
  • Уровни. Каждая задача в каталоге показана со своим уровнем и баллом; у уровня draft выставлен флаг needs_clarification, и в интерфейсе задача честно помечена как требующая уточнения.
  • Низкий рейтинг ничего не запрещает. Задача с любым рейтингом остаётся видимой в каталоге, и откликнуться на неё можно так же, как на любую другую: единственная проверка при отклике — что задача опубликована. В seed есть задача с рейтингом 25, и у неё есть отклик.
  • Рекомендации — только подсказка. GET /api/teams/{id}/recommendations возвращает до 3 задач с рейтингом ≥ 40, у которых совпало хотя бы одно слово из интересов, навыков и технологий команды. Сортировка: число совпадений, затем рейтинг. В reasons — совпавшие слова («совпадает: python, аналитика») и оценка ясности («всё ясно без вопросов к заказчику» / «придётся уточнить у заказчика: данные, критерии успеха»). Рекомендации выводятся отдельным блоком над каталогом и его не фильтруют.
  • Выбор команды — только вручную. Система не назначает и не выбирает команды. Статус отклика меняется исключительно через POST /api/proposals/{id}/decision из интерфейса бизнеса; решения по разным откликам независимы, можно выбрать несколько команд или ни одной.
  • Персональные и чувствительные признаки участников не используются ни в рейтинге, ни в рекомендациях: рейтинг смотрит только на текст карточки, рекомендации — только на интересы, навыки и технологии команды.

5. AI-функция

ИИ помогает превратить черновик в полную карточку. Он не придумывает факты и не считает рейтинг: он раскладывает слова пользователя по полям и формулирует вопросы. Всё, что влияет на решения, делает код — проверяет цитаты, выбирает вопросы и их вес, считает рейтинг.

Две функции (backend/app/ai/init.py)

ФункцияВходВыход
analyze_draft(draft_text, industry)черновик и отраслькарточка из уже известных фактов, цитаты-источники, отброшенные поля, 3–5 вопросов, ai_mode
build_card(draft_text, industry, questions, answers, prev_card)черновик, вопросы, ответы, текущая карточкаобновлённая карточка, цитаты, отброшенные поля, ai_mode

ai_mode (openai / nvidia / stub) показывает, кто реально ответил; интерфейс выводит это тегом «ИИ: …».

Промпты (backend/app/ai/prompts.py)

Системный промпт описывает 10 полей карточки вместе с их весом в рейтинге — так модель спрашивает сначала о самом весомом. Жёсткие правила промпта (FACT_RULES):

  1. Никаких выдуманных фактов: поле заполняется, только если пользователь прямо об этом написал.
  2. evidence — дословная цитата (копия подстроки, без перефразирования). Нет цитаты → value = null и evidence = null.
  3. value — формулировка по сути цитаты, без новых деталей, цифр, технологий и сроков. Выводы запрещены прямым примером: «Мы сеть кофеен» — это описание компании, из него не следует ни users («клиенты кофеен»), ни data.
  4. Язык русский, ответ — только JSON без markdown и пояснений.
  5. (для анализа) 5 вопросов по самым весомым пустым полям в порядке data → expected_result → success_criteria → users → constraints → contact → interaction_format, один вопрос — одно поле, до 25 слов. Каждый вопрос обязан называть детали черновика и подсказывать варианты ответа: плохо — «Какие данные у вас есть?», хорошо — «Какие данные о продажах кофеен вы можете дать команде: выгрузку чеков из кассы, отчёты по дням, за какой период?». О том, что уже есть в черновике, спрашивать нельзя; если контакта нет — последний вопрос про контакт.
  6. (для сборки) ответ может содержать сведения для нескольких полей — их надо разложить («[email protected], созвон раз в неделю» → контакт + формат взаимодействия); прежний смысл карточки сохраняется, а не затирается.

Формат входа и выхода

Вход модели — отрасль, черновик, а для сборки ещё пары «вопрос → ответ» и текущая карточка. Выход строго один JSON-объект:

{"fields": {"context": {"value": "Мы небольшая сеть кофеен в Алматы. Продажи в будние дни падают",
                        "evidence": "Мы небольшая сеть кофеен в Алматы. Продажи в будние дни падают"},
            "need":    {"value": "Хотим понять почему и что делать",
                        "evidence": "хотим понять почему и что делать"}},
 "questions": [{"field": "data",
                "text": "Какие данные о продажах кофеен в будние дни вы можете дать команде: выгрузку чеков, отчёты по дням, за какой период?",
                "why": "чтобы понять причины падения продаж"}]}

Для build_card — то же самое без questions. id и points вопросов модель не присылает: их проставляет код, где points — сумма баллов непройденных проверок рейтинга по этому полю.

Защита от выдуманных данных (backend/app/ai/guard.py)

Поле принимается, только если выполнены оба условия:

  • цитата подтверждена — после нормализации (нижний регистр, ё→е, без пунктуации, схлопнутые пробелы) evidence является подстрокой текста пользователя либо не менее 70 % её слов длиной от 4 букв встречаются в этом тексте;
  • цифры не выдуманы — все числа в значении есть в тексте пользователя. «Рост на 15 %» не пройдёт, если пользователь не называл 15.

Иначе поле остаётся пустым и попадает в removed с причиной «нет подтверждения в тексте пользователя»; интерфейс показывает такие поля как «ИИ не стал заполнять». Название (title) может быть кратким пересказом без цитаты, до 100 символов, но его числа тоже проверяются. При сборке карточки прежнее значение сохраняется, если новое не прошло guard, а ответ, который модель не разложила, кладётся дословно в поле своего вопроса — слова пользователя не теряются. Если модель пропустила факт, названный в тексте прямо («Ждём…», «Успехом будет…», формат созвона рядом с контактом), код восстанавливает пустое поле по правилам заглушки — до того, как формируются вопросы, чтобы не спрашивать об уже сказанном.

Вопросов всегда 3–5: вопросы по уже закрытым полям отбрасываются, недостающие код добивает шаблонами по самым весомым пробелам, вопрос о контакте задаётся всегда, если контакта нет. У почти полного черновика шаблонный вопрос без баллов честно помечен: «Поле уже заполнено, баллов не добавит».

Обработка некорректного ответа и сбоев (backend/app/ai/client.py)

Цепочка провайдеров: OpenAI → NVIDIA по NVIDIA_BASE_URL → локальная заглушка. Провайдер без ключа пропускается. Адрес NVIDIA может вести на hosted Build либо на собственный сервер модели с OpenAI-совместимым API — сейчас проверен второй вариант: Nemotron Nano 9B v2 через vLLM в Brev и SSH-туннель (docs/nvidia_access.md).

На каждом провайдере:

  1. Запрос с таймаутом 20 с и temperature=0; для OpenAI включён JSON-режим (response_format=json_object). При сетевой ошибке или 5xx SDK делает один автоматический повтор.
  2. Ответ проходит json.loads и валидацию Pydantic: обёртка из тройных кавычек с json снимается регуляркой, строки "null" и "none" считаются пустыми значениями.
  3. Если разбор не удался — один повтор с текстом ошибки и напоминанием о схеме (SCHEMA_REMINDER).
  4. Если и повтор не помог или API вернул ошибку (например, 403) — запрос уходит к следующему провайдеру. Все сбои пишутся в logging.warning.

Если не ответил никто, работает локальная заглушка. Дополнительно роутер задач оборачивает весь вызов ИИ в try/except и в худшем случае сохраняет только текст пользователя. Сбой ИИ никогда не даёт ошибку 500, а в ответе API всегда виден фактический ai_mode.

Режим AI_MODE=stub

AI_MODE=stub в backend/.env сразу включает локальную заглушку (backend/app/ai/stub.py) — ключи не нужны, приложение работает полностью. Заглушка тоже ничего не выдумывает: context — весь черновик, дословные фрагменты раскладываются по полям по маркерам («хотим/нужно…» → потребность, «выгрузка, CSV, таблица…» → данные, «срок, NDA, стек…» → ограничения, email/телефон/@ник → контакт), вопросы берутся из шаблонов по самым весомым пробелам, каждый ответ идёт в поле своего вопроса и он же становится цитатой. Демо-сценарий в этом режиме даёт те же цифры 10 → 65 → 90: меняются только формулировки вопросов и тег «ИИ: заглушка».

Примеры

Полные прогоны «вход → выход» на 5 черновиках из seed (модель gpt-4.1-mini, 23 сентября 2026) — в docs/ai_examples.md. Оттуда же:

#ОтрасльПолнота черновикаОценка черновикаРейтинг карточки
1HoReCalow1065
2Госсекторlow1080
3Финансыmedium3078
4ITmedium4588
5Ритейлhigh9595

В том же документе — таблица, как guard отбрасывает выдумки (ложная цитата про «выгрузку из 1С», непрошенные «15 %», поле без цитаты), и список правок промптов по итогам проверки. Воспроизвести прогон: python -m scripts.ai_check в backend/.


6. Технологии и архитектура

Backend: Python 3.11+, FastAPI, SQLModel + SQLite, Pydantic v2, openai SDK, pytest. Frontend: React 18 + Vite 6 + TypeScript, Ant Design 5 (локаль ru_RU), react-router-dom, @formkit/auto-animate. LLM: OpenAI (JSON-режим) → NVIDIA по NVIDIA_BASE_URL (hosted Build или собственный сервер с OpenAI-совместимым API) → локальная заглушка.

flowchart LR
  UI["Frontend · React + Vite + Ant Design<br/>Каталог · Конструктор · Задача · Мои задачи<br/>роль Бизнес / Команда — переключатель в шапке"]

  UI -->|"/api, прокси Vite :5173 → :8000"| API["FastAPI · app/main.py<br/>ошибки 400/404/422 на русском"]

  API --> RT["routers/tasks.py"]
  API --> RC["routers/catalog.py"]
  API --> RP["routers/proposals.py"]
  API --> RM["routers/teams.py"]

  RT --> AI["ai/ — analyze_draft, build_card"]
  AI --> PR["prompts.py<br/>только факты пользователя"]
  AI --> CL["client.py<br/>цепочка провайдеров, 1 повтор, таймаут 20 с"]
  AI --> GD["guard.py<br/>цитата + числа из ввода"]
  CL -.-> OA[("OpenAI API")]
  CL -.-> NV[("NVIDIA API<br/>NVIDIA_BASE_URL")]
  CL --> ST["stub.py — локальная заглушка"]

  RT --> RA["rating.py · compute_rating<br/>детерминированно, без LLM"]
  RC --> RA
  RM --> RE["recommend.py · рейтинг ≥ 40, топ-3"]

  RT --> DB[("SQLite · SQLModel<br/>Task / Team / Proposal")]
  RC --> DB
  RP --> DB
  RM --> DB
  SD["seed/*.json"] -->|"на старте, если БД пуста"| DB

Как это работает вместе:

  • Контракт API — backend/app/schemas.py, фронт зеркалит его в frontend/src/api/types.ts. 16 маршрутов под /api, документация — http://localhost:8000/docs.
  • Разделение ответственности: ИИ только извлекает и спрашивает → guard фильтрует → человек подтверждает → код считает рейтинг и позицию. Ни один из этих шагов не выполняется моделью.
  • Состояние задачи — конечный автомат clarifying → card_ready → confirmed → published; повторное подтверждение уже опубликованной задачи оставляет её опубликованной и просто пересчитывает рейтинг, историю и места.
  • Хранение: три таблицы (Task, Team, Proposal); составные структуры (карточка, вопросы, ответы, рейтинг, история) лежат в JSON-колонках.

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

Нужны Python 3.11+ и Node.js 20+ (для frontend-тестов — Node.js 22.6+). API-ключ не обязателен: без него приложение работает в режиме заглушки, весь сценарий проходится полностью.

Backend

cd backend
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env               # Windows: copy .env.example .env
uvicorn app.main:app --reload --port 8000

Открыть http://localhost:8000/docs. База app.db создаётся сама, seed загружается при первом запуске, если БД пуста.

backend/.env:

OPENAI_API_KEY=                    # пусто → сразу следующий провайдер
OPENAI_MODEL=                      # по умолчанию gpt-4.1-mini
NVIDIA_API_KEY=
NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
NVIDIA_MODEL=                      # по умолчанию nvidia/llama-3.1-nemotron-70b-instruct
AI_MODE=auto                       # auto | stub
DATABASE_URL=sqlite:///./app.db

AI_MODE=stub — полностью локальный режим без сети. .env в git не попадает (.gitignore), ключи не коммитятся.

Frontend

cd frontend
npm install
npm run dev                        # http://localhost:5173, /api проксируется на :8000

Сброс демо-данных

cd backend
python -m app.reset_db             # остановив uvicorn; спросит подтверждение RESET

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

Сценарий в интерфейсе (5 минут)

Проверено 23 сентября 2026 на чистой базе; цифры одинаковы с OpenAI и в режиме заглушки. Полная версия с репликами — docs/demo_script.md.

Подготовка: чистая БД (python -m app.reset_db), backend на :8000, frontend на :5173, роль «Бизнес». В каталоге должно быть 5 задач: 95, 81, 66, 48, 25.

  1. Черновик. «Конструктор» → «Взять пример» → первый пример (HoReCa) или вручную: Мы небольшая сеть кофеен в Алматы. Продажи в будние дни падают, хотим понять почему и что делать., отрасль HoReCa → «Проанализировать». → Ожидается: «Оценка черновика: 10/100», тег «ИИ: …» и 5 вопросов с бейджами +20 / +15 / +15 / +10 / +5.
  2. Ответы. Данные: Выгрузка чеков из POS за 12 месяцев в CSV; Результат: Дашборд продаж по дням и часам для каждой кофейни и три рекомендации, как поднять будни; Критерии успеха — пропустить; Пользователи: Управляющие кофейнями и маркетолог сети; Контакт: [email protected], созвон раз в неделю → «Сформировать карточку». → Ожидается: 10 полей, под заполненными — «Из вашего текста: «…»»; ответ про контакт разложен на контакт и формат связи; «Критерии успеха» и «Ограничения» пустые — ИИ их не выдумал; справа прогноз 65.
  3. Подтверждение и публикация. «Подтвердить карточку» → рейтинг 65 «Рабочая» с разбивкой по 7 категориям → «Опубликовать». → Ожидается: «Задача в каталоге на месте #4». Дальше перейти по ссылке «Открыть страницу задачи»: кнопка «Редактировать», история рейтинга и уведомление о смене места живут на /tasks/{id}.
  4. Рост рейтинга. В блоке «Что повысит рейтинг» — подсказки на 35 баллов. «Редактировать» → Критерии успеха: Рост выручки в будни на 15% за 2 месяца, Ограничения: Срок 6 недель, Python, доступ к данным после NDA → прогноз сразу 90 → «Подтвердить». → Ожидается: «Рейтинг 65 → 90, место #4 → #2», уровень «Приоритетная», история «65 → 90».
  5. Отклик. Роль «Команда» → DataCats → «Каталог» (задача на #2) → «Откликнуться»: идея, план, срок 6 недель, ссылка https://example.com/datacats/coffee-weekdays. Повторить от EduPixel — нужно для следующего шага. → Ожидается: отклик принят; задача с рейтингом 25 тоже открыта для откликов.
  6. Выбор бизнеса. Роль «Бизнес» → «Мои задачи» → задача → «Отклики»: DataCats → «Выбрать», EduPixel → «Отклонить». → Ожидается: решения независимы, ничего не выбирается автоматически.
  7. Этап (stretch). У отклика DataCats → «Подтвердить этап». → Ожидается: +10 баллов команде, счётчик этапов обновился.

Негативные проверки: публикация без подтверждения → 400; отклик на неопубликованную задачу → 400; название короче 3 символов → 422; несуществующая задача → 404 «Задача не найдена» — все с понятным текстом на русском, без 500.

Автотесты

cd backend
pytest                             # 94 теста

Последний прогон (AI_MODE=stub): 94 passed за 4.6 с. Покрытие по модулям: рейтинг — 36 тестов (границы категорий, стоп-лист, маркеры, уровни, next_level), AI — 24 (guard на подложных цитатах и числах, 3–5 вопросов, восстановление явно названных фактов, цепочка провайдеров на моках, повтор при некорректном JSON, заглушка), загрузка seed — 14, сквозной поток API — 9, рекомендации — 7, баллы seed — 3 (expected_score сверяется с compute_rating).

cd frontend
npm test                           # 6 unit-тестов (позиции каталога, представление задачи)
npm run build                      # tsc --noEmit + production-сборка

API-smoke (npm run test:api) проходит 16 маршрутов, но создаёт и меняет данные — запускать только на отдельной БД: поднять второй backend на :8001 со своим DATABASE_URL и AI_MODE=stub, затем задать TASKREADY_TEST_API=http://localhost:8001. Подробности — frontend/QA.md.

Проверка качества ИИ

cd backend
python -m scripts.ai_check         # нужен OPENAI_API_KEY; без ключа пройдёт на заглушке

Скрипт гоняет analyze_draft → ответы → build_card на всех 5 черновиках из seed и автоматически проверяет правила: 3–5 вопросов без повторов, каждый вопрос опирается на детали своего черновика, у каждого поля от ИИ есть цитата из ввода, чисел не из ввода нет, ни один ответ не потерян. Последний прогон: нарушений 0, откатов на заглушку 0.


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

Seed (backend/seed/)

Загружается на старте, если БД пуста, одной транзакцией — частичной загрузки не бывает. Все данные на русском, компании и люди вымышленные, контакты вида [email protected].

ФайлОбъёмЧто внутри
drafts.json5 черновиковHoReCa, Госсектор, Финансы, IT, Ритейл; полнота low / low / medium / medium / high — кнопка «Взять пример»
cards.json5 опубликованных задачТрансЛинк Групп (95, Логистика), Зелёная корзина (81, Ритейл), Онлайн-школа «Код Старт» (66, Образование), Клиника «Семейный доктор» (48, Медицина), Мебельная фабрика «Дубрава» (25, Производство) — в каталоге представлены все 4 уровня
teams.json5 командDataCats, Route42, ByteBridge, EduPixel, Neuron Lab — интересы, навыки, технологии
proposals.json5 откликовпо одному на seed-задачи, включая задачу с рейтингом 25

Баллы seed-задач не записаны руками: при загрузке их считает compute_rating, а тест tests/test_seed.py сверяет результат с expected_score из файла. Справочник отраслей (10 значений) задан в backend/app/routers/catalog.py: Ритейл, HoReCa, Образование, Финансы, Медицина, Логистика, IT, Производство, Госсектор, Другое.

Внешние интеграции

СервисГде используетсяОбязателен
OpenAI Chat Completions (по умолчанию gpt-4.1-mini, JSON-режим)analyze_draft, build_cardнет
NVIDIA по NVIDIA_BASE_URL: hosted Build или собственный сервер с OpenAI-совместимым API (проверен nvidia/NVIDIA-Nemotron-Nano-9B-v2 через vLLM в Brev)второй провайдер в цепочкенет
локальная заглушкапоследний шаг цепочки и режим AI_MODE=stub—

Других внешних вызовов нет: ни аналитики, ни телеметрии, ни векторных БД. Данные лежат в локальном SQLite-файле. Обучение моделей не проводится.


10. Ограничения текущей версии

  • NVIDIA работает, но медленнее целевых 5 секунд. Провайдер проверен реальными запросами на всех 5 черновиках без отката на заглушку — собственный сервер nvidia/NVIDIA-Nemotron-Nano-9B-v2 (vLLM в Brev, SSH-туннель): анализ 10,5–16,3 с, сборка 8,5–16,8 с. Hosted NVIDIA Build по-прежнему отвечает 403 Authorization failed, доступ не подтверждён (docs/nvidia_access.md). Полный отчёт по качеству — docs/nvidia_quality.json, сводка — docs/verification.md. На OpenAI те же шаги занимают 2–6 с.
  • Автоматические проверки цитат не равны смысловой проверке. На NVIDIA-прогоне один вопрос о критериях для кофеен сформулирован общо, без упоминания кофеен и продаж, а у полностью заполненной карточки остаются 3 необязательных шаблонных вопроса с «+0» — следствие требования «всегда 3–5 вопросов».
  • Нет регистрации и авторизации. Роль — переключатель в шапке (осознанное ограничение кейса): любой посетитель может действовать от имени любой команды и любого бизнеса. Для продакшена нужна аутентификация и проверка владельца задачи.
  • Вне рамок демо: чат, уведомления, загрузка файлов, мобильная вёрстка.
  • Формула рейтинга привязана к русскому языку: маркеры конкретики («выгруз», «срок», «nda») ищутся подстрокой, поэтому карточка на другом языке получит меньше баллов. Проверки грубые по построению: «12 слов» и «есть цифра» — прокси для конкретности, а не её измерение.
  • Guard ловит выдуманные цитаты и числа, но не выводы. Если модель верно процитирует пользователя, но неверно истолкует цитату («Мы сеть кофеен» → пользователи «клиенты кофеен»), формальная проверка это пропустит: такие случаи закрываются промптом, а окончательный фильтр — ручное подтверждение карточки человеком.
  • Заглушка проще модели: вопросы шаблонные, разбор текста — регулярные выражения по маркерам. Баллы демо-сценария те же, но формулировки беднее.
  • Масштаб. SQLite, один процесс; каталог сортируется в памяти (помечено в коде как осознанное упрощение для небольшого каталога). История рейтинга и списки откликов не пагинируются.
  • Stretch-механика баллов минимальна: +10 за подтверждённый этап без ограничения на число подтверждений и без отмены; решение по отклику можно изменить повторным вызовом.
  • Ссылки на прототипы в seed (example.com/...) — демонстрационные, реальных разработок за ними нет.
  • Сборка frontend выдаёт предупреждение Vite о размере JS-бандла; сборка при этом проходит успешно.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
30
Авторы
3
Файлы
91
Ветки
1

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

SUNR153 12Aslantos 11t3tsuvastr 7

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

  • 2026-09-23 17:44docs: pitch deckt3tsuvastr
  • 2026-09-23 17:22Add QA.mdSUNR153
  • 2026-09-23 17:17docs: reconcile verification with updated team READMESUNR153
  • 2026-09-23 17:16chore: document and launch NVIDIA Brev connectionSUNR153
  • 2026-09-23 17:16fix(ai): preserve explicit facts and verify NVIDIA demoSUNR153
  • 2026-09-23 17:13docs: READMEt3tsuvastr
  • 2026-09-23 17:01update():testsAslantos
  • 2026-09-23 16:35feat(backend): wire recommendations, add 'what to ask the customer' reason 16:34SUNR153
Все коммиты (30)
  • 2026-09-23 16:27Merge branch 'main' of https://github.com/BAITC-Hacks/hack-4858429d-knewit-number-2Aslantos
  • 2026-09-23 16:27feat(A3): add task proposal and recommendation componentsAslantos
  • 2026-09-23 16:02docs(Z6): demo script 10→65→90, #4→#2 and README AI section 16:02SUNR153
  • 2026-09-23 15:58feat(backend Z5): team recommendations by §9 — rating ≥ 40, stem match, top-3, tests 15:58SUNR153
  • 2026-09-23 15:57Merge branch 'main' of https://github.com/BAITC-Hacks/hack-4858429d-knewit-number-2Aslantos
  • 2026-09-23 15:57feat(A2): подключить рейтинг и каталог к API, исправить загрузку seedAslantos
  • 2026-09-23 15:49backend: L4 validation, errors, stretch endpointst3tsuvastr
  • 2026-09-23 15:42feat(backend Z4): AI quality check script, prompt fixes, docs/ai_examples.md 15:41SUNR153
  • 2026-09-23 15:30feat(backend Z3): AI layer — OpenAI→NVIDIA→stub chain, prompts, evidence guard, tests 15:29SUNR153
  • 2026-09-23 15:29feat(frontend A2): add CatalogPage and integrate with routing; enhance NewTaskPage with RatingPanel and improve stylesAslantos
  • 2026-09-23 15:18feat(backend): seed loading, catalog, proposals, manual decision and testst3tsuvastr
  • 2026-09-23 15:09feat(backend Z2): seed data 95/81/66/48/25, teams with DataCats, drafts and tests 15:06SUNR153
  • 2026-09-23 15:07Merge branch 'main' of https://github.com/BAITC-Hacks/hack-4858429d-knewit-number-2Aslantos
  • 2026-09-23 15:07feat(frontend A1): implement NewTaskPage and update routing for task creationAslantos
  • 2026-09-23 15:00feat(backend Z1): rating engine by §7 with breakdown, hints and tests 14:59SUNR153
  • 2026-09-23 14:56feat(backend Z-stub): AI stub with verbatim evidence and weighted questions 14:55SUNR153
  • 2026-09-23 14:39feat(backend):task flow Draft->answers->confirm->publish and testst3tsuvastr
  • 2026-09-23 14:36feat(frontend): add theme.ts + ui-componentsAslantos
  • 2026-09-23 14:22update(AGENTS.md): ##design on last strsAslantos
  • 2026-09-23 14:10feat(Frontend A0): initialize frontend with React, Ant Design, and ViteAslantos
  • 2026-09-23 14:03feat(backend): add frame FastAPI, SQLite schemes, plugst3tsuvastr
  • 2026-09-23 13:40fear(main): AGENTS and TASKSt3tsuvastr
Структура репозитория (91 файлов)
.gitignore
AGENTS.md
README.md
TASKS.md
backend/.env.example
backend/app/__init__.py
backend/app/ai/__init__.py
backend/app/ai/client.py
backend/app/ai/guard.py
backend/app/ai/prompts.py
backend/app/ai/schemas.py
backend/app/ai/stub.py
backend/app/db.py
backend/app/main.py
backend/app/models.py
backend/app/rating.py
backend/app/recommend.py
backend/app/reset_db.py
backend/app/routers/__init__.py
backend/app/routers/catalog.py
backend/app/routers/proposals.py
backend/app/routers/tasks.py
backend/app/routers/teams.py
backend/app/schemas.py
backend/pytest.ini
backend/requirements.txt
backend/scripts/ai_check.py
backend/seed/cards.json
backend/seed/drafts.json
backend/seed/proposals.json
backend/seed/teams.json
backend/tests/conftest.py
backend/tests/test_ai.py
backend/tests/test_flow.py
backend/tests/test_rating.py
backend/tests/test_recommend.py
backend/tests/test_seed.py
backend/tests/test_seed_loading.py
backend/tests/test_skeleton.py
docs/QA.md
docs/ai_examples.md
docs/demo_script.md
docs/nvidia_access.md
docs/nvidia_quality.json
docs/pitch.pdf
docs/readme_ai.md
docs/start_backend_nvidia.ps1
docs/start_nvidia_tunnel.ps1
docs/verification.md
frontend/.gitignore
frontend/CASE_AUDIT.md
frontend/QA.md
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/scripts/api-smoke.mjs
frontend/src/App.tsx
frontend/src/api/client.ts
frontend/src/api/notify.ts
frontend/src/api/types.ts
frontend/src/components/Proposals.tsx
frontend/src/components/RatingPanel.tsx
frontend/src/components/Recommendations.tsx
frontend/src/components/TaskCardEditor.tsx
frontend/src/components/TaskClarification.tsx
frontend/src/context/RoleContext.tsx
frontend/src/hooks/useResource.ts
frontend/src/lib/catalogPosition.ts
frontend/src/lib/taskPresentation.ts
frontend/src/main.tsx
frontend/src/pages/CatalogPage.tsx
frontend/src/pages/MyTasks.tsx
frontend/src/pages/NewTaskPage.tsx
frontend/src/pages/TaskPage.tsx
frontend/src/styles.css
frontend/src/theme.ts
frontend/src/ui/AiModeTag.tsx
frontend/src/ui/EvidenceNote.tsx
frontend/src/ui/LevelTag.tsx
frontend/src/ui/LoadError.tsx
frontend/src/ui/RankBadge.tsx
frontend/src/ui/RankChange.tsx
frontend/src/ui/ScoreRing.tsx
frontend/src/ui/StatusTag.tsx
frontend/src/ui/fields.ts
frontend/src/ui/index.ts
frontend/src/ui/levels.ts
frontend/tests/catalogPosition.test.ts
frontend/tests/taskPresentation.test.ts
frontend/tsconfig.json
frontend/vite.config.ts

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

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

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