Промпты и чат-шаблоны
Каждое отправленное вами сообщение оборачивается в разметку до того, как модель его увидит. Чат-шаблон превращает ваши сообщения с ролевыми тегами в одну плоскую токен-строку, и модель обучена читать именно эту строку — а не ваши аккуратные API-объекты. Правильный конверт — и instruct-модель на 3B ведёт себя прилично; неправильный — и модель на 70B выдаёт словесную кашу.
Конверт: один чат, три диалекта
Ваш API открывает роли: system, user, assistant. Модель видит специальные токены. Один и тот же разговор из двух сообщений, отрендеренный тремя способами.
ChatML — модели семейства Qwen и файнтюны линии Hermes:
<|im_start|>system
You are a concise assistant.<|im_end|>
<|im_start|>user
Summarize this file.<|im_end|>
<|im_start|>assistant
Стиль Llama 2 — системные маркеры вложены в первую инструкцию:
<s>[INST] <<SYS>>You are a concise assistant.<</SYS>>
Summarize this file. [/INST] The file implements a tokenizer... </s>
Стиль Gemma — системной роли нет; инструкции едут на первом пользовательском ходе, а ассистент называется model:
<bos><start_of_turn>user
You are a concise assistant.
Summarize this file.<end_of_turn>
<start_of_turn>model
Тот же разговор, три несовместимых конверта. Модель, обученная на ChatML, не «догадается» про разметку Llama — она примет чужую разметку за обычный текст и ответит в том же духе.
Движки читают шаблон из файла модели
Метаданные GGUF встраивают chat_template (сниппет Jinja). llama.cpp, Ollama, LM Studio и Open WebUI читают его и рендерят ваши сообщения автоматически — вот почему чат работает из коробки с правильным GGUF. Две классические ловушки: сырой эндпоинт дополнения (/completion) пропускает шаблон и суёт модели голый текст — используйте чат-эндпоинт; и GGUF может прийти с отсутствующим или неверным шаблоном — передайте его явно (--chat-template в llama.cpp, Modelfile в Ollama) или возьмите другой квант той же модели у источника, который сделал правильно.
Симптомы неверного шаблона
- Мгновенный пустой ответ — отрендеренный промпт уже выглядит как законченный разговор, и модель сразу выдаёт EOS.
- Утечка ролей — вывод читается как
user: sure thing! assistant:— модель имитирует конверт вместо ответа. - Мусор — словесная каша базовой модели от модели, которая час назад нормально болтала, потому что вы сменили файл, не сменив шаблон.
Для диагностики дампьте отрендеренный промпт — сервер llama.cpp логирует его подробно, OLLAMA_DEBUG=1 показывает его в Ollama. Затем чините шаблон или файл: диагностика.
Системные промпты и приоритет
Системная роль задаёт устойчивое поведение: персону, язык, формат вывода, позицию безопасности. Это контекст, а не прошивка — длинный разговор может его смыть, а маленькие модели взвешивают его меньше, чем вы надеетесь. Два практичных правила: всё несущее (язык, форматный контракт, жёсткие ограничения) кладите в системный промпт и повторяйте в финальном сообщении пользователя; побеждают инструкции, ближайшие к генерации.
Zero-shot против few-shot
Zero-shot просит; few-shot показывает. Извлечение структурированных полей из баг-репортов моделью 3B, zero-shot — «Extract the title, component, and severity as JSON» — даёт валидный JSON с постоянными ключами примерно в двух третях случаев, с дрейфом имён ключей (severity в один ход, priority в следующий).
Few-shot: та же модель, два разобранных примера, третий отчёт оставлен открытым:
Report: "Login button does nothing on Safari 17."
{"title": "Login button unresponsive", "component": "auth", "severity": "medium"}
Report: "App crashes when uploading 4K video."
{"title": "Crash on 4K upload", "component": "upload", "severity": "high"}
Report: "Dark mode makes settings text unreadable."
Примеры несут форматный контракт, так что сэмплер выбирает содержание вместо изобретения схемы. На этом классе задач частота форматных провалов обычно падает с ~30% до нескольких процентов. Два-три примера — колено кривой; дальше вы платите токенами контекста. Для строгих схем пропустите угадывание совсем: структурированный вывод.
Паттерны, которые выживают на маленьких моделях
| Паттерн | Почему работает |
|---|---|
| Одна задача на промпт | Задачи конкурируют за внимание; проигравшие теряются |
| Явный формат вывода («JSON с ключами a, b, c») | Превращает формат в почти детерминированный выбор |
| Нумерованные шаги («1) найти 2) объяснить 3) патчить») | Цепочка держится на рельсах вместо блужданий |
| Примеры вместо описаний | Модели имитируют лучше, чем подчиняются прозе |
| Короткие мета-инструкции | Каждое лишнее правило разбавляет каждое другое |
Середина контекста — где инструкции умирают
Внимание сильнее всего в начале и в конце промпта — эффект «потерянных в середине», стабильный по семействам моделей. Практичная расстановка: цель и форматный контракт сверху, документ для обработки снизу, а для длинных документов — однострочное повторение инструкции после полезной нагрузки. Позиция несуща так же, как содержание, — и каждая лишняя реплика контекста стоит памяти: KV-кэш.
Стоп-последовательности
Генерация заканчивается, когда модель выдаёт свой токен EOS — шаблон определяет, какой это токен. Дополнительные стоп-последовательности (параметр stop на сервере llama.cpp и OpenAI-совместимых API) обрезают по строке: полезно, когда болтливая модель начинает писать ваш следующий ход пользователя. Выбирайте стопы, которые могут означать только «ход окончен» (\n<|im_start|> на ChatML-моделях), никогда строки, встречающиеся внутри легитимных ответов, — голый перевод строки обрежет многострочный код посреди блока.
Краткость бьёт многословие ниже ~7B
Маленькие instruct-модели следуют инструкции на 40 слов лучше, чем рубрике на 400: их ограниченное внимание размазывается по всему, что вы им дали. Структура и примеры заменяют правила-прозу. Эссе-промпты приберегите большим моделям; только они читают каждую строку.
Переиспользуемые шаблоны-сниппеты
Извлечение:
Extract from the text below: person names, dates, money amounts.
Return a JSON array, one object per item, keys: name, date, amount.
No prose before or after the JSON.
Text: {text}
Суммаризация:
Summarize in exactly 3 bullet points, each under 15 words,
for someone who skipped the meeting. Most important decision first.
Text: {text}
Ревью кода:
Review the diff below. List at most 5 findings, ordered by severity.
Format each as: [severity] file:line — problem — suggested fix.
Skip praise and summary. Diff: {diff}
Связанные разделы: токены · структурированный вывод · параметры движка · диагностика