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 зелёные.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
### Task 5: Оценка контента (язык, темы, fit по профилю задачи)
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/app/services/discovery_eval.py`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `store`, `ml_client`, `ai_service` (chat_json), `pipeline.clean_short`.
|
||||
- Produces:
|
||||
- `def detect_lang_ru(texts: list[str]) -> bool | None` — доля кириллических букв от всех букв в сумме: `>=0.15 → True`; `<=0.03 → False`; между порогами → `None` (неопределённо).
|
||||
- `def group_by_topic(messages: list[dict]) -> list[dict]` — группировка по `topic_id` (None → "main"); возвращает `[{"topic_id", "title", "messages": [...]}]`, title = сниппет первого текста темы (≤60 симв.), сортировка по количеству сообщений (убыв.).
|
||||
- `async def evaluate_message(task: dict, text: str) -> dict` — `{"fit": bool, "reason": str, "source": "heuristic"|"ml"|"ai"}`:
|
||||
1) текст пустой/длина <10 → fit False «слишком короткое»;
|
||||
2) ML: если `ml_client.is_enabled()` и прогноз `take` и `label=='spam'` → fit False «ML: спам»;
|
||||
3) ИИ: если `aiEnabled` → один JSON-вызов `ai_service.chat_json(промпт, user=text)` с промптом из описания задачи и ключей (`{fit, reason}`); ошибка → шаг 4;
|
||||
4) эвристика: fit = любой ключ входит в `clean_short(text)` casefold; reason «совпал ключ "…"» / «нет совпадений с ключами».
|
||||
- `async def evaluate_sample(task: dict, messages: list[dict]) -> dict` — последовательно по каждому сообщению; вернуть `{"fit_count": int, "total": int, "fit_ratio": float, "per_message": [{"text": …, "fit", "reason", "topic_id"}]}`.
|
||||
- `def passed(ev: dict, task: dict) -> bool` — `ev["total"] >= 3 and ev["fit_ratio"]*100 >= task["threshold"]`.
|
||||
|
||||
- [ ] **Step 1: Реализовать модуль**. Промпт ИИ (внутри модуля, константа):
|
||||
`Оцени, относится ли сообщение к сфере/задаче. Описание: {description}. Ключи: {keywords}. Верни JSON {"fit": 0|1, "reason": "краткая причина"}.`
|
||||
|
||||
- [ ] **Step 2: Проверить на временной БД** (без сети): `detect_lang_ru(["Ищем python разработчика"]) is True`; `detect_lang_ru(["we need a python developer"]) is False`; `group_by_topic` объединяет по topic_id и сортирует; `evaluate_message` на задаче без ИИ/ML возвращает эвристический fit по ключу.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user