КЛАСТЕР 01 · ОСНОВЫ УПРАВЛЕНИЕ ОТВЕТОМ ~15 МИНУТ
День 02 · из 35

Управление
формой ответа

Модель ответила — это полдела. Теперь учимся диктовать как именно она должна ответить: в каком формате, какой длины, где остановиться.

Суть урока

У тебя есть три рычага, которыми ты направляешь форму ответа: явная инструкция в промпте (что нужно получить и в каком виде), параметр max_tokens (потолок длины) и stop sequences (триггеры остановки). Без этих рычагов модель отвечает «как считает нужным» — и это всегда хуже, чем «как тебе нужно».

Почему свободный ответ — это плохо

В Дне 1 ты послал запрос и получил какой-то ответ. Если играешь с моделью в плейграунде — норма. Если строишь систему — катастрофа.

Любая система с LLM состоит из двух частей: модель что-то генерирует и твой код это парсит. Если форма ответа не задана, модель свободна — а значит, парсер ломается на каждом втором ответе.

Свободно

Что вернёт модель

Запрос: «Дай 3 идеи названия для кофейни».

«Конечно! Вот несколько отличных идей:
1. Утренний шёпот ☕
2. Кофейная пауза
3. ...»
Контроль

Что вернёт модель

Тот же запрос + «Ответь только JSON-массивом строк, без преамбулы и эмодзи».

["Утренний шёпот", "Кофейная пауза", "Тихая заря"]

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

Управление формой ответа — это не косметика. Это разница между «прототипом, который иногда работает» и «системой, на которую можно опираться».

Три рычага управления

В твоём распоряжении три механизма. Они работают на разных уровнях и решают разные проблемы:

Явная инструкция в промпте

Самый мощный и универсальный рычаг. Ты прямо в тексте промпта говоришь модели, что и как ты ждёшь.

Что можно указывать:

  • Формат: «ответь только JSON», «верни таблицу markdown», «дай список через запятую без переносов»
  • Структура: «обязательные поля — name, price, tags», «не более 5 пунктов»
  • Тон и длину: «в одно предложение», «академический стиль», «без преамбулы и финальных фраз»
  • Чего не делать: «не извиняйся», «не пиши вводных фраз типа Конечно!»

Где работает: везде. Это базовый и самый управляемый рычаг.

Параметр max_tokens — потолок длины

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

Что это даёт:

  • Контроль расходов — ответ не может быть длиннее, чем ты заплатил
  • Защита от «болтливости» — модель не уйдёт в простыню на 2000 токенов
  • Предсказуемое latency — генерация всегда занимает не больше Х секунд

Главное: это не способ задать длину. Если ты поставил max_tokens=100, а модели нужно 200 для нормального ответа — она просто оборвётся посередине. Ответ придёт с finish_reason: "length", и это будет битый ответ.

Привычка: ставить max_tokens с запасом, а реальную длину контролировать через инструкцию в промпте.

Stop sequences — условие остановки

Массив строк. Как только модель в процессе генерации выдаёт любую из этих строк — она немедленно прекращает писать. До этой строки. Сама строка в ответ не попадает.

Зачем это нужно:

  • Остановить модель на конкретном маркере: "\n\n", "###", "END"
  • В диалоговых системах — остановить на ролевом маркере: "User:", чтобы модель не отвечала за пользователя
  • При генерации в формате с разделителями — оборвать на ожидаемом символе

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

Каждый из трёх — на своём уровне:

Уровень 1

Семантика

Инструкция в промпте говорит модели что сделать.

Уровень 2

Бюджет

max_tokens задаёт сколько модель может потратить.

Уровень 3

Триггеры

stop sequences задают когда именно остановиться.

Тонкости управления форматом

Самая распространённая задача — заставить модель отвечать в строго определённом формате (чаще всего JSON), чтобы потом этот ответ парсить машиной. Тут есть несколько уровней «строгости».

Самый простой подход. Работает в 80-90% случаев, но иногда модель решит, что красивее будет добавить пояснение перед JSON или обернуть его в markdown ```code-блок```.

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

У OpenAI и нескольких других провайдеров есть параметр response_format: "json_object". Когда он включён, провайдер гарантирует, что в ответе будет валидный JSON — иначе вернётся ошибка.

Что важно: валидныйправильной структуры. Это будет JSON, но какие в нём ключи и типы — зависит от модели. Поля могут быть не те, что ты ждал.

Уровень надёжности — средний. JSON парсится всегда, но содержимое нужно проверять.

Самый надёжный механизм. Ты передаёшь провайдеру JSON Schema — описание ожидаемой структуры (какие поля, какие типы, что обязательно). На стороне провайдера декодер модели физически может выдавать только токены, валидные под эту схему.

Это есть у OpenAI (response_format: { type: "json_schema", ... }), у Anthropic есть аналогичное через tool calling. Многие маленькие провайдеры тоже поддерживают через библиотеку Outlines.

Уровень надёжности — максимальный. Если ответ пришёл — он 100% соответствует схеме. Но: чуть медленнее обычного режима и не все модели это умеют.

Старая, но очень мощная техника: показать модели 2-3 примера правильно оформленных ответов прямо в промпте. Модель будет имитировать формат.

Когда не нужно строгости (например, формат полусвободный — генерация текста с маркерами), few-shot работает лучше схем, потому что показывает стиль, а не только структуру.

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

Решения AI-инженера

Развилка 1. Как формулировать «не делай»

Распространённая ошибка — писать модели длинные списки запретов: «не пиши преамбулу, не используй emoji, не извиняйся, не пиши в конце "надеюсь это поможет"». Это работает плохо.

Модель лучше понимает позитивные инструкции, чем негативные. «Ответь одной строкой JSON» лучше, чем «не пиши длинно и не добавляй комментариев». Когда нужны запреты — оставляй максимум 1-2, действительно важные, и формулируй их в начале промпта.

Развилка 2. Где задавать формат — в system или user

Если формат постоянный для всех запросов системы — клади его в system. Это «правила игры», которые не меняются.

Если формат зависит от конкретного запроса — клади в user. Это «параметр текущей задачи».

Смешивание (формат и там и там) — путь к багам: разные части промпта будут противоречить.

Концептуальные грабли

«Хочу ответ в 50 слов — поставлю max_tokens=80». Не работает. Модель напишет нормальный ответ длиной 150 токенов, и в твоём случае он оборвётся на полуслове. max_tokens — это потолок, аварийная отсечка. Длину задают словами в промпте: «не более 50 слов», «в одно предложение».

Получил «JSON» от модели, кинул в JSON.parse — и в 5% случаев получишь исключение. Модель добавила пояснение, обернула в markdown, поставила лишнюю запятую. Любое использование free-form формата требует обработки ошибок: либо retry с уточнением промпта, либо Structured Outputs с гарантиями.

Если finish_reason === "length", ответ обрезан — независимо от того, как он выглядит. JSON может быть невалидным (пропущенная закрывающая скобка), текст оборван на полуслове. Это не «нормальный ответ покороче», это битый ответ. Лечится увеличением max_tokens или укорачиванием инструкции на «короче».

Практика

Эксперимент 01 · Свобода vs контроль

Один запрос, два уровня контроля

В плейграунде задай вопрос «Расскажи о трёх главных рисках работы с LLM в проде». Один раз — как есть, без инструкций по формату. Второй раз — добавь «Ответь JSON-массивом объектов с полями name, description, severity (high/medium/low). Без преамбулы и пояснений». Сравни — обрати внимание, что во втором случае ответ можно скормить парсеру, а в первом — нет.

Эксперимент 02 · max_tokens вживую

Поймай finish_reason: "length"

Возьми длинный запрос («объясни подробно, как работает HTTPS»), поставь max_tokens в маленькое значение (50-80). Запусти. Посмотри в ответе на finish_reason — он будет "length". Сам текст оборван. Это даст тебе физическое ощущение того, как именно ломаются ответы из-за низкого лимита.

Эксперимент 03 · Сила позитива

Сравни запреты и инструкции

Возьми запрос «Дай совет, как улучшить мотивацию». Прогон 1: «не пиши вводных фраз, не используй списки, не добавляй emoji, не извиняйся». Прогон 2: «Ответь одним абзацем в 2-3 предложения, конкретно и по делу». Посмотри — какой из них модель выполняет точнее. Это даст интуицию про то, что позитивные инструкции работают лучше.

Что в Дне 03
Управление формой — это «как должен выглядеть результат». В Дне 3 пойдём глубже: научимся управлять самим процессом мышления модели. Те же задачи решать четырьмя разными способами и наблюдать, как меняется качество.