ci / build-test (push) Canceled after 0s
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/ Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue, контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер), Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог). Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0, тесты 1340/130/52/38/9 зелёные.
6.9 KiB
6.9 KiB
Task 5 — Отчёт: Оценка контента (язык, темы, fit по профилю задачи)
Статус
✅ Реализовано и проверено (py_compile + офлайн-сценарий на временной БД в контейнере).
Файлы
- Создан:
backend/app/services/discovery_eval.py— чистая логика оценки (без карточек/очередей/обучения), поверхstore,ml_client,ai_service.chat_json,pipeline.clean_short.
Что сделано
Интерфейсы брифа
detect_lang_ru(texts)— суммарная доля кириллицы (блок U+0400–U+04FF) среди всехstr.isalpha()-букв выборки:>=0.15 → True,<=0.03 → False, между порогами или 0 букв →None.group_by_topic(messages)— группировка поtopic_id(None → "main");[{"topic_id", "title", "messages": [...]}]; title — сниппет первого непустого текста темы (≤60 симв., whitespace схлопнут); группы отсортированы по числу сообщений (убыв.), порядок сообщений внутри группы — входной (хронологический изdiscovery_read).evaluate_message(task, text)async — каскад:- текст пустой/
len(strip) < 10→{"fit": False, "reason": "слишком короткое", "source": "heuristic"}; - ML:
ml_client.is_enabled()→predict(text);takeиlabel=='spam'→False, reason «ML: спам»,source: "ml"; иначе ниже; - ИИ: если
aiEnabledи доступен ключ/локальный провайдер (см. Concerns) — одинai_service.chat_json(промпт, user="Сообщение:\n"+text[:4000]);{fit, reason}из ответа; любая ошибка → шаг 4; - эвристика: любой ключ входит в
clean_short(text).casefold(); reasonсовпал ключ "…"/нет совпадений с ключами,source: "heuristic".
- текст пустой/
evaluate_sample(task, messages)async — последовательно по сообщениям;{"fit_count", "total", "fit_ratio", "per_message": [{"text", "fit", "reason", "topic_id"}]};topic_idв per_message нормализованNone → "main"(стыкуется с ключамиgroup_by_topic).passed(ev, task)—ev["total"] >= 3иev["fit_ratio"]*100 >= task["threshold"].- Промпт ИИ — константа
_AI_PROMPTточно по тексту брифа (description/keywords подставляются через.format).
Вывод проверок
cd /c/telbase && python -m py_compile backend/app/services/discovery_eval.py→PY_COMPILE OK(без ошибок).- Сборка и офлайн-прогон на временной БД:
docker compose build app→ образ собран;MSYS_NO_PATHCONV=1 docker compose run --rm --no-deps -e PYTHONPATH=/srv -e LEADRADAR_DATA=/tmp/lr_eval --entrypoint python app /data/task5_check.py→DISCOVERY_EVAL OK:detect_lang_ru(["Ищем python разработчика"]) is True;detect_lang_ru(["we need a python developer"]) is False; смесь 1/20 кириллицы →None; текст без букв →None;group_by_topic: темыmain/111/222, объединение 2 сообщений в 111, сортировка[111, main, 222], title «Топик A первый» (≤60), входной порядок внутри группы сохранён;evaluate_messageна задаче без ИИ/ML (aiEnabled=False,mlEnabled=False) → эвристика:{"fit": True, "reason": "совпал ключ "python"", "source": "heuristic"}; без ключа → False «нет совпадений с ключами»; «короче» → False «слишком короткое»;aiEnabled=Trueбез ключа провайдера → ИИ-ветка не вызывается (без сети), отвечает эвристика;evaluate_sample: total=4/fit=2/ratio=0.5, per_message topic_id["main","main",5,5];passed: threshold 40 → True, 60 → False, выборка из 2 → False, пустая → ratio 0.0/False.
- Временный файл
data/task5_check.pyудалён после прогона.
- Диагностика файла — без ошибок и предупреждений.
Concerns
sourceдля слишком коротких сообщений —"heuristic": это первая ступень каскада (до ML/ИИ), ноsourceпо брифу — объединениеheuristic|ml|ai, отдельного значения нет. Воркеру (Task 6) это не мешает; при необходимости подсчёта «отсевов по длине» лучше ориентироваться наreason.- ИИ-ветка дополнительно проверяет наличие ключа (
ai_service.provider_status():keySetилиlocal-провайдер), а не толькоaiEnabled— иначеchat_jsonпри отсутствии ключа тратит ~6 c на ретраи и сыплет warning в лог на каждое сообщение. При любой ошибке/недоступности статуса — всё равно fallback на эвристику (как и требует бриф). topic_idвper_messageнормализованNone → "main", чтобы результатevaluate_sampleстыковался с ключамиgroup_by_topic. Task 6 при подсчёте «подходит X из N» по темам форума должен сравнивать ключ"main", а неNone.titleтемы — сниппет первого непустого текста темы (первого во входном порядке), а не обязательно первого сообщения;topic_titleиз сообщений не используется (вdiscovery_readTask 4 его и нет). Если позже понадобится название темы из Telegram — добавить как fallback для пустых текстов.- Лимиты на границе: тексту в ИИ обрезается до 4000 симв. (как в
ai.filter_incoming), причина из ИИ — до 200 симв.; эвристика и длина считаются по полному тексту. - Решения оценки нигде не логируются и не учитываются в счётчиках
ml_client.track_decisions— модуль чистый; учёт/логирование при необходимости добавить в Task 6 на уровне воркера.