Всем привет! На связи Валерий Калинин и Олег Кудинов, мы представляем команду сопровождения тестовых стендов приемочного тестирования Московской Биржи. В этой статье расскажем, как сделали поиск по внутренней документации в таск‑трекере и корпоративной wiki на порядок быстрее, не написав ни единой строчки кода бэкенда.
Проблематика
Наше корпоративное пространство в wiki разрослось до десятков тысяч страниц, а поиск нужной информации стал иногда представлять из себя квест. Проблема заключается в том, что в wiki есть встроенный поиск, но он не понимает суть поискового запроса. Напишешь «как настроить интеграцию с Kafka» — получишь страницы, где слова «интеграция» и «Kafka» встречаются, но не обязательно вместе и не обязательно в нужном контексте.
Вроде бытовые мелочи, но есть большие интеграционные процессы, которые проходят через множество ИТ‑систем. Для того, чтобы узнать подробности, как они работают целиком, нужно внимательно изучить документацию. К тому же команды меняются: разработчик приходит на готовый процесс «на поддержку», не успев поучаствовать в его создании, и пока еще не глубоко погружен. Экспертиза фиксируется в документации, но порой документация растёт быстрее, чем люди успевают в ней ориентироваться. А это очень важно, например, при разборе инцидентов на проде, когда счёт идёт на минуты.
Или вот еще один кейс от команды E2E‑тестирования. У коллег есть ежедневные дежурства по анализу отчётов регресс‑тестов. По итогам анализа заводятся тикеты и перед этим нужно проверить, нет ли дублей. Обычный поиск таск‑трекера ищет по жёсткому совпадению, а у них тикеты часто содержат почти одинаковые стектрейсы, которые отличаются только переменными данными — UID, датами, трейд‑кодами. Сложности начались, когда за 4 года работы команды количество решённых задач превысило 9000. В итоге ребята ежедневно тратили время на ручной поиск дублей, а это по 10 мин на каждый поиск. Ниже расскажем, как мы сэкономили ребятам несколько десятков часов монотонной работы.
В итоге мы решили сделать поиск быстрым, удобным и не по конкретным ключевым словам, а по смыслу и на естественном человеческом языке.


Архитектурный стек
Перед тем как описывать решение, обозначим, какие мы использовали инструменты.
Движок автоматизации‑ open‑source low‑code платформа для создания workflow‑автоматизаций. Уже из коробки имеет сотни готовых интеграций и простой интерфейс для взаимодействия с LLM и поддержку мульти‑агентных систем.
Ключевое для нас: open‑source low‑code платформа разворачивается полностью self‑hosted. Именно так она и используется в Московской Бирже — никаких внешних API‑запросов с нашими данными.
Low‑code платформа в нашей системе:
оркестрирует весь пайплайн (загрузка → чанкинг → эмбеддинг → поиск → ответ);
взаимодействует с API wiki‑платформ, LLM и базами данных;
позволяет визуально строить и отлаживать сложные цепочки без написания кода.
Второй ключевой инструмент — векторная СУБД. Хранит эмбеддинги документов и выполняет семантический поиск по близости векторов. В отличие от полнотекстового поиска, она:
находит смысловые совпадения, а не только точные вхождения слов;
поддерживает фильтрацию по метаданным (автор, дата, пространство в wiki);
возвращает результаты с оценкой релевантности;
горизонтально масштабируется.
Именно векторная СУБД позволяет задать вопрос «где описан порядок эскалации инцидентов?» и получить нужную страницу, даже если в ней нет слова «эскалация».

LLM используется для понимания запросов и генерации ответов.
Языковая модель решает две задачи:
Понять запрос пользователя и преобразовать его в форму, удобную для векторного поиска.
Сформулировать ответ на основе найденных чанков документации, добавив ссылки на источники.
Модель работает внутри периметра Московской Биржи — данные не уходят во внешние сервисы.
PostgreSQL — выступает реляционным хранилищем для промежуточных данных перед загрузкой в векторную базу. Там же хранятся метаданные страниц, статусы индексации и история обновлений.

Решение

Для наглядности на изображении проиллюстрированы действия в первом воркфлоу.
Мы использовали 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 оказался практичным выбором: каждый инструмент делает своё, они хорошо интегрируются друг с другом, и вся система прозрачна для отладки.
Если есть вопросы по архитектуре или хотите поделиться своим опытом — пишите в комментариях.
