Привет, Хабр! На связи снова Всеволод Зайковский, заместитель руководителя проекта в «Газпром ЦПС».
В прошлый раз я рассказывал, как мы научили нашу систему «АФИДА» распознавать и раскладывать по полочкам тысячи строительных актов с помощью компьютерного зрения. Хаос был побежден, документы оцифрованы, лежали в нужных папках, и их даже можно было найти. Казалось бы — живи и радуйся, но аппетит приходит во время еды. Мы поняли, что система «видит» текст, и задали логичный вопрос: «А можно просто спросить у нее, какую марку бетона использовали для заливки фундамента, и она ответит?». Подумали и решили, что можно.
Тогда нам казалось, что прикрутить LLM к нашему архиву, чтобы она работала как умный поисковик, очень просто. Но все оказалось не так радужно. Первая версия нашего «строительного чат-бота» галлюцинировала так, что путала проектную документацию с дизайном, а ответы генерировались по три минуты.
Это история о том, как мы прошли путь от игрушечного чат-бота до полноценной RAG-системы в закрытом контуре. Расскажу, как мы запускали нейросети на CPU, почему в нашем сравнительном тесте победил Qwen, и как мы оценивали качество ответов в Excel, когда поняли, что стандартные бенчмарки нам не подходят. Статья будет полезна архитекторам, ML-инженерам, и руководителям, которые ищут рабочие on-premise решения. Если вы тоже пытаетесь внедрить LLM в энтерпрайз без бюджета и видеокарт — этот кейс для вас.
Как продать идею руководству
Для того чтобы продать идею внедрения LLM, вам нужно показать пример на полуреальных данных. Иначе никакие уговоры не сработают (по крайней мере, у меня не сработали).
Я начал с того, что смотрел на крупные релизы 24-го года (в апреле вышла Llama, в октябре вышел Claude), ходил к руководителям и говорил: "Давайте внедрять LLM, это здорово, это круто". Но мои "давайте" - не работали. И на новогодних каникулах решил действовать более решительно.
Я сел, придумал в голове объект строительства, взял формы документов из интернета и сделал синтетику. Взял общедоступного чат-бота (на тот момент я использовал Алису) и собрал небольшой «ручной» конвейер, чтобы показать, как из документов можно получить хороший результат. Наделал скриншотов с информацией, которую выдала модель, и выложил в рабочий чат.

Это заинтересовало руководство, началась дискуссия. Мы начали обсуждать, как это можно сделать, и мне дали добро на выделение ресурсов.
Сама команда была не против — ребята и так были заряжены поиграться с новой технологией. Но была проблема с железом, которая наверняка есть у многих. Нам не хотелось ждать, пока кто-то выделит GPU. Мы взяли то, что было под рукой — невостребованный ноутбук Lenovo ThinkPad на складе.

И начали работать на обычном процессоре (CPU). Это долго, но почему бы и нет? И вот тут мы допустили классическую ошибку — о ней дальше.
Первая итерация: чат-бот, который не взлетел
Не нужно внедрять LLM только ради того, чтобы «внедрять LLM». Но поначалу мы решили сделать именно так.
Мы собрались командой и обсудили все возможные решения. Определились, что будем делать обычного чат-бота. Разговор был примерно следующим:
— Вот есть же ChatGPT, там пользователи пишут вопросы и получают ответы.
— Отлично.
— Давайте сделаем тоже самое?
— Окей.
Сказано — сделано. Мы нарисовали обычное модальное окошко на фронте, в дизайне основного приложения. По бэкенду развернули Ollama и обращались к ней просто по API. Ничего сверхсложного: поднимается веб-сервер вместе с LLM, вы обращаетесь к эндпоинту, отправляете вопрос, вам прилетает ответ.

И все получилось, в целом. Бот запустился, работал, мы его показали.
Но после того, как его показали, мы поняли, что результат-то на самом деле не очень. А если честно – то совсем плохой. Наш энтузиазм значительно упал ?.
Во-первых, на тот момент у нас все еще не было GPU, и ответы мы ждали долго, по две-три минуты.
Во-вторых, качество ответов, конечно же, было весьма и весьма скромное. У нас был строительный бот - у него в системном промте было зашито, что он строитель. Но не тут-то было. Я спрашиваю: "Что такое ПСД?" (хочу ответ "приемо-сдаточная документация"). А в ответ получаю что-то про дизайн (Place Setting Design).

Видимо, в весах модели дизайн и строительство — это где-то рядышком. Такое качество, конечно, никого не устраивало. И дело не столько в качестве. Мы работаем с инженерами, строителями, а им нужна какая-то четкая база, основа. Если дается ответ на вопрос, то нужно предоставить и релевантные источники, иначе они не доверяют им. Инженеры вполне обоснованно предполагают, что модель что-то придумала. А значит, с этой информацией идти дальше (например, к руководству) - нельзя.
Инженеру нужен проверяемый ответ со ссылкой на источник. Желательно, чтобы источник был с синей подписью и печатью.
Мы поняли, что нужно пересобраться и взять опору на реальные документы, которые и так есть у нас в системе.
Вторая итерация: делаем «по уму» (RAG)
Нужен был RAG (Retrieval Augmented Generation), и его можно реализовать по-разному. Команда начала думать, как его сделать применительно к нашей системе, и остановился на архитектуре, которую сейчас опишу.
Мы исходили из следующих предположений:
У заказчика, так же как у нас, может не быть GPU. Поэтому решили сделать эту опцию отключаемой. Приложение в целом работает и без всякого чат-бота, оно должно приносить ценность, даже если GPU нет.
Пользователи будут чаще задавать вопросы по имеющимся в системе документам, чем загружать новые документы (например, по завершенным объектам строительства новые документы вообще не загружаются. А потребность найти по ним информацию есть всегда). Поэтому нет смысла обновлений базы данных в real-time. Разумная задержка допустима. Но при этом документы всё-таки будут добавляться. А значит векторизация новых данных должна происходить автоматически, в фоновом режиме.
У нас не было большого опыта в синхронизации баз данных. Чтобы не изобретать велосипеды, мы вынесли нужные таблицы из основного монолита в микросервис ИИ-помощника через WAL (Write-Ahead Logging) репликацию PostgreSQL. Это позволило отвязать нагрузку LLM от основной системы: если помощник «задумается», основная «АФИДА» не ляжет. Подняли ChromaDB, потому что модель общается с векторами, а не с голым текстом. Саму LLM поднимали через Ollama, которая крутилась в отдельном docker-контейнере.

На векторизации я хотел бы остановиться чуть-чуть поподробнее. Тем, кто RAG еще не реализовывал, будет интересно понять, как это работает. Я на схеме нарисовал два потока данных: синий поток — это поток эмбеддингов, и черный поток — это поток работы с вопросами пользователя.
Они работают параллельно, и чтобы реализовать постоянно работающий RAG в типовом варианте, вам нужно реализовать оба этих потока:
Синий поток (индексация документов). Сначала по API получаем новый текст — пользователь загрузил документ, он распознался. Затем делим его на чанки (chunks) — кусочки текста. Чанк отправляется в модель-эмбеддер (Embedder). Это «малая» большая языковая модель, которая берет текст и делает из него число. И это число — эмбеддинг — складывается в ChromaDB и там хранится. Этот поток работает постоянно: документ загрузился — эмбеддинг добавился.
Черный поток (работа с вопросами). Пользователь задает вопрос, например: «Дай мне объемы работ по бетону». Этот вопрос тоже векторизируется, превращается в число. Затем сопоставляется с теми числами, которые есть в ChromaDB, с релевантными кусочками текста. Эти релевантные кусочки объединяются с вопросом пользователя, и уже этот объединенный вопрос летит во вторую модель — большую языковую модель (LLM). И уже на выходе нее мы получаем ответ, который показываем пользователю.

Такое, конечно, мы реализовывали подольше. На все у нас ушло порядка трех месяцев, а не месяц, как на обычного чат-бота. И после того, как мы показали хоть какой-то результат (с чат-ботом), у наконец-то у нас появилась GPU.
На итог, что получили?
Во-первых, мы полностью контролируем источники данных. Решаем вопрос информационной безопасности: работаем не с данными, на которых кто-то обучил модель, а с документами в нашем контуре. Знаем, откуда пользователь зашел, и пробрасываем только те документы, к которым у него и так есть доступ. Просто так получить ответ на вопрос про «заработную плату директора» - нельзя. Но если ты находишься в каталоге с документами про заработную плату всех сотрудников – то можно. А вот кому давать доступ к какому каталогу – решается обычным распределением доступов через панель администратора системы. И LLM эту схему никак нарушить не сможет.

Во-вторых, модель объединяет несколько документов сразу. В классическом поиске вам нужно прокликать десять документов, которые вам показала поисковая выдача. А здесь - модель их быстро посмотрит сама.
И третье, что понравилось нашим заказчикам больше всего — это кликабельные ссылки на источник. Если ты контролируешь источник, ты можешь дать пользователю ссылку. Он нажмет, увидит глазами превью PDF-ки и поймет, откуда взята информация. Ему уже не надо думать: галлюцинировала модель или нет, потому что интерфейс системы даёт возможность в режиме этого же окна за секунду проверить источник. Это полностью снимает вопрос доверия к LLM.
Все заработало, я выдохнул. Но возник финальный вопрос: а всё ли хорошо мы сделали? И вообще, на самом деле, в работе LLM — что такое хорошо, а что такое плохо?
Выбор модели: как впихнуть невпихуемое
Этим вопросом я задался после того, как мы с командой начали безудержно экспериментировать.
Разработчики приходили ко мне с горящими глазами: «Там вышла новая моделька! У Яндекса, у Т-Банка, Qwen новый релизнул. Давайте попробуем, интересно». Мы заливали новые веса на наш закрытый сервер и смотрели.
Мы веди для себя небольшую таблицу:
модель;
размер модели = влезают ли веса в видеокарту (на тот момент у нас было ограничение по VRAM — половина от Tesla L40, т.е. около 24 Гб);
адекватное ли время ответа;
пишет ли она хорошо на русском языке (для локальных моделей это проблема, так как некоторые, особенно раньше, подмешивали в ответ и китайский, французский).
И был пятый, самый субъективный критерий — «Качество» (хорошо ответила или плохо).

На тот момент мы остановились на Qwen 2.5 7b. Она хорошо понимала русский, влезала в нашу память и давала вменяемую скорость. Llama 3.2 3b оказалась слишком неинформационной для наших задач, а более крупные модели просто не могли запустить локально с нужной скоростью.
Где теряется качество в RAG?
Но выбрав модель, мы поняли, что качество ответа зависит далеко не только от нее. Как говорят на крутых ML-конференциях, система с LLM под капотом — это не про возможность выполнения задачи, а про вероятность выполнения задачи. И эта вероятность — всегда не 100%.
Была нарисована схема «где мы можем облажаться» (спойлер: везде).

Смотрите, где качество может «просесть»:
Загрузка: загрузили не ту PDF-ку, или OCR криво распознал таблицу (поехали строки).
Чанкинг: мысль разорвало пополам: начало предложения в одном чанке, конец — в другом.
Эмбеддер: модель не чувствует нюансов. Для нее «бетон» и «цемент» могут быть одним и тем же, а для строителя — нет.
Поиск (Retrieval): система нашла пять релевантных кусков, а мы решили подавать в LLM только четыре. А самый важный ответ был в пятом.
Промт пользователя: строители — не промт-инженеры. Они могут в окно чата просто написать слово «Бетон» (без дополнительных пояснений). Или использовать ненормативную лексику (я это не проверял, но риски есть всегда). Причём плохой промт портит качество два раза - сначала на основе плохого промта находятся плохие чанки, а потом этот же плохой промт вместе с плохими чанками прокидывается на вход LLM.
Ранжирование (Reranking): подали в модель чанки в неправильном порядке, "закопав" самый нужный где-то посередине.
Отображение ответа: пользователь прочитал текст и расстроился — он хотел ответ больше (или меньше). Он хотел нумерованный или маркированный список. Он был бы в восторге от mermaid-схемы. Даже если ответ в целом верный — плохо оформленный ответ будет восприниматься хуже (а хорошо оформленный — лучше).
Делаем свой бенчмарк «на коленке»
Возник вопрос: как это измерить? Бенчмарки, которые есть на рынке (MMLU и прочие), содержат олимпиадные задачи по математике или биологии. Я не уверен, что наши инженеры на работе решают олимпиадные задачки по биологии. Нам нужно проверять знание ГОСТов и СНиПов.
Если LLM – это вероятностная история, и мы хотим использовать её для решения конкретной бизнес-задачи, то мы должны оценивать результат от LLM в виде вероятности выполнения конкретной бизнес-задачи.
Подкрепление этой мысли я нашёл в кейсе коллег про Робота Макса на «Госуслугах» — там тоже ушли от «абстрактной» оценки модели к проверке качества ответов на своих данных.
Так как команда у нас небольшая, мы решили сделать свой бенчмарк просто в Excel.

Механика простая:
взяли наши реальные документы;
экспертно написали по ним вопросы и «эталонные ответы»;
наша тестировщица (у нее, кстати, лингвистическое образование) прогнала вопросы через систему и выписала ответы.
Оценивали так:
1 балл — хорошо.
0.5 балла — средненько, но источники указаны верно.
0 баллов — плохо.
Складываем, делим на количество вопросов, получаем число. Все прозрачно! А с этим числом — работаем дальше. Смотрим на схему потерь в RAG-системе, пробуем «подкрутить» какой-либо из этапов, и затем отслеживаем изменение метрики.
Если бы мы сделали такой бенчмарк сразу, то сэкономили бы, наверное, месяц разработки, исключив хаотичные эксперименты.
Что я понял за 4 месяца
В целом кейс по внедрению LLM я оцениваю как успешный. Мы не только попробовали новую технологию, но и добились вменяемого результата. Это направление планируем развивать дальше: повышать качество ответа, внедрять LLM для извлечения сущностей и проверки документации.
Резюмируя наш путь «от страха к успеху», хочу поделиться тремя главными выводами:
Фокусируйтесь на бизнес-задаче, а не на технологии. Не делайте LLM ради LLM. Игнорируйте хайп. Ваш ИИ должен решать конкретную проблему (в нашем случае — поиск информации в документах), а не рассказывать анекдоты про стройку.
Продукт с «LLM под капотом» не должен делать ставку только на ИИ. Иногда простые «хаки» в интерфейсе могут снять проблемные вопросы, например, подозрение на галлюцинации.
Создайте бенчмарк «для себя» в начале проекта. Это сэкономит вам не только время на пустые эксперименты, но и нервы — и свои, и команды!
Спасибо, что дочитали! Буду рад ответить на вопросы в комментариях. Рассказывайте, как вы внедряете нейросети в свои рабочие процессы. Особенно если вы — производственная или строительная компания!
И пожалуйста, напишите нам на afida@gazpromcps.ru, если вы хотите сократить время на поиск нужных документов в десятки раз, снизить риски ошибок из-за устаревших версий и автоматизировать обработку больших архивов с помощью ИИ!