Кейс 4. Интеллектуальная навигация по событиям / таймлайнам

Цель кейса

Показать, как с помощью эмбеддингов и inference можно реализовать поиск по смыслу в ленте событий или базе знаний, не прибегая к обучению моделей, сложному NLP-пайплайну или хрупким эвристикам. Кейс демонстрирует, как трансформеры в PHP превращаются из "ML‑экзотики" в обычный инженерный компонент.

Сценарий

Предположим, у нас есть лента событий или материалов, где каждое событие описано несколькими предложениями. Например:

  • "введены новые ограничения в отношении технологических корпораций"

  • "страны региона наращивают инвестиции в спутниковые программы"

  • "обострение конфликта на политической почве в нескольких провинциях"

Пользователь вводит запрос:

"космическая гонка среди стран региона"

Очевидная проблема: ни одно из слов запроса буквально не обязано встречаться в описании событий. Классический поиск по словам здесь быстро превращается в набор костылей: синонимы, морфология, разные языки, ручные словари и бесконечные исключения.

И это не ошибка данных. Это нормальное поведение человека – формулировать мысли иначе, чем их описывает система.

Идея решения

Вместо попытки угадать слова мы будем искать по смыслу. Не в философском, а в сугубо инженерном смысле:

  • описание события → вектор

  • запрос пользователя → вектор

  • дальше – обычный поиск ближайших значений

Эмбеддинги здесь выступают как универсальный индекс смысла. Мы переводим текст в геометрию, а дальше работаем уже с расстояниями.

Алгоритм выглядит так:

  1. Берём массив событий {id, title, description}

  2. Считаем эмбеддинг только по description, так как заголовки часто слишком короткие и добавляют шум

  3. Эмбеддим пользовательский запрос

  4. Считаем близость запроса к каждому событию

  5. Сортируем и возвращаем top‑N результатов

Всё это - без обучения моделей и без сложной инфраструктуры.

Логика работы

Вынесем всю механику в отдельный класс SemanticEventSearch. Этот класс не претендует на идеальную архитектуру и используется исключительно в демонстрационных целях, поэтому мы сознательно опускаем вопросы абстракций и оптимизаций.

Класс SemanticEventSearch:

chevron-rightКод класса SemanticEventSearchhashtag

Снаружи мы задаём:

  • список событий через setEvents()

  • модель эмбеддингов через setModel()

  • текст запроса через setQuery()

  • количество результатов topN через конструктор

Дальше вызываем run() и получаем отсортированный результат.

Детали реализации

При первом запуске поднимается embedder:

При этом используется каталог .transformers-cache. При первом обращении модель, токенизатор и веса скачиваются и сохраняются локально. Все последующие запуски используют кэш, что делает работу существенно быстрее и предсказуемее.

Для самих событий используется локальный файл embeddings.events.json. Это простой кэш эмбеддингов:

  • при запуске мы пытаемся его прочитать

  • проверяем, совпадает ли модель в кэше с текущей

  • если для каких-то событий эмбеддингов нет, считаем их и сохраняем обратно

Таким образом эмбеддинги событий считаются один раз и переиспользуются.

Поиск

После этого логика становится тривиальной:

  • эмбеддим запрос пользователя

  • нормализуем вектор (чтобы косинусная близость была стабильной)

  • считаем cosine similarity с каждым событием

  • сортируем по убыванию

  • берём top‑N

Рендер результатов (render(query, results)) намеренно вынесен наружу. Класс поиска возвращает только данные, а не HTML или текстовый вывод.

Пример использования

Список событий:

chevron-rightМассив $eventshashtag

Запускаем код:

Результат:

Мы получили совпадение не по словам, а по смыслу. Самое релевантное событие оказалось первым, хотя формулировки запроса и описания различаются.

Диаграмма пайплайна семантического поиска

Чтобы зафиксировать архитектуру решения целиком, полезно посмотреть на неё не через код, а через поток данных. В этом кейсе пайплайн предельно простой и именно поэтому хорошо масштабируется.

23.6 Конвейер поиска семантических событий

Диаграмма подчёркивает два ключевых момента:

Во‑первых, эмбеддинги событий считаются заранее и переиспользуются. Это превращает ML‑часть из дорогой операции в обычный кэшируемый ресурс.

Во‑вторых, запрос пользователя проходит тот же самый путь преобразования, что и данные. Благодаря этому мы сравниваем не слова, а представления в одном и том же семантическом пространстве.

Где это применять в продакшене

В целом это уже похоже на продуктовый подход, а не на эксперимент. К тому же вы можете легко заметить, что такой подход:

  • слабо привязан к конкретной формулировке запроса

  • хорошо работает на коротких описаниях

  • легко комбинируется с обычными фильтрами (дата, регион, тип события)

Ограничения и подводные камни

Важно не воспринимать семантический поиск как серебряную пулю.

Модели весят немало, и производительность нужно учитывать. Для некоторых задач обычный SQL с LIKE или полнотекстовый индекс будет проще и надёжнее. Но это уже нормальный инженерный разговор про trade‑off'ы, а не про магию и чёрные ящики.

Именно в таком виде трансформеры и эмбеддинги перестают быть "AI ради AI" и становятся частью поддерживаемой, объяснимой системы.

circle-info

Чтобы самостоятельно протестировать этот код, установите примеры из официального репозитория GitHubarrow-up-right или воспользуйтесь онлайн-демонстрациейarrow-up-right для его запуска.

Last updated