Модуль 3. Инференс: декодирование, температура, контекст

Цель модуля: освоить слой между моделью и пользователем — декодер и контракт общения с моделью. После модуля вы осознанно выставляете temperature/top-p под задачу, понимаете, что такое chat template и почему без него модель «несёт не туда», и знаете, откуда берутся галлюцинации — а значит, готовы выбирать между «положить в контекст» и «положить в веса».

Перед началом: модули 1–2 (токены, attention, «предскажи следующий токен»). Инструменты: песочница сайта, сниппет temperature-topp. Трудоёмкость: ~1–1.5 ч (чтение 40 мин · сниппет и практика 30 мин).

🔥 «Модель стала тупее после релиза» — а модель не менялась

Команда поддержки жалуется: бот стал многословным и «фантазирующим». Никто модель не трогал. Расследование занимает день: в очередном рефакторинге дефолтные параметры генерации поменялись местами — temperature подросла с 0.3 до 0.8. Модель та же, веса те же, ответы другие.

Это самая недооценённая истина работы с LLM: модель и ответ — не одно и то же. Между ними стоит декодер — правила выбора токенов из распределения, которое выдаёт модель, — плюс шаблон диалога и управление контекстом. Этот слой не «настройки для гурманов», а часть дизайна продукта: поддержка, экстракция структурированных данных и копирайтинг хотят разного декодера. И только поняв его, можно честно рассуждать, что чинить — промпт, декодер или веса (модуль 4).

📖 3.1. От распределения к токену: greedy, sampling, temperature

На каждом шаге модель выдаёт логиты — баллы для каждого токена словаря. Превратить их в «следующий токен» можно по-разному:

  • greedy: взять самый вероятный. Детерминированно, но текст закомпонованный и «мертвенный», а в длинной генерации — зацикливается («очень хорошо, очень хорошо, очень…»).
  • sampling: трактовать распределение как вероятности и бросать кубик. Живее, но без регулировки можно вытащить «банан» из хвоста распределения.

Регулятор — temperature. Перед softmax логиты делятся на T: T < 1 заостряет распределение (вероятные токены вероятнее), T > 1 — разглаживает (хвост получает шанс). T → 0 вырождается в greedy. Ключевой факт: T не меняет модель и не «делает её умнее» — она лишь перераспределяет выбор среди тех же кандидатов.

Фильтры хвоста: top-k («рассматриваем только k самых вероятных») и top-p («рассматриваем минимальный набор токенов, чья суммарная вероятность ≥ p»). Отрезание хвоста почти не снижает качество, но убирает катастрофические выборы «банана». Продакшен-дефолты индустрии: T 0.1–0.7, top_p 0.8–0.95; для генерации кода и извлечения данных — T ближе к 0.

Запустите сниппет temperature-topp.py: одни и те же логиты при T 0.2/1/3 дают три разных «характера» ответа, top_p 0.8 отрезает хвост, а пять выборок с фиксированным seed показывают, что «случайность» декодера воспроизводима, если зафиксировать seed. Это базовое требование к evals (модуль 8): нефиксированный декодер делает любой A/B-тест шумом.

Всё это — не учебная абстракция: в рабочем сервисе, который ходит к LLM, запрос выглядит так. Фрагмент боевого контракта (за одним портом — Ollama, OpenAI-совместимые провайдеры, Anthropic — язык один):

go
type Request struct {
    Model            string    // "qwen2.5:7b", URI своей модели
    Messages         []Message // system/user/assistant — тот самый чат-контракт
    MaxTokens        int       // 0 = лимит провайдера по умолчанию
    Temperature      float64   // 0..1; провайдер может клампить
    TopP             float64   // 0 = дефолт провайдера
    FrequencyPenalty float64   // штраф за повторы (-2..2)
    PresencePenalty  float64   // штраф за «уже сказанное»
    Stop             []string  // строки-остановки
}

Разница между этой структурой и сниппетом ниже — только масштаб последствий: за неверной temperature в проде стоят ответы, которые видит пользователь.

📖 3.2. Chat template: договор о разметке диалога

Модуль 2 закончился тем, что модель обучали продолжать текст. Диалоговое поведение в ней появляется после instruction tuning, и обучалась она на размеченных диалогах специальными служебными токенами:

text
<|system|> Ты — ассистент поддержки. Отвечай кратко.<|end|>
<|user|> Как вернуть деньги за курс?<|end|>
<|assistant|>

Роли: system — конституция диалога (тон, правила, границы), читается моделью как самый сильный контекст; user — реплики пользователя; assistant — ответы модели (в конце шаблона модель продолжает именно позицию assistant). Это chat template — у каждого семейства моделей свой формат служебных токенов, и API провайдера обычно прячет его за структурой messages[].

Следствия для практики:

  • Системный промпт — не «мягкое пожелание», а часть контекста с высоким приоритетом, но не гарант: конфликтные инструкции модель может трактовать по-разному, проверяйте на тестах (модуль 8).
  • Few-shot примеры — это 2–5 пар user/assistant прямо в контексте перед реальным вопросом. Дёшево (только токены), мгновенно меняет формат ответов — первая ступень лестницы решений в модуле 4.
  • Кривой шаблон = кривые ответы. Если «скормить» диалоговой модели просто склеенный текст без разметки, она отвечает хуже: вы сдвигаете её из распределения, на котором она обучалась. При дообучении (модуль 5) датасет обязан использовать тот же шаблон, что и инференс, — иначе обучили одному, спрашиваете другое.

📖 3.3. Контекстное окно: бюджет, а не вместилище

Модуль 1 дал единицу измерения (токены), модуль 2 — причину лимита (квадратичная цена attention). Теперь инженерная сторона. Контекстное окно — это бюджет на всё сразу: системный промпт + история диалога + few-shot примеры + документы из RAG + черновики + сам ответ. Превысили — придётся усекать, и вопрос «что выбросить» — дизайн-решение:

  • усечение истории «оставить последние N реплик» — просто, но теряет обещания из начала диалога;
  • резюмирование старых реплик моделью — сохраняет суть, добавляет латентность и цену;
  • «вынести наружу» в хранилище и подкладывать по запросу (эмбеддинги, модуль 1) — так работает RAG.

Правило инженера: считайте бюджет токенов для худшего случая. Виджет поддержки с 8k окном, системным промптом на 400 токенов, тремя few-shot на 1500 и документов на 4000 — уже почти без запаса на диалог.

Насколько быстро контекст съедается на самом деле — замер из рабочего сервиса-оркестратора (агентный цикл: модель отвечает «действием», код выполняет его и возвращает результат). Каждая итерация добавляет +125–150 токенов: ответ модели плюс наблюдение инструмента. На задаче из 11 файлов контекст вырос с 602 до 2 142 токенов за 12 итераций — живой график метрики это и показал. Экстраполяция: 40–50 итераций рефакторинга — 7–9k токенов только на историю, и вторая беда важнее первой: старые шаги становятся шумом, качество решений падает. Решение, которое там применили: детерминированное «rolling summary» — история шагов сжимается по коду (без обращения к LLM), а системный промпт и цель не сжимаются никогда. Приём запомните: сжимайте историю, не трогая конституцию диалога.

📖 3.4. Откуда галлюцинации и «незнание»

Финальный кусок фундамента. Веса модели заморожены в момент окончания обучения; всё, что «знает» модель, — статистика данных той давности. Отсюда три честных вывода:

  1. Ваших фактов в весах нет. Регламент возврата вашей платформы, вчерашняя цена, внутренний API — модели неизвестны, но она обязана продолжить текст уверенно: в этом её работа. Уверенный тон — не признак знания (модуль 2: задача — правдоподобное продолжение, а не истина).
  2. Галлюцинация — не сбой, а режим. Плохо определённый вопрос + sampling из хвоста → правдоподобная выдумка. Снижается холодным декодером, few-shot и прямым запретом «если не знаешь — так и скажи», но не исчезает: проверка фактов — задача системы, не модели.
  3. Свестись к нулю можно только фактами. Есть ровно два места, куда кладут факты: контекст (RAG, модуль 4) или веса (дообучение на своих данных, модули 5+). Это и есть главная развилка всего курса.

📖 3.5. Компромиссы

  • T низкая vs высокая. Низкая — стабильность, повторяемость, фактология; высокая — разнообразие, креативность, и галлюцинации. Под задачу, не «по вкусу», и фиксировать в конфиге версии сервиса (модуль 12).
  • top_p vs top_k. top_p адаптивен (отрезает по вероятностной массе), top_k стабилен (фиксированный список). Обычно достаточно одного; их комбинируют редко.
  • seed vs случайность. Seed делает evals воспроизводимыми, но прячет хвостовые режимы; для тестов гоняют несколько seed.
  • Длинный контекст vs RAG. Вложить всё в 128k проще архитектурно, но дороже за запрос и хуже по точности («иголка в стоге теряется»); RAG дешевле и точнее на фактах, но требует инфраструктуры. Это же сравнение вернётся в модуле 4 как выбор ступени.

💻 Практика

  1. Три характера (10 мин). Запустите temperature-topp.py. По выводу оцените: при какой T «банан» получает заметные проценты? Какая T даёт «поддержку», какая — «копирайтера»?
  2. Сломайте фильтр (10 мин). В сниппете поднимите top_p до 0.99 и опустите до 0.5. Объясните, почему при 0.5 распределение после перенормировки снова похоже на greedy.
  3. Бюджет виджета (15 мин). Посчитайте токен-бюджет своего гипотетического бота: системный промпт (напишите его, ~200 токенов), 3 few-shot, окно 8k. Сколько реплик диалога помещается? Когда пора резюмировать?
  4. Декодер-чеклист продакшена (10 мин). Составьте для своей задачи конфиг: temperature, top_p, seed, max_tokens, и объясните каждую строку одной фразой. Этот же конфиг станет частью «версии модели» в модуле 12.

🧩 Quiz "Проверь себя"

1. Что такое top_p = 0.9?
2. Почему greedy-декодер плох для длинной генерации?
3. Что делает temperature?
4. Зачем few-shot примеры в контексте?
5. Модель уверенно рассказала про ваш внутренний регламент возврата, которого не существует. Почему?

✅ Итоги модуля

  • Настроить декодер под задачу: поддержка/экстракция vs креатив.
  • Показать на сниппете эффект T и top_p и объяснить переносормировку.
  • Объяснить роли system/user/assistant и зачем датасету тот же шаблон, что и на инференсе.
  • Посчитать токен-бюджет приложения с worst case.
  • Назвать два места, куда можно «положить факты», и их цену.

🔗 Мостик к Модулю 4

Фундамент заложен: вы знаете, как текст становится токенами, как attention делает их контекстными, как декодер превращает распределение в ответ и куда деваются факты. Пора к главному инженерному вопросу: у модели «что-то не так» — чинить промптом, подкладывать документы (RAG) или дообучать веса? В модуле 4 построим лестницу решений и научимся выбирать ступень без лишних трат.