BorisovAI

Блог

Публикации о процессе разработки, решённых задачах и изученных технологиях

Найдено 3 заметокСбросить фильтры
Новая функцияtrend-analisis

Пять фильтров, которые спасают тренды от мусора

Работаю над системой анализа трендов, и вот столкнулся с классической проблемой: алгоритм извлекает тренды из событий, но половина из них — полная ерунда. Тренд про нефть, апельсины и страховку в одной кучке. Код всё правильно считал, данные прошли все проверки, а результат — помойка. Начал добавлять фильтры. Первый — **проверка когерентности эмбеддингов**. Идея простая: если события в тренде топически не связаны, их эмбеддинги будут далеко друг от друга. Задал порог 0,35 на косинусное сходство. Тестировал на 84 старых трендах — алгоритм корректно отклонил все 56 мусорных. Остальные 28 прошли, потому что там правда были связанные события. Второй шаг — добавил **relевance score прямо в события тренда**. Раньше я просто считал, что событие относится к тренду с уверенностью 1.0. Теперь считаю косинус до центроида кластера. Неожиданный эффект: стало видно, какие события в тренде настоящие якоря, а какие — на грани выпадения. Третий — **чёрный список сущностей**. Оказалось, что при связывании новых событий с существующими трендами система матчит их на "Россия", "Китай", "ИИ" — настолько общие сущности, что они ничего не говорят. Добавил фильтр: если матч только на blacklist-сущности, событие не цепляется к тренду. Шум упал заметно. Четвёртый — **порог уверенности LLM >= 0.5**. Когда модель извлекает тренд, она даёт скор. Если скор низкий, я просто отбрасываю кандидата до материализации. Фильтр дешёвый, срабатывает до дорогих операций. И наконец, пятый — **второй проход LLM**. Это был прорыв. На каждого кандидата тренда LLM отвечает на один вопрос: "Это действительно тренд или просто ситуация/процесс?" Дешево — всего 1-2 кандидата на кластер, но ловит ложные срабатывания, которые прошли все code-фильтры. Вместе эти пять ворот резко подняли качество. Не идеально, но уже можно работать. История же в том, что когда ты полагаешься только на код и статистику, спотыкаешься об edge-cases, которые люди видят с первого взгляда. 😄

#git#commit#security
20 апр. 2026 г.
Новая функцияtrend-analisis

Как HDBSCAN раскрыл истинное лицо трендов

Три месяца назад в проекте Trend Analisis возникла беда: система обозвала *трендом* любое, даже совершенно рандомное событие. Мы парили события в эмбеддингах, выуживали несколько похожих друг на друга и думали, что открыли закономерность. На самом деле собирали мусор. Первое время казалось, что проблема в нейросетях или в пороговых значениях для фильтрации. Раскидывали параметры кластеризации, ловили иголку в стоге сена. Потом дошло: проблема не в инструментах, а в самой логике. Мы искали тренд в одном событии вместо того, чтобы смотреть на *паттерны внутри кластера*. Развернули HDBSCAN и переписали всю pipeline с нуля. Теперь тренд — это не одно событие, а структурированный паттерн, извлечённый из группы связанных событий. В каждый кластер добавили шаг `_extract_trends_from_cluster()`, который просит LLM найти 0–3 реальных структурных закономерности с доказательствами: какие события их подтверждают, в какую сторону идёт изменение, кто задействован, какие метрики вообще говорят. Потом добавили **domain_tags** — 3–5 широких категорий для каждого события. Звучит небольшой деталью, но эта штука стала мостом между источниками данных. Теперь события из гита, Слака и журналов понимают друг друга через общие темы. И главное — это не требует дополнительных вызовов LLM: теги шли вместе с экстракцией паттернов. Пришлось перестраивать матчинг. Раньше пробовали простое совпадение по сущностям — полный провал. Теперь используем гибридный подход: 55% веса на эмбеддинг-похожесть, остальное на пересечение тегов и сущностей. Миграция базы добавила три новых таблицы для хранения связей события-тренд, и дедупликация трендов с порогом перекрытия 0.40. На 12GB сервере обработали 5 кластеров, вытащили 14 валидных трендов и повязали к ним 56 событий. Это не мировая цифра, но тренды стали реальными паттернами, а не сборищем разнополюсных событий. Одна смешная деталь — в пики нагрузки Ollama работает на два порта одновременно, всё синхронизируется мьютексом и гробит RAM нещадно. Поэтому пришлось временно отключить переклассификацию событий после дедупликации, иначе сервер шёл в отказ. TensorFlow, кстати, здесь не причём — но принцип тот же: решение проблемы, о которой ты не знал, способом, который никто до конца не понимает 😄

#git#commit#python#security
18 апр. 2026 г.
Исправлениеtrend-analisis

Как два портала Ollama спасли трендовый анализ от краша

Работаю над Trend Analysis — сервис, который ловит тренды из разных источников и анализирует их на лету. Недавно столкнулся с паттерном ошибок, который казался совершенно случайным: иногда pipeline падал с «Remote end closed connection», но воспроизвести его не удавалось. Выглядело так, будто кто-то рубит соединение с Ollama прямо во время запроса. Начал копать логи. Оказалось, что pipeline одновременно вызывал две разные модели — hermes3:8b и gemma4:e2b — через одно соединение к Ollama. Обе модели жрут VRAM как сумасшедшие, и когда они грузятся одновременно, память взрывается. Ollama просто закрывал соединение, и всё рушилось. Решение было дерзким и простым: развести модели на разные порты. Олдам запустил я на 11435 (для gemma4) и 11436 (для hermes3). Теперь каждая модель знает своё место в памяти, и они перестали давить друг на друга. Плюс добавил глобальный `_ollama_mutex` — теперь запросы идут в очередь, никаких гонок. Но это было только начало. Копался в конфигах и наткнулся на `keep_alive="-1"`. Выглядит невинно, но Ollama работает на Go, а там это не валидный duration. Сервер просто отклонял все запросы с такой настройкой. Заменил на `keep_alive="999h"` — модели теперь зависают в VRAM по 41 день, готовые к работе. Параллельно выяснилось, что при переводе chunk_size стоял в 50 символов. Это приводило к тому, что промпты раздували до 16K+ символов — контекстное окно переполнялось. Снизил до 5 — проблема решена. Ещё добавил retries (с 2 до 5), потому что FRP-туннель иногда глючит, и нужна возможность переподключиться. А busy_timeout для SQLite поднял до 60 секунд — иногда блокировка базы стоит дольше, чем ожидается. В watchdog cycle переделал логику: обогащение теперь работает *до* проверки кластеризации, а не параллельно. И если extraction активна, обогащение просто пропускает цикл, не ждёт. После фиксов pipeline стал стабильнее. Нет больше фантомных крахов, модели не воют в памяти, а timeouts предсказуемы. *По-поводу Scala и Stack Overflow:* оказывается, они правда считают себя специалистами. 😄

#git#commit#python#api#security
17 апр. 2026 г.