Почему LLM игнорирует системный промпт: история диалога сильнее инструкций. ai-агенты.. ai-агенты. few-shot.. ai-агенты. few-shot. llm.. ai-агенты. few-shot. llm. Natural Language Processing.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение. Отладка.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение. Отладка. отладка LLM.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение. Отладка. отладка LLM. промпт-инжиниринг.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение. Отладка. отладка LLM. промпт-инжиниринг. системный промпт.. ai-агенты. few-shot. llm. Natural Language Processing. голосовые ассистенты. Голосовые интерфейсы. искусственный интеллект. история диалога. контекст модели. Машинное обучение. Отладка. отладка LLM. промпт-инжиниринг. системный промпт. чат-боты.

Знакомая ситуация: в системном промпте четко написано, чего модель делать не должна ни в коем случае, а она все равно это делает. Вы переписываете правило, добавляете «ВАЖНО», капслок и примеры, становится чуть лучше, а потом снова за старое. В какой‑то момент начинаешь подозревать модель, ее настройки (например, температуру) или провайдера.

Мы наступили на это в голосовом ассистенте и разобрали случай до конца: переиграли боевые запросы и меняли по одному фактору за раз. Промпт оказался ни при чем. Модель слушалась собственной истории диалога, и ниже я покажу, как мы это нашли, какие получились цифры и что с этим делать, если вы строите на LLM чат‑бота, агента или ассистента.

Где это возникает

Ассистент слушает созвон и подсказывает пользователю ответы на экране. В режиме автоответа модель видит каждую фразу собеседника и сама решает, нужна ли подсказка или реплику лучше пропустить. Для пропуска у нее есть служебный ответ NOOP, который пользователь не видит.

Задача «понять, когда промолчать» встречается чаще, чем кажется. Бот в рабочем чате должен отвечать, только когда к нему обратились. Бот поддержки на части тем должен звать оператора вместо ответа, голосовой ассистент не должен перебивать на каждое «угу», а агент перед рискованным действием должен остановиться и переспросить. Схема везде одна: правило в системном промпте и длинный диалог, в котором модель должна соблюдать это правило раз за разом.

Что пошло не так

На тестовом наборе реплик модель пропускала 78% лишних, на эту цифру мы и рассчитывали. На реальных созвонах картина оказалась другой: модель отвечала почти на все подряд, включая «угу», рассуждения вслух и длинные объяснения собеседника. Подсказки наезжали друг на друга, и нужная терялась среди лишних. Политика в промпте при этом была подробная, с примерами того, на что отвечать и на что нет, а ее правки давали эффект в пределах шума.

Как искали причину

Первое правило отладки LLM — воспроизвести боевой запрос байт в байт. Имеется в виду ровно то, что ушло в модель: системный промпт, история диалога, контекст и параметры, а не похожий промпт в плейграунде. Дальше меняем один фактор за раз и смотрим, как меняется доля правильного поведения. Мы взяли размеченные ходы, где подсказка была нужна и где нет, и прогнали варианты.

Вариант

Молчит, где надо

Отвечает, где надо

Боевой запрос как есть

16%

100%

temperature: 0

21%

100%

Без сводки разговора

17%

93%

История: 4 последних обмена

37%

100%

История: 2 последних обмена

48%

100%

История: 1 последний обмен

65%

90%

Без истории

78%

82%

Боевой запрос воспроизвел поведение прода, так что стенд был честным. Температура дала плюс 5 пунктов, сводка разговора не дала ничего. История двигает результат монотонно и сильно: с полной историей модель молчит там, где надо, в 16% случаев, без нее — в 78%. Это ровно цифра тестового набора, потому что туда историю просто не передавали.

Почему история сильнее промпта

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

user: Угу. Ну вот, смотри, тут у нас доска.

assistant: [развернутый ответ на три абзаца]

Получается few‑shot. Обычно его включают намеренно и кладут в промпт несколько примеров, чтобы задать формат ответа. Здесь мы включили его случайно, и он учил обратному: на любую реплику отвечать развернуто. Десятки примеров перевешивают целый абзаца правил, и это петля: чем чаще модель ответила на лишнее, тем увереннее она отвечает дальше. На длинном монологе собеседника модель с полной историей молчала в 4% случаев, без истории — в 78%.

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

Что не сработало

Прежде чем резать историю, мы попробовали способы помягче. Первым была строка в промпте «прошлые ответы — не образец»: молчание выросло до 54%, но модель стала пропускать 10% нужных ответов. Инструкция против примеров снова проиграла, да еще и с побочным эффектом.

Второй вариант — писать в историю и NOOP‑ходы, то есть пары «реплика → NOOP», не показывая их в интерфейсе. Это дало 52% молчания при потере от 1 до 7% нужных ответов. Лучше исходного, но хуже короткой истории!

Третьей была эвристика «не отвечать, пока человек говорит». Она убирает 9% лишнего и теряет 11% нужного, потому что настоящие уточнения часто звучат как перебивка: «Почему?», «Что это значит?». temperature: 0, как видно по таблице, добавляет 5 пунктов и проблему не решает.

А ответы не испортятся без истории?

Страх понятен: история нужна, чтобы ответ учитывал сказанное десять минут назад. Мы проверили это вслепую. На 45 вопросов, где ответ был нужен, сгенерировали по два ответа в двух вариантах, с полной историей и с двумя последними обменами, а сводка разговора и профиль пользователя были одинаковыми. Четыре ответа в случайном порядке оценивали восемь агентов на Claude, и каждый видел полную историю до этого момента. Оценка шла по шкале от 1 до 5 плюс флаги: пересказ старого вместо ответа на новое, противоречие истории, выдумка.

Полная история

2 обмена

Средняя оценка

3,40

3,52

Пересказ прошлого вместо нового

18

12

Противоречит истории или профилю

11

8

Промолчал там, где был нужен ответ

0

1

Разница в средней оценке в пределах шума: два ответа одного варианта на один вопрос расходятся сильнее, чем варианты между собой. Зато всплыли два неожиданных наблюдения. Полная история не защищает от противоречий, с ней их даже больше, потому что модель путается в собственных прошлых ответах. Кроме того, полная история тянет пересказывать: на «а третий код?» модель снова перечисляла первые два, а на «назови один код из 3 xx» выдавала весь ряд от 100 до 504.

Потом мы прогнали ту же схему на других типах разговоров: технический созвон, разговор со службой поддержки, звонок на двух языках. Модель пропускает лишнее в 62% случаев вместо 23% и отвечает на 98% нужного, а по слепой оценке ответы не хуже. Цена — редкие пропуски, примерно один на сорок нужных ответов. Почти все они приходятся на короткие уточнения вроде «Угу. Какие?» и случайны: при повторе модель на них отвечает.

Рецепт

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

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

Посчитайте, чему учит история. Сколько в ней примеров нужного поведения и сколько ненужного? Если модель должна иногда молчать, а в истории нет ни одного пропуска, вы явно ее учите НЕ молчать.

Разделите решение и ответ. Решение «отвечать или нет» модель может принимать с короткой историей, а сам ответ писать с полной. Мы обошлись одним вызовом с историей из двух обменов, по качеству этого хватило.

Мерьте на реальном потоке. Наш тестовый набор был аккуратным, но без истории, и показал 78% там, где прод давал 16%. Если в тестах нет того, что модель видит в бою, тесты меряют другую систему.

Грабли по дороге

Регулярка с честным комментарием. Функция «похоже на вопрос» искала подстроки какs, чтоs и когда в любом месте фразы, поэтому «так что» и «то, что» тоже считались вопросом. Над ней в коде было написано:

// MEASUREMENT ONLY. NEVER use this to filter fresh speech.

Функцию написали для замеров, а потом переиспользовали ровно там, где комментарий это запрещал. Комментарий видит только тот, кто открыл файл, а имя функции видно в каждом месте вызова.

Промежуточные результаты распознавания. Распознавание речи в стриминге отдает промежуточные гипотезы, которые потом переписываются. Мы считали границу «это уже обработано» по длине текста вместе с ними и иногда резали фразу посреди слова, так что в запрос уходило «Тести» вместо вопроса. Границу стоит считать только по финальному тексту.

-1 в логах. Поле «сколько миллисекунд назад говорил человек» равно -1, если данных нет. Регулярка [^0-9]+(?P<x>[0-9]+) в запросе к логам честно выдергивала из -1 единицу, и метрика получилась 33% вместо 9%. Правильно так: (?P<x>-?[0-9]+) и отдельный фильтр x >= 0.

Что в итоге

Сейчас решение «отвечать или молчать» модель принимает с короткой историей, а запросы, которые пользователь отправляет сам, идут с полной. На реальном потоке модель стала молчать заметно чаще, хотя и реже, чем на стенде, и это расхождение мы еще разбираем. Когда разберемся, напишу продолжение.

Главный вывод для меня такой: история диалога — это тоже промпт, причем самый сильный. Если модель упорно делает не то, что написано в инструкциях, сначала посмотрите, каким примерам она следует. Если вы сталкивались с похожим, например с ботом, который не умеет промолчать, или с агентом, который к середине диалога «забывает» правила, расскажите в комментариях, как решали.

Автор: solominqa

Источник