Привет! Меня зовут Александр Золотых, уже два года я работаю во ВкусВилле разработчиком ИИ-решений. В этой статье хочу рассказать, как мы сделали карманного консультанта по клиентократии — и зачем вообще он понадобился.

ВкусВилл работает по клиентократии — модели управления, которую развивает и распространяет система управления  Beyond Taylor. Основная особенность клиентократии — фокус на клиенте, когда все процессы компании выстраиваются для удовлетворения его потребности. Модель инновационная: погружаешься, и возникает множество вопросов. Конечно, лучше спросить и узнать, чем не спросить и не узнать, но не всем и не всегда это просто. Значит, нужно снижать порог входа и сделать описание модели ближе к изучающему.

Именно из этого понимания у нашей команды и появилась идея карманного консультанта — инструмента, который готов отвечать на все «глупые» и каверзные вопросы. Мы поделились замыслом с коллегами из Beyond Taylor, получили их поддержку и приступили к реализации. Так родилась наша первая задача с тем, что сейчас называется RAG (Retrieval-Augmented Generation).

Конечно, есть готовые решения (Notebook LM, Нейроэксперт), но они имеют несколько минусов:

  1. Отсутствие интеграций (Базы знаний с Готовым решением и Готового решения с сайтом / интерфейсом заказчика)

  2. Низкое качество на узком домене знаний.

Сказ 1: О том как мы модель выбирали

С горящими глазами мы бросились решать эту задачу и первым делом взяли известный LangChain и поставили туда Qwen 32, как самую “умную” (лучшую по совокупности параметров) (если верить метрикам) из открытых и развернутых (на тот момент) на Hugging Face.

Далее нам предстояло разбить текст на такие куски, которые бы могли поместиться группой в промпте модели, предоставив ей необходимые знания, на основе которых будет отвечать модель. Разбив документ автоматически, мы столкнулись с первой проблемой – контекст.

Фраза: «Она была очень захватывающей и полезной».
Вопрос: «Кто она?»

Согласитесь, тут без контекста не получится ответить на вопрос, это может быть «Игра», а может быть «Книга» или «Проза», а может «Лекция»? Что же делать? Мы были зеленые, документ был маленький, поэтому долгим рабочим днем было принято волевое решение разбить файл вручную.

Мы получили 150+ кусков текстов, теперь надо было выбирать из них те, которые подходят под конкретный вопрос пользователя, для этого мы воспользовались bm25.

Связав тексты таким алгоритмом, мы выяснили несколько новых проблем:

  1. Модель жестко проседает в некоторых моментах дня, предположительно, когда высокая нагрузка. Просадки отражаются на качестве. Это вам и зацикливание на паре символов, и банальная генерация без конца.

  2. К тому же, обнаружили нестабильность работы с HF API для Qwen-like моделей, которые по середине генерации могут скатиться в другой язык.

  3. LangChain не позволяет прокинуть параметры внутрь node. Допустим, у нас 10 node и они соединены в pipeline. И вот наша задача в десятую node передать параметр file_path, чтобы узел понимал, куда сохранить результаты. Решение, которое мы нашли, заставляет выдать этот параметр выходом для каждого узла ранее. На одном параметре не видно проблемы, но если у нас по одному параметру на каждый узел? У первого узла будет 9 параметров, проходящих через него транзитом и не использующихся. А если, о ужас, понадобится заменить последний узел и поменять в нем входные параметры?

  4. Документация! Мой любимый пункт. За время поиска решений на 1 и 2 пункт мы нашли 3 разных куска кода, которые решали одно и тоже разными частями библиотеки. Но сюр в том, что ни один из этих кусков не работал. Библиотека развивается и переписывается в разы быстрее, чем обновляется документация и гайды (в том числе и официальные).

Заменив LangChain на кастомную разработку (об этом, может быть, в следующей статье), мы перешли с китайской модели на GigaChat (здесь и далее мы приводим свои результаты, они могут отличаться от ваших из-за того, что модель всё время развивается, и из-за того, что мы могли допустить неточности в работе с данной моделью первый раз). Кроме LLM, мы взяли у них и Embedding-токены, и собрали поиск на основе FAISS (Векторное хранилище).
Это дало нам существенный прирост, но...

Сказ 2: О том как мы узлы общаться учили

Мы избавились от переключения языков и бесконечных генераций несвязного текста, и даже собрали рабочий pipeline, но пришел мой лидер и все сломал выяснилось, что вопросы можно задавать как душе угодно.

—Расскажи про клиентократию?
— Что такое клиентократия?
— Есть ли клиентократия на Марсе?

Тогда мы решили сделать переводчика с человеческого на нормальный.
LLM принимает вопрос пользователя и контекст, который нашелся по этому вопросу, а дальше пытается предположить, а что имел в виду пользователь. Дополнительно еще и теги пытается написать, по которым можно было бы легче искать.
Всё звучит круто, но...
Фраза «пиши каждый вопрос с новой строки» заканчивалась провалом.

Если узел выдает неконтролируемый выход, это стопорит всё, что идет далее. Поэтому мы перешли на 2 вещи:

  1. JSON — делает разделение между сущностями более явными, даже при потере некоторого кол-ва символов (ошибки генерации модели).

  2. JSON Repeater — узел, который парсит JSON, а если находит ошибки, то переписывает его.

Теперь, решив все задачи, мы можем представить наш MVP заказчику.

Сказ 3: О том как мы форматировали базу знаний

Чуть ранее для оценки правильности ответов мы собрали пул из 30 вопросов, на которые нам эксперты благородно ответили. Далее мы прокинули документ через Notebook LM и получили ответы строго по контексту, без знаний экспертов, и на этапе второго Сказа мы уже «делали» Notebook LM в каверзных вопросах (экспертная оценка). Однако беда пришла оттуда, откуда не ждали:

  • (Заказчик): В данном вопросе должна быть ссылка на книгу (название).

  • (Мы): Хорошо, сейчас всё проверим.

(Проверяем логи и контекст)

  • (Мы): Так в контексте эта книга не упоминается.

  • (Заказчик): Да, мы знаем, но она есть!

Потянув далее за эту ниточку, мы поняли, что база знаний имеет несколько недочетов, и текущий формат не подходит для работы RAG-системы.

Определив новую проблему, мы решили не оставаться в стороне и помочь заказчику со структурированием информации. Мы взяли наш документ, добавили к нему транскрибирование видеолекций (чтобы нивелировать отсутствие информации) и передали это в LLM. На каждый блок мы задали вопросы, которые объединили в несколько кластеров. Из каждого кластера сделали статью, попросив LLM написать статью, которая бы отвечала на все вопросы и при этом была бы единая. Таким образом получили 20+ markdown файлов, которые передали экспертам для правок.


   

Пока эксперты превращали сгенерированные файлы в структурированную проверенную информацию, в контуре ВВ появилась и «своя» LLM (дистиллированный DeepSeek).

На этом этапе мы получили готовый продукт, который можно улучшать, а значит, нужны метрики, чтобы отслеживать прогресс. Тогда, подсмотрев метрику МЕРА, мы решили сделать свой датасет для оценки нашей RAG. На основе документов (внутренней документации, примерно 10 документов) было задано 85 вопросов тестового характера. Посчитав точность (ответ верен/неверен), мы поняли, что LLM ужасна (спойлер — мы ошиблись)!

Но всё поменялось, когда мы добавили информацию о верности контекста. Оказалось, что LLM ошибается больше в вопросах, на которые не найден нормальный контекст. Мы проверяли, есть ли в контексте целевой документ, по которому задавался вопрос (контекст верен / неверен).

Открыв для себя слабое место, мы бросили свои усилия на него, и после долгого кропотливого труда по выявлению плавающих ошибок мы пришли к:

 

От картинки к картинке могут отличаться LLM под капотом, но это было не важно, наша задача была выявить общие ошибки. Но теперь, когда мы:

1) Построили поисковик;

2) Структурировали контекст;

3) Подружили это всё в едином продукте.

Мы были готовы выбрать её, святая святых: LLM. (Если быть менее пафосным, то просто хотели сравнить GigaChat с внутренним DeepSeek.)

Планы на будущее


? Улучшение поиска — эксперименты с эмбеддингами и векторными хранилищами

? Масштабирование базы знаний — автоматизация обновления и дополнения документов

? Автоматизация — внедрение «продовых» практик для разворачивания микросервисов

? Развитие метрик — более глубокая оценка качества ответов

Мы прошли большой путь от идеи до работающего решения, но впереди еще много интересных задач!

С Вами была команда Центра Экспертизы ИИ компании ВкусВилл, до новых встреч.

P.S. Ждем ваших комментариев, как вы решали подобные задачи и с какими трудностями столкнулись.

Благодарим за помощь в написании материала @Andriljo.

Комментарии (1)


  1. dibu28
    06.09.2025 07:34

    Перебрал несколько моделей, пока остановился на ColbertV2 для эмбеддингов и поска кусков документов, gpt-oss-20B для ответов. Связка с dense эмбеддингами и Qwen3 отвечала хуже. Хотя тут зависит больше от того что нравится пользователям(или что им нужно по задаче) короткие и быстрые ответы или длинные ответы.