Все записи
6 мин

Разные данные, одинаковые ответы: почему мой бот с нейросетью всем писал одно и то же

нейросетикартарапромпты

Если вы делаете бота с нейросетью и вдруг замечаете, что он разным людям с разными вводными отвечает подозрительно похоже, то почти наверняка дело не в баге и не в дубле в базе данных, а в самой природе модели. LLM по умолчанию тянет к среднему, к типовому, к самой вероятной структуре ответа, и если вы её специально не заставляете цепляться за конкретные цифры пользователя, она их аккуратно проигнорит и выдаст вам то же самое в четырёх разных коробках. Я наткнулся на это в своём проекте Картара, где AI делает расклады по нумерологии и астрологии, и история вышла поучительная, так что расскажу как было.

Как я вообще это заметил

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

Я кинул задачу разобраться своему агенту, и он сначала пошёл по этой же дорожке, искать дубли в данных. И вот тут я психанул, потому что чувствовал что он копает не туда: «Че по поводу одинаковых ответов блять? проигнорил сука задачу?» Проблема была не в том что записи одинаковые, а в том что записи РАЗНЫЕ, а тексты к ним нет. Это совсем другой класс бага. Это не дублирование данных, это content similarity, типовость самого вывода нейросети. И пока ты держишь в голове первую гипотезу про дубли, ты в принципе не туда смотришь.

Почему модель так делает

Давайте по-простому. Когда вы скармливаете LLM натальную карту с конкретными планетами и числами и просите интерпретацию, она вообще не обязана на эти цифры опираться. Она обучена выдавать связный правдоподобный текст на тему «астрологический разбор», и самый правдоподобный, самый вероятный такой текст это набор общих формулировок, которые подходят примерно всем. Это же классика, эффект Барнума: «вы человек глубокий, но иногда сомневаетесь в себе» подходит и мне, и вам, и соседу. Это та же типовость, по которой сразу видно, что писала нейросеть — общие места вместо конкретики. Модель не злонамеренно ленится, она просто идёт по пути наименьшего сопротивления, и если в промпте не вшит жёсткий механизм который её туда не пускает, она там и останется.

И вот ключевой момент, на котором всё и держалось. У меня в промпте было написано что-то вроде «опирайся на конкретные планеты, привязывай интерпретацию к числам». Звучит разумно. Но это же просто слова в инструкции. Скептик-агент, которого я гоняю на свои решения, зафиксировал это прямо и грубо: промпт говорит «ссылается на планеты», но это никак не валидируется. То есть я ПОПРОСИЛ модель цепляться за данные, но нигде не ПРОВЕРИЛ что она это сделала. А просьба без проверки в мире нейросетей стоит ровно ноль. Точно так же мой оракул врал во спасение из-за моих же промптов, а не из-за данных или модели. Можно сколько угодно писать в системном промпте красивые требования, но если на выходе никто не сверяет что упомянута именно Венера юзера в его доме, а не абстрактная Венера вообще, то модель спокойно напишет про абстрактную и будет формально права.

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

Куда полез разбираться

Чтобы понять масштаб, пришлось лезть в реальные данные на сервере, глянуть логи, поднять что модель реально нагенерила по разным пользователям и сравнить руками. Тут опять же характерный момент соло-разработки с телефона: я за рулём, агент на ПК дома, и я ему: «Иди епт, проверяй у тебя есть же ссш и доступ к базе». То есть проверка гипотезы это не я открываю ноутбук, это я командую, а Claude Code идёт и достаёт данные. Я код руками не пишу и это давно не секрет, я оркестрирую, а железную работу делает нейросеть под моим управлением.

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

Что вообще можно крутить кроме температуры

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

Если совсем коротко и простыми словами. Температура это насколько модель готова брать менее вероятные слова, грубо «смелость»: поднял, и текст живее и разнообразнее, но и бреда больше. top_p (его ещё зовут nucleus sampling) отсекает хвост маловероятных вариантов, оставляя только ядро, работает в паре с температурой, и часто крутить надо что-то одно, а не оба сразу. Есть штрафы за повторы, presence и frequency penalty, они давят модель когда она начинает жевать одни и те же слова и обороты, и вот это как раз прямо про мою боль с одинаковостью. Есть максимум токенов на ответ. И главное это сам промпт, потому что никакая температура не спасёт, если в инструкции нет требования держаться за конкретику и нет проверки что модель его выполнила.

А вот мысль разделить настройки чата и раскладов, это, мне кажется, и есть правильный вывод из всей этой истории. Это же два разных режима по сути. В чате с AI-персонажем мне нужна живость, тепло, лёгкая непредсказуемость, человеку должно быть приятно болтать, и там температуру повыше, там разнообразие в плюс. А в раскладе мне нужна точность и привязка к данным конкретного человека, там разброс ради разброса только вредит, там важнее чтобы упомянули именно его планеты и его числа. Гонять оба режима на одних и тех же параметрах это как одним соусом поливать и десерт, и стейк. Так что да, разными их сделать стоит.

Что я из этого вынес

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

И ещё про процесс. Эту проблему я поймал не потому что у меня был хитрый мониторинг, а потому что сел и сам прочитал что отдаёт мой продукт двум живым людям. Тут перекликается с моим разбором, как я неделю выкатывал нумерологию в Картаре, там я тоже только в проде, на реальных данных, понял что фича работает не так как задумано. Так что если делаете нейросети для бизнеса или своих продуктов, не ленитесь читать сырой вывод глазами. Метрики покажут что всё зелёное, тесты пройдут, контейнер будет Up, а пользователь будет получать одно и то же в четырёх коробках, и узнаете вы об этом последним. Вот и делайте выводы.