Опыт создания AI-консультанта (Часть -1)
2026-02-10
Опыт создания AI-консультанта: Путь от медленного Fine-tuning к быстрому RAG. Часть 1
В 2026 году идея создать собственного AI-ассистента уже не кажется научной фантастикой. Это становится необходимостью для бизнеса, который хочет оставаться конкурентоспособным. Представьте себе консультанта, который доступен 24/7, знает всё о вашем ассортименте, никогда не устает и мгновенно отвечает на самые каверзные вопросы клиентов. Именно такую задачу я поставил перед собой, решив создать умного ассистента для компании-производителя автозапчастей «ВФ-Авто».
Этот путь оказался гораздо сложнее и интереснее, чем я предполагал. В этой серии статей я поделюсь своим опытом, расскажу о допущенных ошибках, тупиковых путях и, в конечном итоге, о решении, которое действительно сработало.
Зачем это нужно в 2026 году? Преимущества AI-агента для бизнеса
Прежде чем погружаться в технические дебри, давайте определимся, зачем вообще бизнесу нужен такой инструмент.
- Мгновенная экспертиза. Клиенту не нужно ждать ответа менеджера. Он получает точную информацию о применяемости запчасти, ее характеристиках и аналогах здесь и сейчас.
- Разгрузка отдела продаж. Ассистент берет на себя рутинные вопросы, позволяя менеджерам сконцентрироваться на сложных сделках и работе с ключевыми клиентами.
- Круглосуточная поддержка. Ваш «лучший продавец» работает без перерывов и выходных, помогая клиентам из разных часовых поясов.
- Стандартизация знаний. Вся информация о товарах хранится в единой базе, исключая ошибки и «человеческий фактор», когда один менеджер знает больше другого.
Цель была ясна: создать не просто чат-бота, а настоящего цифрового эксперта. Мой первый инстинкт был — «научить» модель всему, что мы знаем.
Глава 1: Искушение Fine-tuning и холодный душ реальности
Логика казалась простой: если мы хотим, чтобы модель знала наш товар, нужно ее дообучить (fine-tune) на наших данных. Для экспериментов я использовал вполне доступное «железо»: видеокарту NVIDIA GeForce 3060 с 12 ГБ видеопамяти.
Попытка №1: Модель Qwen-1.5B
Я начал с относительно легковесной модели Qwen-1.5B. Процесс дообучения прошел на удивление гладко. Модель действительно «впитала» в себя данные о товарах. Но когда я попытался использовать её, меня ждало разочарование.
Проблема: катастрофически долгое время ответа.
Время генерации ответа доходило до 96 секунд. Можете представить себе клиента, который ждет ответа полторы минуты? Это неприемлемо.
При этом загрузка видеопамяти составляла всего около 5 ГБ. Казалось бы, есть огромный запас, но ни свободная память, ни тензорные ядра не спасали ситуацию. Скорость инференса (генерации ответа) была узким местом.
Попытка №2: Модель Qwen-3B
Я предположил, что проблема может быть в самой модели, и решил попробовать более «умную» и крупную версию — Qwen-3B. Качество ответов действительно выросло. Модель стала лучше понимать контекст и давать более точные рекомендации.
Но за это пришлось заплатить.
- VRAM: Загрузка видеопамяти при старте подскочила до 9 ГБ, оставив совсем небольшой запас.
- Скорость: Время ответа... практически не изменилось. Оно все так же колебалось в районе 80-90 секунд.
Я оказался в тупике. Прямое дообучение давало либо быстрые, но глупые ответы на совсем маленьких моделях, либо качественные, но бесконечно долгие — на моделях побольше. Стало очевидно, что для создания отзывчивого консультанта на доступном оборудовании нужен другой подход.
Глава 2: Поворот к RAG. Как заставить модель «подсматривать»
После неудач с fine-tuning я решил кардинально сменить стратегию и сфокусироваться на архитектуре RAG (Retrieval-Augmented Generation).
Идея RAG элегантна: вместо того чтобы заставлять модель «помнить» все данные, мы даем ей возможность «подсматривать» в базу знаний в реальном времени.
Процесс выглядит так:
- Пользователь задает вопрос (например, «нужна прокладка для двигателя КАМАЗ Евро-2»).
- Система не передает вопрос сразу модели. Сначала она ищет в специальной векторной базе данных (в моем случае — Qdrant) наиболее релевантные документы, описывающие нужные прокладки.
- Затем система формирует новый, расширенный запрос к LLM, который включает в себя и исходный вопрос пользователя, и найденные фрагменты из базы знаний.
- Большая языковая модель (LLM) получает этот контекст и генерирует ответ, основываясь на предоставленных ей фактах, а не на своей «памяти».
Этот подход обещал решить мои главные проблемы:
- Скорость: Модели не нужно «напрягать» все свои знания, она работает с небольшим, релевантным фрагментом текста.
- Актуальность: Чтобы обновить знания консультанта, достаточно обновить базу данных, а не переобучать всю модель.
- Точность: Модель не «галлюцинирует», так как ее ответы жестко привязаны к найденным документам.
Глава 3: Подготовка данных — фундамент для RAG
«Мусор на входе — мусор на выходе». Это золотое правило работает и для LLM. Качество ответов AI-ассистента на 90% зависит от того, как мы подготовим для него базу знаний.
Шаг 1: Извлечение (Extraction)
Первым делом нужно выгрузить данные. У меня было два пути:
- Парсер: Написать скрипт, который будет обходить сайт и собирать информацию из разных полей (Характеристики, Применяемость, Марка). Это хороший вариант, если данные на сайте разбросаны по разным таблицам в базе данных.
- Прямая выгрузка: Выгрузить контент всех страниц напрямую из базы данных. Этот путь быстрее, но есть риск получить неполные или «грязные» данные.
Я выбрал первый путь. Вот пример «сырых» данных, которые я получил для одного товара:
{
"url": "https://vfavto.ru/navigacziya/proizvoditeli/kamaz/kolczo-upl.-7405-1303172.html",
"title": "Кольцо упл. системы охлаждения ( 172 )",
"normalized_title": "кольцо уплотнительное системы охлаждения",
"description": "Уплотнительное кольцо с артикулом 7405-1303172— это уплотнительный элемент для соединений в системе охлаждения двигателей КАМАЗ.",
"article": "7405-1303172",
"price_full_string": "Цена по запросу",
"characteristics": {
"Артикул": "7405-1303172",
"Тип": "кольцо уплотнительное системы охлаждения",
"Материал": "Силиконовый компаунд (термостойкий эластомер)"
},
"applicability": {
"Автомобили": "КАМАЗ",
"Двигатели": "Двигатели КАМАЗ",
"Узел": "Система охлаждения"
}
}
Шаг 2: Нормализация и обогащение
Сырые данные нужно привести к единому виду. Например, в заголовках часто встречаются сокращения: «упл.», «рем-кт», «дв.». Чтобы модель их понимала, я написал простой скрипт для их замены на полные слова.
replacements = {
"упл.": "уплотнительное", "уплотнит.": "уплотнительное",
"рем-кт": "ремкомплект",
"дв.": "двигатель",
"гбц": "головки блока цилиндров",
"пркл.": "прокладка",
}
# ... и так далее
Далее я написал конвертер, который превращает плоскую структуру в обогащенный «цифровой паспорт» товара. Я добавил туда новые поля: brands, product_type, search_keywords и, самое главное, embedding_text — специальное текстовое поле, которое будет превращено в вектор для поиска.
Вот что получилось после обработки:
{
"id": 85431,
"url": "https://vfavto.ru/navigacziya/proizvoditeli/kamaz/kolczo-upl.-7405-1303172.html",
"title": "Кольцо упл. системы охлаждения ( 172 )",
"normalized_title": "кольцо уплотнительное системы охлаждения",
"description": "Уплотнительное кольцо с артикулом 7405-1303172— это уплотнительный элемент для соединений в системе охлаждения двигателей КАМАЗ.",
"brands": ["МАЗ", "КАМАЗ"],
"product_type": "Кольцо",
"search_keywords": [
"кольцо уплотнительное системы охлаждения",
"7405-1303172",
"МАЗ",
"КАМАЗ"
],
"embedding_text": "Товар: Кольцо упл. системы охлаждения ( 172 ) | Описание: Уплотнительное кольцо с артикулом 7405-1303172— это уплотнительный элемент для соединений в системе охлаждения двигателей КАМАЗ. | Марки: МАЗ, КАМАЗ | Характеристики: Артикул: 7405-1303172; Тип: кольцо уплотнительное системы охлаждения; Материал: Силиконовый компаунд (термостойкий эластомер) | Применяемость: Автомобили: КАМАЗ; Двигатели: Двигатели КАМАЗ; Узел: Система охлаждения"
}
Для приведения разных написаний брендов и типов товаров к единому стандарту я использовал словари-мэппинги. Я назвал этот процесс «слова к слову» — не судите строго, я небольшой выдумщик названий :)
# Приводим 'газель', 'волга' к 'ГАЗ'
self.brand_mapping = {
'газ': 'ГАЗ', 'уаз': 'УАЗ', 'волга': 'ГАЗ',
'газель': 'ГАЗ', 'камаз': 'КАМАЗ', 'маз': 'МАЗ',
}
# Приводим 'рукав' и 'патрубок' к одному типу
self.product_type_mapping = {
'прокладка': 'Прокладка', 'кольцо': 'Кольцо',
'рукав': 'Рукав/Патрубок', 'патрубок': 'Рукав/Патрубок',
}
В следующей части я расскажу о том, как я использовал эти подготовленные данные для генерации синтетических вопросов и ответов, чтобы протестировать и улучшить точность поиска в векторной базе, и покажу, как всё это работает вместе в реальном времени.
Продолжение следует...