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

Проблематика

Наше корпоративное пространство в wiki разрослось до десятков тысяч страниц, а поиск нужной информации стал иногда представлять из себя квест. Проблема заключается в том, что в wiki есть встроенный поиск, но он не понимает суть поискового запроса. Напишешь «как настроить интеграцию с Kafka» — получишь страницы, где слова «интеграция» и «Kafka» встречаются, но не обязательно вместе и не обязательно в нужном контексте.

Вроде бытовые мелочи, но есть большие интеграционные процессы, которые проходят через множество ИТ‑систем. Для того, чтобы узнать подробности, как они работают целиком, нужно внимательно изучить документацию. К тому же команды меняются: разработчик приходит на готовый процесс «на поддержку», не успев поучаствовать в его создании, и пока еще не глубоко погружен. Экспертиза фиксируется в документации, но порой документация растёт быстрее, чем люди успевают в ней ориентироваться. А это очень важно, например, при разборе инцидентов на проде, когда счёт идёт на минуты.

Или вот еще один кейс от команды E2E‑тестирования. У коллег есть ежедневные дежурства по анализу отчётов регресс‑тестов. По итогам анализа заводятся тикеты и перед этим нужно проверить, нет ли дублей. Обычный поиск таск‑трекера ищет по жёсткому совпадению, а у них тикеты часто содержат почти одинаковые стектрейсы, которые отличаются только переменными данными — UID, датами, трейд‑кодами. Сложности начались, когда за 4 года работы команды количество решённых задач превысило 9000. В итоге ребята ежедневно тратили время на ручной поиск дублей, а это по 10 мин на каждый поиск. Ниже расскажем, как мы сэкономили ребятам несколько десятков часов монотонной работы.

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

Пример поиска в корпоративной wiki
Пример поиска в корпоративной wiki

Архитектурный стек

Перед тем как описывать решение, обозначим, какие мы использовали инструменты.

Движок автоматизации‑ open‑source low‑code платформа для создания workflow‑автоматизаций. Уже из коробки имеет сотни готовых интеграций и простой интерфейс для взаимодействия с LLM и поддержку мульти‑агентных систем.

Ключевое для нас: open‑source low‑code платформа разворачивается полностью self‑hosted. Именно так она и используется в Московской Бирже — никаких внешних API‑запросов с нашими данными.

Low‑code платформа в нашей системе:

  • оркестрирует весь пайплайн (загрузка → чанкинг → эмбеддинг → поиск → ответ);

  • взаимодействует с API wiki‑платформ, LLM и базами данных;

  • позволяет визуально строить и отлаживать сложные цепочки без написания кода.

Второй ключевой инструмент — векторная СУБД. Хранит эмбеддинги документов и выполняет семантический поиск по близости векторов. В отличие от полнотекстового поиска, она:

  • находит смысловые совпадения, а не только точные вхождения слов;

  • поддерживает фильтрацию по метаданным (автор, дата, пространство в wiki);

  • возвращает результаты с оценкой релевантности;

  • горизонтально масштабируется.

Именно векторная СУБД позволяет задать вопрос «где описан порядок эскалации инцидентов?» и получить нужную страницу, даже если в ней нет слова «эскалация».

Векторная БД
Векторная БД

LLM используется для понимания запросов и генерации ответов.

Языковая модель решает две задачи:

  • Понять запрос пользователя и преобразовать его в форму, удобную для векторного поиска.

  • Сформулировать ответ на основе найденных чанков документации, добавив ссылки на источники.

Модель работает внутри периметра Московской Биржи — данные не уходят во внешние сервисы.

PostgreSQL — выступает реляционным хранилищем для промежуточных данных перед загрузкой в векторную базу. Там же хранятся метаданные страниц, статусы индексации и история обновлений.

Хранение данных в PostgreSQL
Хранение данных в PostgreSQL

Решение

Воркфлоу в low-code платформе
Воркфлоу в low‑code платформе

Для наглядности на изображении проиллюстрированы действия в первом воркфлоу.

Мы использовали 2 промежуточные таблицы postgres для того, чтобы в первую собирать id нужных страниц, по которым будет работать поиск. Во вторую таблицу уже записывали контекст страницы и использовали её для проверок (пройденные LLM‑кой страницы, загруженные в векторную БД и так далее). Этого можно не делать, но на пространстве в 10000+ страниц при работе воркфлоу возможны разного рода коллизии и проверки id по таблицам помогают продолжать работу с того места, где случилась ошибка.

0 этап

Собираем список необходимых страниц пространства, из которых будем с помощью LLMполучать контекст. Мы делали это скриптом на python, который обходит все подстраницы пространства и записывает id страницы в БД postgres.

1 этап (синяя подложка на воркфлоу)

С помощью стандартной ноды берём id страниц из первой БД и проверяем, обрабатывалась она или нет. Если нет, то группой нод получаем её содержимое http‑запросом в API wiki, подчищаем от лишнего форматирования и превращаем в markdown. Самые длинные страницы мы обрезали, чтобы они влезали в контекстное окно LLM. Далее простым промптом просим LLM выделить из страницы контекст по стандартной форме и сформировать несколько тегов ключевых слов, описывающих эту статью. После чего грузим всё во вторую БД.

2 этап. Жёлтая подложка

Получившуюся таблицу мы просматривали вручную и выборочно проверяли некоторые строки. Убедившись, что всё ок, проставили скриптами галки true в колонку validate и запустили с помощью ручного триггера и стандартной ноды для работы postgres обход всех строк таблицы с проставленной галкой validate=true и loaded=false. Дальше с помощью стандартного для такой задачи набора нод (embeding, splitter) загружаем необходимые данные в векторную БД, формируя базу для контекстного поиска.

3 этап. Зелёная подложка

Тут реализован модуль общения с ботом. Всё стандартно, триггер чата обращается в агент с LLM‑моделью, у которого в качестве инструмента есть получившееся шагом ранее хранилище. Количество выборки и формат ответа регулируется промптом.

Общение в чате реализовано через простой веб‑интерфейс. На той же машине, что и low‑code платформа, развернут локальный веб‑сервер, отдающий идущую из коробки страницу чата low‑code платформы.

Как мы реализовали аналогичное решение для таск‑трекера

И, как и обещали, рассказываем, как нам удалось тиражировать данное решение на работу c таск‑трекером для команды E2E‑тестирования.

Штатный поиск по таск‑трекеру работал так же, как и по wiki. Нужно помнить точные keyword. Немного ошибся, выбрал синоним или сокращение — и нужную задачу найти уже не получится.

Наше решение работало по той же схеме:

  • python скрипт разбирает все задачи в пространстве и складывает содержимое в postgres БД;

  • embedding перегружает в векторную базу и оттуда уже выдается чат‑боту;

  • чат‑бот помогает тестировщикам искать по сути ФЗ, не утруждая себя вспоминать дословные формулировки.

Поиск по таск-трекеру
Поиск по таск‑трекеру

Подводные камни

  • Несколько раз падало из‑за невозможности обработать LLM‑кой запросы, поэтому и пришли к версии с 2-мя таблицами и retry на всех важных нодах.

  • Так же следует заранее подумать о данных, которые вам необходимо индексировать. Если там есть обязательные поля (например, названия проектов на странице автора) и вы хотите гарантированно получать их в ответе на свой поисковый запрос — стоит их заранее разметить как метаданные для векторной БД и записывать как metadata.key для точного поиска.

  • Обновление индексов в векторной базе — из коробки через low‑code платформу специальной нодой обновить метаданные конкретной записи нельзя. Если вам требуется периодически изменять страницы в wiki и изменять соответствующие векторы, потребуется хранить id страницы как метаданные metadata.key и уже по ним искать в векторной базе id записи и уже по нему — обращением в api векторной базы обновлять или удалять запись.

Что мы в итоге получили

  • Поиск по смыслу, а не по словам.
    Запросы в стиле «как откатить деплой на стенде» находят нужную документацию, какими бы словами она ни была написана. Сотрудники тратят меньше времени на поиск информации.

  • Система не требует точной формулировки.
    Опечатка, синоним, смешение латиницы и кириллицы — раньше это затрудняло поиск. Снижается риск потери знаний в командах, а также снижается порог входа для новых сотрудников.

  • Для того конкретного кейса для команды E2E
    Поиск дубля тикета стал в 10 раз быстрее. До внедрения дежурный тратил на поиск дубля среди 9000+ тикетов около 10 минут — и делал это 1–2 раза за рабочий день. После внедрения тот же поиск занимает минуту. Это дало команде 50+ часов сэкономленного времени в год. И таких кейсов можно найти несколько у каждой команды.

  • Все данные остаются внутри периметра компании.
    Low‑code платформа, LLM и базы данных развёрнуты self‑hosted, ни один запрос и ни один фрагмент документации не уходит во внешние сервисы. Отсутствуют интеграционные риски с внешними API.

  • Переиспользование коллекций.
    Коллекции векторной БД можно выгружать и передавать смежным командам — не нужно переиндексировать одно и то же дважды.

Мы только в начале пути. В ближайшее время планируем:

  • расширить покрытие на другие источники внутренней документации;

  • добавить возможность задавать вопросы с уточнениями (диалоговый режим);

  • улучшить качество чанкинга с учётом структуры страниц wiki (заголовки, таблицы, блоки кода);

  • настроить аналитику запросов, чтобы понимать, что сотрудники ищут чаще всего.

Если у вас растёт корпоративная база знаний и стандартный поиск перестаёт справляться — контекстный поиск на базе LLM и векторных баз данных решает задачу.

При этом не обязательно писать бэкенд с нуля: low‑code платформа позволяет собрать весь пайплайн, а данные можно оставить внутри периметра.

Стек low‑code платформа + векторная СУБД + PostgreSQL + LLM оказался практичным выбором: каждый инструмент делает своё, они хорошо интегрируются друг с другом, и вся система прозрачна для отладки.

Если есть вопросы по архитектуре или хотите поделиться своим опытом — пишите в комментариях.