Зачем он нужен
Анализатор новостей помогает превращать обычный поток статей в понятные финансовые события TokenBel. Он работает в фоне: пользователь не заполняет форму и не нажимает кнопки, а видит уже подготовленный результат в новостном разделе.
Задача анализатора — отделить финансово значимые материалы от шума, кратко объяснить событие, выделить связанные организации и инструменты, убрать дубли и сохранить аккуратную запись для дальнейшего просмотра.
Место в новостном процессе
Анализатор включается после того, как новая статья уже найдена и сохранена как исходный материал. Он не ищет новости самостоятельно, а работает с теми статьями, которые поступили в новостной поток. Технически это обработчик очереди: каждое сохранение статьи порождает сообщение, и анализатор разбирает сообщения по одному.
Перед началом работы анализатор проверяет служебные настройки и версию сообщения, а затем загружает статью из backend и сверяет хеш её содержимого. Если статья изменилась уже после постановки в очередь, расхождение фиксируется в журнале и попадает в диагностику возможных ошибок — само по себе оно обработку не останавливает.
Для пользователя это означает простую цепочку:
- сначала появляется исходная публикация;
- затем система решает, относится ли она к тематике TokenBel;
- если относится, из неё формируется финансовое событие;
- если похожее событие уже есть, новая статья может быть присоединена к нему как дополнительный источник;
- если статья не подходит, она не попадает в готовый новостной поток.
Обогащение прикреплёнными документами
Если парсер при сохранении статьи отметил прикреплённые документы — например, PDF с решением, декларацией или отчётом, — анализатор ещё до очистки текста распознаёт их через OCR-модель Mistral. Распознанный текст добавляется к тексту статьи в памяти, с сохранением границ документов, и дальше обрабатывается вместе с ним. Учитывается не более пяти документов.
Обогащение устроено «fail-closed»: если у статьи заявлены документы, все они должны быть успешно распознаны до начала анализа, иначе обработка не начинается. Частично распознанный набор не используется. Распознанный текст нигде не сохраняется — он существует только на время обработки одной статьи, а в служебной диагностике остаются лишь количественные показатели, имена хостов и технические метрики.
Первичная очистка статьи
Перед смысловой проверкой статья приводится к более чистому виду. Из текста убираются лишние фрагменты, повторы, рекламные и навигационные вставки, пустые строки и другие элементы, которые мешают понять суть. Нормализуются пробелы, кавычки, тире и невидимые символы; регистр, имена и обозначения валют сохраняются как в оригинале.
Этот этап нужен не для изменения смысла, а для того, чтобы дальнейшая проверка работала с содержанием статьи, а не с техническим или редакционным шумом вокруг неё. Очистка детерминирована: один и тот же текст всегда даёт один и тот же результат, и на этом этапе нет обращений к AI.
Что может остановить обработку до AI
Формальных правил, которые отклоняли бы статью по внешним признакам — слишком короткий текст или «бытовой» заголовок, — в анализаторе нет. Решение о несоответствии теме принимает только AI-скрининг, чтобы короткая, но важная новость не терялась из-за неточного заголовка.
До обращения к AI обработку могут остановить только технические причины: например, у статьи отсутствует текст даже после обогащения документами. Такой случай считается постоянной проблемой и фиксируется в служебной истории.
Проверка финансовой значимости
После очистки AI-модель оценивает, подходит ли статья для TokenBel. Модель возвращает вердикт — «пропустить» или «отправить в глубокий разбор» — вместе с оценкой релевантности, подходящими категориями, обоснованием и предупреждениями. Решение принимает модель: оценка и категории сохраняются в служебных данных, а жёсткого порогового правила, отклоняющего статью по числовой оценке, в пайплайне нет.
Статья должна быть связана с инвестиционными или сберегательными инструментами, эмитентами, торговыми площадками, рынком, выплатами, регулированием или другими темами, которые помогают пользователю понимать финансовые события. Кроме того, нужна конкретная связь с Беларусью или TokenBel: финансовая тема сама по себе или публикация на белорусском источнике не делают иностранную новость релевантной, если в материале нет местного инструмента, эмитента, рынка или иной прямой связи.
Если вердикт модели — «пропустить», статья спокойно завершается без глубокого разбора. Это нормальный исход, а не ошибка, и плата за глубокий анализ на неё не тратится.
Какие темы считаются подходящими
К подходящим темам относятся не только акции, облигации и токены. Анализатор также учитывает:
- эмиссии, размещения, изменения условий выпусков и документов;
- новости эмитентов и важные корпоративные события;
- купоны, дивиденды, погашения и другие выплаты;
- дефолтные риски, реструктуризации и ухудшение обязательств;
- налоги и регулирование, если они затрагивают инструменты или участников рынка;
- торговую инфраструктуру, площадки, правила торгов и расчётов;
- банковские вклады, ставки, сроки и доходность;
- драгоценные металлы как сберегательные или инвестиционные активы;
- валютный рынок Беларуси, курсы, динамику BYN и рыночные обзоры с конкретными фактами.
Важное правило: слово «токен» в белорусском инвестиционном контексте не считается криптовалютой автоматически. Если речь идёт о токенизированном инструменте, размещении, декларации или White paper, такая новость может быть релевантной для TokenBel.
Извлечение фактов
Для релевантной статьи модель выделяет фактическую основу события. На этом этапе собираются:
- главный факт;
- дополнительные факты с оценкой уверенности;
- суммы, проценты, даты, сроки, цены, ставки и идентификаторы — каждая значимая дата сохраняется отдельно со своей ролью (решение, выплата, размещение, торги), а не сводится к одной «дате события»;
- упомянутые организации, инструменты, площадки, регуляторы, банки, валюты, рынки и другие объекты;
- замечания о неясностях, если в статье не хватает данных или формулировки неоднозначны.
Тип события и теги на этом этапе не выбираются: извлечение фактов занимается только содержанием статьи. Классификация — задача следующего слоя.
Эти материалы не показываются пользователю как отдельная черновая карточка. Они помогают собрать более точное и осторожное итоговое событие.
Создание события
После выделения фактов формируется готовое новостное событие. Оно описывает не саму статью, а финансовый смысл того, что произошло.
В событие входят:
- короткий заголовок;
- подробное, но сжатое описание;
- тип события — например выплата, эмиссия, изменение регулирования, событие по токену, валютный рынок или депозитная новость;
- характер влияния — позитивный, негативный, нейтральный или смешанный;
- оценки важности и уверенности;
- теги;
- упомянутые сущности;
- ключевые факты;
- предупреждения, если часть информации неполная или требует осторожного чтения.
Текст события должен быть нейтральным: без советов, рекламных формулировок, эмоциональных оценок и выводов, которых нет в статье. Единой «даты события» у записи нет: все даты остаются в фактах и значениях со своими ролями, а датой публикации по-прежнему считается дата исходной статьи.
Теги выбираются из закрытого утверждённого списка тем, а их русские подписи назначаются кодом, а не моделью. Если модель предлагает новый тег, он не попадает в событие автоматически — предложение попадает только в служебный аудит для редакционного обзора.
Каждый AI-этап работает на своей модели Mistral: скрининг, извлечение фактов и редакторская вычитка используют младшую модель, а сборка события — старшую, потому что именно она определяет структуру события.
Детерминированная нормализация структуры
Сразу после сборки код без участия AI приводит структуру события к каноническому виду:
- известные псевдонимы — например, «Национальный банк Республики Беларусь» в «НБРБ» или «Белорусская валютно-фондовая биржа» в «БВФБ» — заменяются каноническими именами, а тип сущности (компания, площадка, регулятор) сохраняется осмысленно, а не по случайной роли в статье;
- теги, которых нет в утверждённом списке, и все предложенные теги исключаются из события, эмбеддинга и сохраняемой записи — они остаются только в аудите;
- результат нормализации фиксируется один раз и дальше используется одинаково: для эмбеддинга, поиска дублей, вычитки и сохранения.
Проверка на дубликаты
Перед сохранением нового события система проверяет, не было ли уже очень похожей новости. Это важно, потому что один и тот же факт может появиться в нескольких источниках или повториться в близких формулировках.
Сначала для события строится эмбеддинг — смысловой вектор, который модель mistral-embed вычисляет по нормализованным данным события: тип, заголовок, описание, ключевые и выделенные факты, сущности, теги и категории. Сырой текст статьи, HTML, реклама и служебный шум в вектор не попадают.
Поиск дубля выполняет backend. Он сравнивает событие с событиями за окно поиска в несколько дней (сейчас — 4 дня) и возвращает оценки по нескольким шкалам:
- близость смысла (семантическая близость векторов);
- схожесть заголовков;
- совпадение упомянутых сущностей;
- пересечение тегов;
- совпадение типа события;
- близость по времени.
Если совпадение уверенное — итоговая оценка выше порога автоприсоединения (сейчас 0,85), backend рекомендует объединение, а жёстких противоречий нет, — новая статья не создаёт дубль. Она связывается с уже существующим событием как дополнительный источник, а этапы редакторской вычитки и сохранения нового события пропускаются. Если совпадение слабое, спорное или конфликтное, система не объединяет новости автоматически и создаёт отдельное событие. Если при уверенном совпадении само присоединение завершилось ошибкой, система не переходит к сохранению нового события — это не создаст дубль, который backend только что признал тем же фактом; сообщение либо возвращается в очередь, либо фиксируется как постоянная проблема.
Редакторская вычитка
Когда событие не оказалось дублем, его текст проходит дополнительную редакторскую вычитку. Этот этап улучшает только читаемость: заголовок, описание, ключевые факты и предупреждения.
Модель получает только эти четыре текстовых поля — ни исходную статью, ни структуру события. Тип события, характер влияния, оценки, теги и список упомянутых сущностей при этом не меняются: они остаются теми, что были выбраны на этапе создания события. Это защищает результат от случайного изменения смысла при стилистической правке.
Правила сохранности — суммы, проценты, коды, валюты, даты (полные даты приводятся к европейскому виду ДД.ММ.ГГГГ) и формулировки статусов нельзя искажать или «улучшать» — заложены в самом промпте, а не проверяются кодом. Ответ, нарушающий утверждённую структуру, не исправляется повторным запросом: он считается постоянной ошибкой и фиксируется в служебной истории.
Что видит пользователь
Пользователь видит не весь внутренний путь статьи, а итоговую новостную запись. Она помогает быстро понять:
- что произошло;
- с каким инструментом, компанией, рынком или регулятором это связано;
- какой тип события перед пользователем;
- какие факты важнее всего;
- есть ли предупреждения о неполных или неоднозначных данных;
- не является ли статья повтором уже известного события.
Если статья была только дополнительным источником к уже существующему событию, пользователь получает более чистый поток без лишних дублей.
Что происходит с неподходящими и проблемными статьями
Не каждая статья становится событием. Возможны разные исходы:
- Некорректное или устаревшее сообщение очереди — сообщение принимается без повторной обработки.
- Отсутствует текст статьи — постоянная проблема, фиксируется в служебной истории.
- Недостаточная финансовая значимость — вердикт «пропустить», статья пропускается без ошибки и без затрат на глубокий анализ.
- Похожая уже известная новость — статья присоединяется к существующему событию, если совпадение уверенное.
- Временный сбой — обработка повторяется позже: после первой доставки допускается до трёх повторных попыток.
- Постоянная проблема — например недоступная статья или некорректный результат одного из этапов — фиксируется в служебной истории, чтобы не повторяться бесконечно.
Главная идея: временным проблемам даётся повторная попытка, а нерелевантные или неисправимые случаи аккуратно завершаются. Повторная обработка той же статьи с тем же содержимым, моделью и версией набора правил (сейчас — news-v8.0) не порождает второе событие: backend распознаёт уже выполненный анализ.
Как связаны этапы
Каждый этап сужает поток и добавляет точность. Сначала статья обогащается текстом прикреплённых документов и очищается от технического шума, затем AI оценивает её релевантность, выделяет факты и собирает событие, код нормализует структуру, после чего событие проходит проверку дублей и — если дубля нет — редакторскую вычитку перед сохранением.
Такой порядок помогает не тратить дорогие этапы на неподходящие статьи: скрининг отсекает шум до извлечения фактов, а проверка дублей — до редакторской вычитки. При этом короткие, но важные сообщения о токенах, выплатах, эмиссиях, вкладах, драгоценных металлах или валютном рынке не отбрасываются формальными правилами — их судьбу решает модель.
Техническая схема слоёв
Внутри анализатор устроен как последовательность слоёв. Каждый слой получает результат предыдущего, делает только свою часть работы и передаёт дальше уже более структурированные данные. Это важно для управляемости: ранние слои дешёво убирают мусор, средние слои извлекают смысл, а поздние слои отвечают за дубликаты, читаемость и сохранение.
Слой не должен делать всё сразу. У каждого слоя есть свои «права»: что он может менять, где может остановить обработку и какие решения ему запрещено принимать.
| Слой | Основная задача | Что разрешено | Что запрещено |
|---|---|---|---|
| Вход очереди | Получить сообщение о сохранённой сырой статье и загрузить её из backend. | Проверить версию сообщения, настройки и наличие статьи; сверить хеш содержимого; повторить обработку при временной ошибке. | Самостоятельно искать новости или менять смысл статьи. |
| Обогащение документами (OCR) | Распознать прикреплённые к статье документы и добавить их текст к материалу. | Обработать только корректные абсолютные ссылки из метаданных парсера; распознавать документы последовательно; остановиться при ошибке, если документы заявлены. | Сохранять распознанный текст; использовать частично распознанный набор; судить о релевантности. |
| Layer 0: Cleaner | Очистить заголовок и текст от технического шума. | Нормализовать символы, пробелы, кавычки, тире, убрать emoji, пустые и служебные строки, зафиксировать диагностику очистки. | Решать, финансовая статья или нет; создавать событие; обращаться к AI. |
| Layer 1: Screening | Оценить релевантность TokenBel через Mistral. | Вернуть вердикт: пропустить статью или отправить в глубокий разбор — вместе с оценкой, категориями и обоснованием. Учитывать связь с Беларусью и правило, что белорусский инвестиционный «токен» не равен криптовалюте автоматически. | Формировать итоговое событие, сохранять данные или объединять дубликаты. |
| Layer 2.1: Fact Distillation | Вытащить фактическую основу статьи. | Собрать главный факт, дополнительные факты, суммы, проценты, даты, ставки, идентификаторы, организации, инструменты, площадки и неясности. | Выбирать тип события и теги, писать финальный заголовок события или выбирать окончательные структурные поля. |
| Layer 2.2: Event Builder | Собрать каноническое новостное событие. | Определить заголовок, описание, тип события, характер влияния, важность, уверенность, теги, сущности, ключевые факты и предупреждения. | Проверять дубликаты, сохранять запись или позволять редакторскому слою менять структурные поля. |
| Нормализация | Привести структуру события к каноническому виду без участия AI. | Заменить известные псевдонимы каноническими именами, назначить утверждённые подписи тегам, исключить неизвестные теги и предложения из события. | Одобрять предложенные теги, переосмысливать роли сущностей или менять смысл события. |
| Embedding | Построить смысловой вектор события для поиска дублей. | Построить стабильное представление события из нормализованных фактов, сущностей и тегов; отправить его в модель эмбеддингов. | Использовать сырой текст статьи, HTML, рекламу и служебный шум; принимать редакторские решения. |
| Dedupe Lookup | Проверить, не описывает ли статья уже известное событие. | Спросить backend о похожих событиях по смыслу, заголовку, сущностям, тегам, типу и близости по времени; при уверенном совпадении присоединить статью как источник и остановиться. | Автоматически объединять спорные или конфликтные новости; при неудачном присоединении создавать дубль. |
| Layer 2.3: Humanizer | Улучшить читаемость события перед сохранением. | Полировать только текст: заголовок, описание, ключевые факты и предупреждения. | Менять тип события, влияние, важность, уверенность, теги и список сущностей. Эти поля всегда остаются из Layer 2.2. |
На практике это даёт несколько защитных правил:
- ранние слои убирают только технический мусор, а решение о тематической пригодности принимает AI, чтобы не терять потенциально важную финансовую новость;
- Layer 2.2 является источником правды для структуры события;
- нормализация структуры выполняется кодом один раз, и все дальнейшие этапы работают с одним и тем же каноническим событием;
- Layer 2.3 улучшает язык, но не имеет права менять смысловую классификацию;
- проверка дублей происходит до финальной вычитки, чтобы не тратить редакторский слой на статью, которая будет просто присоединена к уже существующему событию;
- сохранение происходит только в конце, когда событие прошло обогащение, очистку, релевантность, факты, сборку, нормализацию, проверку дублей и вычитку.
Краткий глоссарий
- Сырая статья — исходный новостной текст, который ещё не прошёл очистку и смысловой разбор.
- Новостное событие — готовая запись о финансовом факте: что произошло, с кем связано и почему это относится к TokenBel.
- Релевантность — соответствие статьи тематике инвестиционных и сберегательных инструментов TokenBel.
- Тег — короткая тематическая метка из утверждённого списка, которая помогает сгруппировать похожие новости. Новые темы модель может только предложить — их добавляет редакция.
- Упомянутая сущность — организация, инструмент, площадка, регулятор, валюта или другой объект, который важен для понимания события.
- Дубликат — статья, которая описывает уже известное событие и может быть присоединена к нему как дополнительный источник.
- Эмбеддинг — смысловой вектор события, по которому ищутся дубли. Он строится по нормализованным фактам и структуре события, а не по сырому тексту статьи.
- OCR-обогащение — распознавание прикреплённых к статье документов и добавление их текста к материалу анализа.
- Каноническое событие — нормализованная структура события после детерминированного этапа; единый источник данных для эмбеддинга, поиска дублей, вычитки и сохранения.