ADG Оставить заявку
Блог Автоматизация 5 мин чтения

RAG над внутренней документацией: ChromaDB + Llama 2 без передачи данных наружу

Строим RAG-систему над runbook клиента: ChromaDB как векторная БД, Llama 2 на локальном GPU - инженеры спрашивают через чат, данные не покидают периметр.

Контекст момента

Retrieval-Augmented Generation (RAG) как паттерн обогащения LLM корпоративными данными без дообучения, 2023

В сентябре мы подняли локальный helpdesk-ассистент через Ollama и честно зафиксировали: треть вопросов из реальной очереди закрывается «из коробки», остальное - нет, потому что модель просто не знает корпоративной специфики. Туда же ушло резюме: следующий шаг - RAG поверх документов клиента.

Следующий шаг случился.

Что такое RAG и почему не дообучение

Retrieval-Augmented Generation - это паттерн, при котором модель не хранит корпоративные знания в весах, а получает их в момент запроса. Упрощённо: пользователь задаёт вопрос, система ищет в базе документов релевантные фрагменты, вставляет их в контекст и отдаёт всё это модели. Модель видит вопрос плюс релевантный кусок runbook - и отвечает опираясь на него, а не на веса.

Альтернатива - файн-тюнинг, то есть дообучение модели на документах клиента. Звучит логично, пока не смотришь на практику: дообучение дорого по GPU-часам, теряет общие знания модели, требует переобучения при каждом обновлении документации. RAG дешевле, гибче и, главное, не требует ML-экспертизы внутри команды - нужны только разработчик и понимание, как устроен поиск.

Задача конкретного клиента

Это тот же клиент, что и в сентябре. Документация - около 300 markdown-файлов: runbook по дежурству, описание сервисов, инструкции по реагированию на инциденты. Всё это лежит в корпоративном Confluence и периодически экспортируется в markdown для офлайн-хранения.

Требования жёсткие: данные не выходят за периметр. Значит, никакого OpenAI, никакого облачного векторного поиска. Всё на железе клиента.

Серверная часть - тот же физический сервер с RTX 4090 24 GB, Rocky Linux 9.

Стек

Выбор получился такой:

  • Модель. Llama 2 13B в формате GGUF через llama-cpp-python. Мы уже работали с ней в августе, знаем характер - это не Ollama, тут больше контроля над параметрами инференса, что для RAG важно: надо управлять длиной контекста и поведением при его заполнении.
  • Векторная БД. ChromaDB. Работает как Python-пакет, не требует отдельного сервера, данные хранит локально на диске. Для 300 документов - более чем достаточно.
  • Эмбеддинги. sentence-transformers, модель all-MiniLM-L6-v2. Маленькая, быстрая, для технической документации на английском и русском работает терпимо. Запускается на CPU - GPU оставляем для инференса.
  • Интерфейс. Простой FastAPI-эндпоинт плюс Telegram-бот. Инженеры дежурства уже сидят в корпоративном Telegram, добавить бота - минимум интеграционных усилий.

Как это работает

Вопрос пользователя
      |
      v
Эмбеддинг вопроса (sentence-transformers)
      |
      v
Поиск top-5 фрагментов в ChromaDB
      |
      v
Сборка промпта: system_prompt + фрагменты + вопрос
      |
      v
Инференс Llama 2 13B (llama-cpp-python, n_ctx=4096)
      |
      v
Ответ с указанием источника (имя файла + номер строки)

Фрагменты - это чанки по 512 токенов с перекрытием 64 токена. Разбивка по токенам, а не по словам или символам, потому что иначе смысловые единицы рвутся в случайных местах.

Индексация

Скрипт индексации прогоняет все markdown-файлы, режет на чанки, считает эмбеддинги и складывает в ChromaDB вместе с метаданными - имя файла, заголовок раздела, дата последнего изменения. Индексация 300 файлов на CPU заняла около 20 минут. Пересчёт при обновлении документации - только изменённые файлы, обнаруживаем по хешу.

Промпт и поведение модели

Это оказалось важнее, чем казалось. Первая версия промпта была наивной - просто «вот документы, ответь на вопрос». Модель охотно отвечала, но придумывала детали, которых в документах не было. Классическая галлюцинация, только теперь она ещё и маскировалась под ответ «из runbook».

Помогло жёсткое ограничение в системном промпте: отвечать только на основе переданных фрагментов, при недостаточной информации явно говорить об этом. Плюс мы добавили вывод источника - какой файл и какой раздел послужил основой. Инженер видит ссылку и может проверить. Это убивает иллюзию оракула и делает инструмент честным.

Что получилось на реальных запросах

Тестировали на выборке дежурных вопросов за последние три месяца. Сравнивали с сентябрьскими результатами без RAG.

Инфраструктурно-специфичные вопросы - «какой endpoint у метрик кластера prod-1», «кто on-call по сервису payments» - теперь закрываются, если ответ есть в документации. Точность заметно выросла именно в этой категории.

Процедурные вопросы - «как заэскалировать инцидент P1» - работают, если runbook написан внятно. Если документ содержит фразы вроде «действовать по ситуации» - модель честно воспроизводит эту бесполезность.

Вопросы не из документации - модель говорит, что информации нет. Иногда добавляет общий ответ из своих весов, хотя мы просили этого не делать. Нужно доработать промпт.

Русский язык всё ещё проседает - Llama 2 периодически переключается на английский в середине ответа, особенно при цитировании технических фрагментов. Раздражает, но дежурные привыкают.

Что не решили

Документация обновляется нерегулярно - часть runbook устарела, и модель честно воспроизводит устаревшие инструкции. Это проблема процесса, не технологии, но пользователи всё равно вопрос задают нам.

Оценка качества ответов - пока субъективная. Нет метрики, нет автотестов над набором «вопрос - правильный ответ». Работаем по ощущениям, что некомфортно.

Текущий статус

Бот запущен в дежурной группе около двух недель. Обратная связь от инженеров - осторожно положительная. Главная ценность оказалась не в том, что бот отвечает вместо человека, а в том, что даёт ссылку на нужный раздел runbook. Поиск по Confluence у клиента так себе - бот находит быстрее.

Интеграция с внутренними системами - отдельная работа: подключить корпоративный SSO для бота, настроить ротацию ключей API, разобраться с правами доступа к разным категориям документов. Это следующая итерация.

Общая оценка: паттерн работает, стек работает, данные периметр не покидают. Галлюцинации есть, но управляемые - пока явно говоришь модели «молчи если не знаешь» и показываешь источник, инструмент остаётся честным.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.