Сообщения, роли и системная инструкция
Работа с LLM API: продвинутое · Урок 2 / 21
Диалога нет, есть массив
Модель не хранит вашу переписку. Каждый вызов она получает полный список сообщений заново и не помнит, что было в предыдущем запросе. Ощущение памяти создаёт ваш код, который склеивает историю и отправляет её целиком. Из этого следует практический вывод: длина диалога напрямую превращается в деньги и задержку, и растёт она квадратично, если вы отправляете всю историю на каждом шаге.
Роли и что они значат
- system — правила поведения на весь вызов. Обычно идёт первым и получает повышенный вес при конфликте с остальным.
- user — то, что пришло от человека. Здесь всегда недоверенные данные.
- assistant — прошлые ответы модели, включая её запросы на вызов инструментов.
- tool — результаты, которые ваш код вернул на запрос модели. Каждый такой ответ должен ссылаться на идентификатор вызова.
messages: system правила, формат, границы user первый запрос пользователя assistant запрос на вызов инструмента (id вызова) tool результат вызова (тот же id) assistant финальный текст user следующий запрос
Что кладут в системную инструкцию
Системное сообщение задаёт роль, формат ответа, границы и правила отказа. Оно неизменно между вызовами, поэтому именно оно первым кандидат на кэширование префикса — если провайдер это умеет, а системная часть длинная и постоянная, вы платите за неё по сниженной ставке. Меняющиеся данные пользователя туда класть нельзя: они ломают кэш и смешивают доверенное с недоверенным.
Разграничение ролей — не косметика, а первая линия защиты. Модель обучена относиться к системным правилам как к более приоритетным. Если вы склеиваете инструкцию и пользовательский текст в одну строку роли user, вы отдаёте это преимущество бесплатно: строка «игнорируй предыдущие указания» внутри отзыва клиента получает тот же статус, что и ваши правила.
Как обрезать историю
- Окно последних сообщений. Дёшево и предсказуемо, но теряет факты из начала разговора.
- Сжатие в резюме. Старые сообщения заменяются коротким текстом состояния. Стоит одного лишнего вызова, зато держит длину постоянной.
- Структурное состояние. Хранить не текст диалога, а разобранные поля — заказ, шаг, подтверждённые параметры — и собирать промпт из них.
Третий вариант почти всегда лучше, если задача не является свободной беседой. Он даёт постоянную длину контекста, воспроизводимость и возможность проверить состояние обычными тестами.
Порядок сообщений тоже влияет
Модель по-разному относится к началу и к концу массива. Инструкции, помещённые в самый конец, ближе к моменту генерации и выполняются охотнее, чем те же слова в начале длинного контекста. Практический приём: правила общего поведения держать в системном сообщении, а требование к формату конкретного ответа повторять последней строкой пользовательского сообщения. Это дублирование стоит десяток токенов и заметно снижает долю ответов не в том формате на длинных промптах.
Шпаргалка
- Память диалога создаёт ваш код, модель ничего не хранит.
- Роли разделяют доверенное и недоверенное — не склеивайте их.
- Системная часть постоянна: это кандидат на кэш префикса.
- Для задач со сценарием храните состояние структурой, а не текстом переписки.
Возьмите один реальный вызов модели из своего кода или проекта. Выпишите его массив messages целиком с указанием роли каждого сообщения и числа токенов в каждом. Отдельно отметьте, что из этого меняется от вызова к вызову, а что постоянно. Затем прикиньте, во что превратится этот массив после десяти реплик диалога, и опишите, какую стратегию обрезки вы выберете и почему именно её.