vvs35.ru боты, парсеры, серверы
УслугиГотовые решенияБлог принимаем заказы

Бот называл цену чужой услуги. Дело было не в нейросети

Я поставила на этот сайт ИИ-бота — того самого, что отвечает в правом нижнем углу. Через несколько часов он начал уверенно называть посетителям неправильные цены. Причина оказалась не в модели, не в промпте и не в базе знаний, а в одной строке кода. Рассказываю целиком, потому что ошибка типовая, а ищут её обычно не там.

Что происходило

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

— Сколько стоит ускорить сайт?
Ускорение сайта — от 5 000 ₽. Для начала можно провести
бесплатную диагностику.

В базе написано 3 500. Пять тысяч — цена другой услуги, стоящей в тексте по соседству. Дальше хуже:

— Сколько стоит ИИ-бот?
Содержание ИИ-бота стоит от 6 500 ₽ за пакет на 100 миллионов
токенов в год.

6 500 — это стоимость пакета токенов у поставщика нейросети, то есть расходы клиента после запуска, а вовсе не цена работы. Бот взял число из соседнего раздела и подал его как цену услуги.

Самое неприятное здесь не сама ошибка, а её тон. Бот не сомневался. Он не сказал «уточните» и не предложил связаться — он назвал сумму, как будто прочитал её в прайсе.

Первая гипотеза была неверной

Первое, что приходит в голову: поиск слабый, надо взять модель поумнее. У меня стоял простой поиск по словам, и логика казалась очевидной — заменить его на нейросетевые эмбеддинги, которые ищут по смыслу, и всё наладится.

Я почти так и сделала. Остановило одно: прежде чем менять, стоит померить. Иначе потом не отличишь «стало лучше» от «стало иначе».

Как мерили

Сделала простой стенд. Список вопросов, по каждому известно, какой кусок базы правильный. Прогон считает, попал ли нужный кусок на первое место и в тройку. Отдельно — вопросы не по теме, на которых бот обязан молчать.

Ключевая деталь, без которой замер бессмысленен: часть вопросов отложена. На них ничего не настраивается, они смотрятся один раз, в конце. Это защита от подгонки — о ней ниже, она в этой истории сыграла главную роль.

Первый же прогон показал, где теряется ответ. Вот что находил поиск на вопрос про ускорение сайта:

0.259  Стартовые цены — в разделе услуг. Точную назову, когда...
0.199  Ежедневная сводка по рекламным каналам — сколько заявок...
0.186  Речь не о цене разработки — она отдельная, от 5 000 ₽...
0.129  Исправлю ошибку на сайте — от 1 000 ₽. Белый экран...

Нужного куска — того, где написано «ускорение сайта, от 3 500 ₽» — в четвёрке нет вообще. Зато есть три куска, объединённых одним: в них часто встречается слово «цена». Модель получила их и добросовестно взяла ближайшее число.

Вывод, который экономит часы: если бот отвечает не то, сначала смотрите, что доехало до модели. Промпт тут ни при чём — модель отвечала ровно по тому, что ей дали.

Причина: одна строка и порядок действий

В поиске по словам есть стоп-слова — те, что встречаются почти везде и темы не несут: предлоги, местоимения, вопросительные слова. Их выбрасывают, чтобы они не тянули поиск на себя.

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

Код выглядел так:

# сначала отсев
if slovo in STOP or len(slovo) < 3:
    continue
# и только потом начальная форма
slovo = morf.parse(slovo)[0].normal_form

Отсев идёт до приведения к начальной форме. В списке стоп-слов лежит «стоить». В запросе человек пишет «стоит». Совпадения нет — слово проходит дальше, уже потом превращается в «стоить», и участвует в поиске полноправно.

Результат: любой вопрос «сколько стоит X» тянул поиск на любые куски, где часто говорится про деньги, вместо куска про саму услугу X. Чем чаще в тексте слово «цена», тем выше он всплывал.

Починка — переставить порядок и отсеять ещё раз после лемматизации:

if slovo in STOP or len(slovo) < 3:
    continue
if MORF is not None:
    slovo = MORF.parse(slovo)[0].normal_form
    # отсев повторно: «стоит» и «стоимость» отсекаются
    # только после приведения к начальной форме
    if slovo in STOP:
        continue

Плюс в список стоп-слов добавились вопросительные и ценовые слова: «сколько», «стоить», «стоимость», «цена», «какой». Довод простой: тему вопроса несёт название услуги, а не они.

После правки тот же вопрос:

0.280  Ускорение сайта, сайт медленно грузится, долго открывается...
0.086  На сайте, который и так стоял на быстрой схеме, прибавка...

Нужный кусок первый, с отрывом втрое.

Неожиданное: поиск по смыслу проиграл

Дальше я всё-таки собрала смысловой поиск — компактную нейросетевую модель для русского языка, работающую локально. И прогнала оба способа на одних и тех же вопросах.

Способ поискаВерный кусок первымВсего верных решений
Поиск по словам (после починки)17 из 2239 из 45
Поиск по смыслу, компактная модель8 из 2231 из 45

«Умный» поиск проиграл простому, причём заметно. И проиграл он там, где должен был выигрывать: на переформулировках, ради которых его и берут.

Причина видна в цифрах близости. У смысловой модели вопросы по теме дали 0,42–0,70, а вопросы не по теме — 0,30–0,65. Диапазоны перекрываются целиком: порога, который пропускает нужное и не пропускает лишнее, просто не существует. У поиска по словам зазор был широкий: по делу 0,17–0,35, мимо темы 0,07–0,16.

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

Про отложенные вопросы — самое важное

По ходу работы нашёлся вариант настройки, который на рабочем наборе давал 40 верных из 45. Отличный результат, хотелось брать.

Проверка на отложенных вопросах дала 5 из 11 — хуже, чем без всякой настройки.

То есть параметры подобрались под конкретные вопросы, а не под задачу. Без отложенного набора этот вариант уехал бы в работу как улучшение, и обнаружилось бы это на живых посетителях, месяцем позже, в виде «бот что-то странно отвечает».

Правило, которое стоит завести до первой настройки: вопросы делятся на два набора сразу. На одном подбираем, второй не трогаем вовсе до финальной проверки.

Что осталось в остатке

Порог отсечки — цифра, ниже которой бот говорит «в моих материалах нет ответа», — принадлежит не боту, а паре «способ поиска + база». У прежнего способа он был 0,32, у нового 0,15. Число само по себе не значит ничего; переносить его между системами нельзя.

Отсюда практическое следствие: после заметной правки базы знаний порог стоит перемерить. Не потому, что он «сбился», а потому, что изменилась база, а он к ней привязан.


Похожий случай, где код был написан правильно, а вёл себя неверно: промокод, который увеличил сумму заказа. И ещё один, про доверие к настройке по внешним признакам: сжатие, работавшее только для HTML.

Коротко: частые вопросы

Почему ИИ-бот называет неверную цену, хотя в базе всё указано верно?

Чаще всего дело не в модели, а в поиске: до модели доезжает не тот кусок базы. Модель добросовестно отвечает по тому, что ей дали. Проверяется это просмотром найденных кусков до генерации ответа — если нужного куска среди них нет, чинить надо поиск, а не промпт.

Что такое стоп-слова и почему их порядок важен?

Стоп-слова — это слова, которые встречаются почти в любом тексте и потому не несут темы: предлоги, местоимения, вопросительные слова. Их убирают из поиска. Важен порядок: если отсев идёт до приведения слова к начальной форме, то «стоит» не совпадёт со «стоить» из списка и останется в запросе. Тогда вопросы вида «сколько стоит X» начинают притягивать любые куски, где часто говорится про деньги.

Всегда ли поиск по смыслу лучше поиска по словам?

Нет. На небольшой базе поиск по словам с морфологией может оказаться точнее нейросетевых эмбеддингов. В нашем замере на 45 вопросах лексический поиск дал 39 верных решений из 45, а смысловой на компактной модели — 31. Выяснить это можно только замером на своих вопросах: общего ответа не существует.

Как проверить, что улучшение поиска настоящее, а не подгонка?

Разделить вопросы на два набора: на одном подбирать параметры, второй держать отложенным и не смотреть при настройке. Если результат хорош на первом наборе и разваливается на втором — это подгонка. У нас именно так отсеялся вариант, который на подборе давал 40 верных из 45, а на отложенных вопросах — 5 из 11.

Бот отвечает клиентам не то, что нужно? Посмотрю, что доезжает до модели, и скажу, чинить поиск, базу или промпт. Проверить, как это работает, можно прямо здесь — виджет в правом нижнем углу отвечает по моим же материалам.

Спросить ИИ-бота здесь

Читать дальше