- BrainTools - https://www.braintools.ru -
Полтора года назад мы запустили AI-ассистента внутри «Первой Формы», чтобы он помогал сотрудникам компании выполнять рабочие процессы. Сегодня он отвечает на вопросы по регламентам, ищет документы, подсказывает по задачам, даже запускает проверку контрагентов и формирует КП. Всё это работает через большую языковую модель, а точнее — через набор моделей, потому что разные задачи требуют разного подхода: для быстрой классификации входящего запроса нужна одна, для развёрнутой генерации ответа — другая, для работы с длинным контекстом — третья.
Модели живут в облаке, и это даёт свободу выбора. Инструменты, которыми агент пользуется, тоже ходят в интернет: поиск по документации обращается к облачным моделям векторизации текста, проверка контрагентов — к внешним сервисам вроде Контур.Фокуса и так далее. Агент обновляется из GitLab, CI/CD развозит изменения по стендам автоматически, мониторинг стекается в один дашборд. Нас это устраивало.
Недавно заказчик из промышленного сектора обратился к нам с задачей: «У нас закрытый контур, интернета нет и доступа к облачным API — тоже. Единственное, что у нас есть — это сервер с локальной моделью и наша внутренняя инфраструктура. Хотим такого же ассистента, как у вас». В статье рассказываем, как мы с этим справились. Спойлер: не без приключений.
Чтобы понять, что именно ломается при отключении интернета, стоит сначала разобраться, на чём держится работа агента в нормальных условиях.
Сам агент — это набор из примерно семидесяти независимых модулей, каждый из которых отвечает за свой инструмент или этап обработки. Один модуль принимает запрос пользователя и решает, к какой категории он относится. Второй отвечает за поиск по документации. Третий отправляет запрос к большой языковой модели и получает ответ. Четвёртый может проверить контрагента по ИНН. Пятый — найти задачу в системе по фильтру. И так далее. Всего таких инструментов — больше тридцати семи, и каждый из них в какой-то момент обращается либо к внешнему API, либо к облачной модели, либо к интернет-ресурсу.
Поиск по документации построен на гибридном подходе: сначала BM25 (классический текстовый поиск по ключевым словам), затем dense vector поиск (где запрос и документы переводятся в векторные представления и сравниваются семантически), а затем cross-encoder rerank — повторное ранжирование результатов более тяжёлой моделью, которая даёт финальную оценку релевантности.
Все эти стадии, кроме BM25, требуют обращения к облачным моделям векторизации. Индекс хранится на отдельном сервисе, а для его обновления используется GitLab webhook — как только в документацию вносятся изменения, файл автоматически обновляется в индексе.
Деплой кода агента выглядит так: разработчик вносит изменения в один из семидесяти C# скриптов, отправляет изменения в GitLab, оттуда через API скрипт попадает сначала на dev-стенд, затем на preprod, затем на production. Между стендами синхронизация тоже идёт через Git. Мониторинг — HTTP-логи, smoke-тесты, алерты — тоже завязан на внешние системы.
Когда мы сели разбирать, что именно требуется изменить для работы в закрытом контуре, список получился длиннее, чем мы ожидали. И модель в этом списке занимала далеко не первое место.
Самое очевидное: вместо набора облачных моделей у нас остаётся одна локальная. Заказчик предоставил сервер с NVIDIA H200, на котором крутится Qwen 3.5 с примерно 122 миллиардами параметров. По качеству она нас устраивала, проблема была в задержке ответа.
Облачная модель отвечает за одну-три секунды, локальная — заметно дольше. Агент встроен в интерфейс системы: пользователь задаёт вопрос и ждёт ответа прямо в рабочем окне. Если ответ идёт слишком долго, это ломает пользовательский опыт [1]. Нам пришлось пересматривать всю стратегию промптов.
Если в облаке мы могли позволить себе роутинг — быструю модель для простых задач вроде классификации и мощную для сложных (генерация, анализ), то в закрытом контуре модель одна, и она должна справляться со всем спектром задач. Это значит, что промпты нужно адаптировать под её сильные и слабые стороны, а процесс генерации разбивать на чанки с использованием стриминга, чтобы пользователь видел, что ответ пишется, а не висит «в молчании».
Часть инструментов агента просто перестала существовать. Например:
web search и web fetch — поиск в интернете и чтение веб-страниц;
kontur focus check — проверка контрагентов по ИНН;
browser agent run — автоматизация браузера для получения данных из внешних источников.
Здесь возникает проблема Агент должен не падать с ошибкой [2], а понимать, что инструмент недоступен, и честно сообщать об этом пользователю: «Проверить контрагента сейчас не могу — проверка обращается к внешним сервисам, которые недоступны в вашем контуре». Иначе он начинает придумывать данные, а это может привести к репутационным и финансовым рискам.
Здесь мы столкнулись с одной из самых трудоёмких проблем. Гибридный поиск, который в открытом контуре работает через облачные модели векторизации, в закрытом контуре рассыпается на части. BM25 работает без интернета, это чистая математика [3] над индексом, а вот dense vector поиск и rerank требуют вызова облачной модели — и в закрытом контуре они просто не работают.
Мы пошли по пути manifest-driven деплоя через скрипт deploy-cutover.py [4]. Скрипт держит манифест — список из скриптов, которые должны быть на стенде. При запуске он сравнивает размер каждого скрипта на сервере с размером в локальном манифесте. Если размеры совпадают, скрипт считается in sync. Если нет — его нужно переписать.
Почему размер, а не хеш? Потому что C# скрипты хранятся как plain text, и совпадение длины в сочетании с визуальной сверкой diff даёт достаточную уверенность в идентичности. Хеш потребовал бы хранения эталонов на каждом стенде и дополнительных вычислений — мы посчитали, что это избыточно для этой задачи.
Скрипт работает в два этапа: сначала dry-run — показывает план, какие скрипты будут перезаписаны и какой реальный drift между манифестом и сервером, а затем, после подтверждения, — выполнение. При этом у каждого стенда могут быть исключённые из деплоя скрипты и свои параметры. С каждым обновлением мы автоматически накатываем их поверх стандартной конфигурации, чтобы они не затирались.
Чтобы реализовать поиск, мы подняли контейнер с собственным сервисом, который назвали zvec-search. Это FastAPI-приложение, которое включает в себя модуль векторизации, модуль ранжирования и управление индексом. Выбор стека зависит от того, какое железо есть у клиента:
Если есть GPU — можно использовать vLLM с моделью BGE-m3 для векторизации текста.
Если GPU нет — llama.cpp GGUF на CPU, медленнее, но работает.
Мы пошли по первому пути, потому что у заказчика были GPU-сервера под модель, и часть ресурсов мы смогли выделить под векторизацию.
Первичная индексация пяти тысяч с лишним файлов документации заняла примерно столько же, сколько в облаке. А вот инкрементальное обновление без webhook’а пришлось делать через отдельный скрипт — он забирает обновления документации через прокси-сервер и запускает частичную переиндексацию.
Smoke-тесты после деплоя остались. Алерты пришлось убрать, потому что мессенджеры недоступны. HTTP-логи пишутся, но не уходят во внешний агрегатор. Отладка ведётся через прямой доступ к стенду. Это, пожалуй, единственное, что мы не смогли полностью автоматизировать — мониторинг в закрытом контуре остаётся зоной ручного контроля.
Агент в закрытом контуре отвечает на вопросы в задачах, ищет по документации, помогает с бизнес-процессами. Деплой скриптов на пять стендов проходит за один прогон, а задержка ответа укладывается в ожидание пользователя за счёт стриминга и чанковой генерации.
Главный вывод, который мы для себя сделали: при переходе AI-агента в закрытый контур модель — наименьшая из проблем. LLM сегодня достаточно хороши, чтобы работать в enterprise-сценариях, и локальные сервера для инференса (H200, A100) дают приемлемую производительность.
А вот всё, что было «дано по умолчанию» в открытом контуре, превращается в отдельный инженерный проект. Примерно восемьдесят процентов работы пришлось на инфраструктуру, и только двадцать — на адаптацию модели.
Мы нашли рабочий пайплайн и готовы повторять [5] его для других клиентов — главное, заранее понимать, что инфраструктура в закрытом контуре требует такого же внимания [6], как и сам AI.
Автор: 1forma
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/30879
URLs in this post:
[1] опыт: http://www.braintools.ru/article/6952
[2] ошибкой: http://www.braintools.ru/article/4192
[3] математика: http://www.braintools.ru/article/7620
[4] deploy-cutover.py: http://deploy-cutover.py
[5] повторять: http://www.braintools.ru/article/4012
[6] внимания: http://www.braintools.ru/article/7595
[7] Источник: https://habr.com/ru/companies/1forma/articles/1040438/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1040438
Нажмите здесь для печати.