Большинство статей про промпты заканчивается на «будь вежливым и добавь «think step by step»». На практике предсказуемость ответа языковой модели зависит не от волшебных слов, а от структуры запроса и формата вывода — ровно так же, как предсказуемость API зависит от схемы, а не от того, как вы назвали переменную. Здесь разберём то, что реально двигает результат: анатомию промпта, разделение системной роли и пользовательских данных, формат вывода, few-shot и guard-формулировки против галлюцинаций.
Все примеры взяты из реальных системных промптов рабочих инструментов — не из учебных заготовок. Это не «магия промптов», а инженерия ввода: та же дисциплина, что в валидации форм и контрактах API, перенесённая на общение с моделью.
Часть I. Анатомия промпта: пять слоёв предсказуемого ввода
Предсказуемый промпт собирается из пяти слоёв — как запрос к API из пяти обязательных полей. Пропуск любого возвращает «почти то, но не то».
| Слой | Вопрос, на который отвечает | Что ломается без него |
|---|---|---|
| Роль | «Кто отвечает?» | Модель выбирает усреднённый стиль, не ваш домен |
| Задача | «Что сделать?» | Расплывчатый, пространный ответ |
| Контекст | «По каким данным?» | Додумывание вместо фактов (→ галлюцинации) |
| Формат вывода | «В какой форме вернуть?» | Невозможно разобрать ответ программно |
| Ограничения | «Чего не делать?» | Секреты в выводе, markdown-обёртка там, где нужен голый текст |
Возьмём реальную строку из генератора SEO-метатегов — она собрана ровно по этим пяти слоям (роль → задача → контекст-переменные → формат → ограничение):
Ты — SEO-эксперт. Проанализируй текст статьи и сгенерируй SEO-метатеги.
{движок}{язык}{бренд}{ключевые слова}
Верни результат СТРОГО в формате JSON:
{
"title": "title до 60 символов",
"description": "meta description до 160 символов",
"slug": "url-slug транслитом",
"keywords": "5-7 ключевых слов через запятую"
}
Только JSON, без markdown и лишнего текста.
Здесь наглядно всё:
- роль — «Ты — SEO-эксперт» (сужает стиль до предметной области);
- задача — «проанализируй и сгенерируй метатеги»;
- контекст — четыре переменные (
движок/язык/бренд/ключевые слова), подставляемые до отправки; - формат — конкретный JSON со схемой и лимитами прямо в значениях-плейсхолдерах («до 60 символов»);
- ограничение — «Только JSON, без markdown и лишнего текста».
Мнемоника для границы между слоями: роль говорит «откуда», формат говорит «куда», контекст — «из чего». Веб-мастер чаще всего пропускает формат и ограничения — и получает красивый, но непригодный для автоматической обработки текст.
Часть II. system и user — это не украшение, а граница доверия
Любой вызов модели несёт сообщения с полем role. Два основных роли различаются не вежливостью, а семантикой доверия:
$messages = array(
array( 'role' => 'system', 'content' => $system_prompt ), // правила игры
array( 'role' => 'user', 'content' => "Проанализируй текст:\n\n" . $text ), // данные
);
system— инструкция, которую модель трактует как правило поведения. Она задаёт роль, формат и ограничения. Пользователь её не «перебивает» casually: то, что попало в system, держится до конца диалога.user— содержимое для обработки. Это данные: текст статьи, код-фрагмент, описание товара.
Почему это важно именно веб-мастеру: если вы положите данные пользователя в системную роль, любая реплика из формы на сайте станет для модели командой. Это и есть prompt injection — и его полный разбор оставим статье про AI-безопасность (№6), но границы закладываются здесь: правила — в system, недоверенные данные — в user.
Практический приём из рабочих инструментов: пользовательский текст вставляется в user после жёсткого заголовка-маркера, чтобы модель отделяла инструкцию от payload:
Проанализируй текст и сгенерируй метатеги:
<текст из формы пользователя>
Сам заголовок («Проанализируй текст и…») — часть приложения к user-сообщению в коде, а не в system: так инструкция «что сделать» не конфликтует с правилом «как себя вести», а данные ясно ограничены снизу.
Часть III. Формат вывода важнее «волшебных слов»
Главная причина непредсказуемых ответов — не «плохая модель», а размытый формат. «Подробно опиши метатеги» даёт прозу, которую нельзя парсить. «Верни СТРОГО JSON с полями…» даёт то, что подставляется прямо в код.
Три рабочих приёма:
1. Задайте закрытый формат и назовите поля с лимитами. Схема в самом промпте — одновременно и инструкция, и документация:
Верни результат СТРОГО в формате JSON:
{ "title": "до 60 символов", "description": "до 160 символов", "slug": "транслитом" }
Значения-плейсхолдеры внутри JSON несут ограничения — модель удерживает лимит, не выдумывая отдельное правило.
2. Запретите обёртку явно. Модель по привычке оборачивает вывод в markdown ... — и ваш парсер падает. Отсюда ограничение «Только JSON, без markdown и лишнего текста». В генераторе конфигов это же правило сформулировано для файлов:
Пиши только содержимое файла, без пояснений вне него и без markdown-обёртки ```.
3. Читайте ответ как данные, а не как разметку. Ответ модели — недоверенный ввод. Его текст идёт в textContent/esc_html, а не как готовый HTML. Это ровно та же XSS-гигиена, что для формы: вывод модели — не исключение. (Связка с №6 и со статьёй про XSS/CSRF.)
Однострочный вывод этого раздела: инвестируйте в описание формата, а не в «умные» прилагательные. «Предоставь исчерпывающий, но лаконичный SEO-анализ» хуже, чем точная схема полей.
Часть IV. Few-shot: один пример стоит абзаца инструкций
Few-shot — это показать модели один-два прецедента «вход → желаемый выход» прямо в промпте. Работает надёжнее текстового описания формата, потому что модель имитирует образец, а не трактует правило.
Пример для генератора метатегов (схема → один прогнанный случай как эталон формы):
Пример.
Вход: статья «Как настроить PHP-FPM под WordPress».
Выход:
{"title":"PHP-FPM под WordPress: движок и пул","description":"Как PHP-FPM обслуживает WordPress: базовый php.ini, пул www.conf, Opcache. Где задать лимиты и почему настройка не подействовала.","slug":"php-fpm-pod-wordpress"}
Теперь обработай вход ниже в том же формате.
Что few-shot фиксирует лучше инструкции:
- форму полей (какие ключи, в каком порядке, JSON-кавычки);
- регистр и стиль значений (транслит в slug, а не кириллица — видно на примере);
- длину «в натуре» (модель видит реальную длину title, а не «до 60» абстрактно).
Правило экономии: 1–2 примера, а не десять. Каждый пример — это токены на входе (а значит деньги и квота); при закрытом формате два примера покрывают вариативность, десятый почти ничего не добавляет. Пример уместен, когда формат сложнее словесного описания (вложенный JSON, конкретный DSL); для «верни JSON с тремя полями» достаточно самой схемы из Части III.
Рядом лежит приём из инструментов-конвертеров: «текст → Markdown → HTML → чистый текст» проще показать одним прецедентом на входе/выходе, чем описывать правила обработки. Тот же regex-инструмент внутри промпта опирается на формат «ответ на русском, regex в блоке кода» — и даёт нумерованный разбор по частям. Связка с Регулярные выражения на практике.
Часть V. Guard-формулировки: замок против галлюцинаций
Галлюцинация модели — это не «врал», а правдоподобно достроил то, чего во входе не было. Для веб-мастера это дорого: выдуманный CSS-класс, несуществующая константа wp-config, директива php.ini, которую ядро проигнорирует.
Два guard-приёма из реальных системных промптов.
1. Ограничить область утверждений — «ИСКЛЮЧИТЕЛЬНО показанный фрагмент». Реальная формулировка объяснителя кода:
Ты — терпеливый технический объяснитель кода. Объясняй ИСКЛЮЧИТЕЛЬНО показанный
фрагмент, а не вымышленный контекст и не функции, которых во входе нет.
Если фрагмент неполный или обрезан и без внешнего контекста что-то нельзя
утверждать уверенно — явно скажи, какого контекста не хватает, а не додумывай.
Пиши по-русски. Не оборачивай весь ответ в markdown-блок ```; используй обычный текст.
Здесь три разных замка в одной связке: запрет вымышленного контекста → разрешение признать нехватку данных («скажи, какого контекста не хватает») → формат ответа. Ключевое — второе: модели нужно легальное право сказать «не знаю», иначе она заполнит дыру выдумкой. Это и есть дешёвая страховка от галлюцинаций.
2. Отсечь опасную свободу вывода. В генераторе конфигов guard про секреты — он запрещает модели то, чего та в принципе знать не может:
ГЛАВНОЕ ПРАВИЛО БЕЗОПАСНОСТИ: никогда не подставляй реальные секреты (пароли,
ключи API, соли, токены) — только явные плейсхолдеры вида REPLACE_ME или
CHANGE_ME_WITH_REAL_VALUE.
Не выдумывай несуществующих директив, констант и переменных — только реально
существующие в выбранном формате.
Два правила, два класса ошибок: «значение похоже на настоящий пароль» (его зальют в прод и не поменяют) и «константа, которую ядро не знает» (настройка молча не применится). Оба — прямое следствие того, что модель не знает ваших секретов и вашего формата лучше справочника, и guard фиксирует это как запрет, а не как надежду. Подробный разбор «почему секрет — всегда плейсхолдер» — в Три конфига WordPress.
Сводка Части V: хороший guard отвечает «чего не делать» и даёт легальный выход «я не знаю», вместо молчаливой правдоподобной выдумки.
Часть VI. Предсказуемость = проверяемость: валидация ответа кодом
Финальный слой инженерии ввода — то, что большинство статей про промпты пропускает: ответ модели надо валидировать так же строго, как пользовательский ввод. Промпт повышает вероятность нужной формы, но не гарантирует её — поэтому код обязан проверять.
Минимальный цикл на PHP (та же дисциплина, что в [ai_seo_meta_example]):
<?php
// $raw — строка ответа модели. Не доверяем, что там "только JSON".
$json = json_decode( $raw, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
// модель вернула мусор или обёртку — повтор/откат, а не показ пользователю
error_log( 'bad llm json: ' . substr( $raw, 0, 120 ) );
return null;
}
// 1. поля на месте
$title = $json['title'] ?? '';
$desc = $json['description'] ?? '';
// 2. лимиты соблюдены (те самые "до 60/160", что были в промпте)
if ( mb_strlen( $title ) > 60 || mb_strlen( $desc ) > 160 ) {
return null; // не прошло валидацию — генерируем заново, а не отдаём гостю
}
// 3. недоверенный текст выводим экранированным
echo esc_html( $title );Три проверяемых свойства — зеркало слоёв из Части III:
- Это вообще валидные данные (JSON-парсится) — иначе ответ бесполезен программно;
- Ограничения соблюдены (длины, enum, regex-форма slug) — промпт попросил, код требует;
- Вывод экранирован (
esc_html) — модель не защищена от генерации markup/скриптов лучше человека.
Здесь же про «температуру» и детерминизм: параметр температуры (temperature) управляет случайностью выбора токенов. Для задач с единственным правильным форматом (JSON, конфиг, slug) температуру опускают — так ответ стабильнее; для творческого текста её поднимают. Это не «сделать модель умнее», а сузить распределение, чтобы валидатор из Части VI реже откатывался.
Экономику предсказуемости нельзя игнорировать: повторная генерация при провале валидации стоит токенов, поэтому лимиты ввода/вывода и квота на IP — часть дизайна (разбор rate-limiting и квот — в Защита API). Тот же подход — «не верь на слово, проверь тестом» — лежит в основе Юнит-тестов на практике; вывод модели — просто ещё один внешний источник, который надо покрыть проверкой.
Инструменты под задачу
Всё выше — не теория «про промпты», а механика, на которой собраны рабочие инструменты codestack.space. Их системные промпты — живые образцы пяти слоёв из Части I, guard-формулировок из Части V и закрытого формата из Части III:
- закрытый JSON-формат с лимитами прямо в схеме — генератор SEO-метатегов (
titleдо 60,descriptionдо 160, «Только JSON, без markdown»); - guard «ИСКЛЮЧИТЕЛЬНО показанный фрагмент… не додумывай» — объяснитель кода;
- «никогда не подставляй реальные секреты — только REPLACE_ME» — генератор конфигов;
- детерминизм + валидация вывода — общая схема для всех инструментов серии.
Поговорить с моделью вживую, проверить структуру промпта на реальном запросе и увидеть, как меняется вывод при разных форматах, — можно в песочнице чата:
Чат позволяет задать системную роль и формат вывода прямо в запросе и сразу увидеть разницу между «подробно опиши» и «верни JSON с полями…» — то же, что разбирается в частях III–IV, но руками.
Чек-лист граблей
- Не задали формат вывода → модель вернула прозу, парсер не может её разобрать; опишите поля и тип.
- Положили данные пользователя в
system→ реплика из формы стала командой модели (путь к prompt injection); правила — вsystem, данные — вuser. - Забыли запретить markdown-обёртку → ответ приходит в json , и
json_decodeпадает; просите «только JSON, без обёртки». - Описали формат прилагательными, а не схемой → «лаконичный, но подробный» непредсказуем; дайте закрытую схему с лимитами.
- Не дали модели право сказать «не знаю» → она заполнит дыру выдумкой; добавьте «если контекста не хватает — скажи, а не додумывай».
- Ставите few-shot ради few-shot в 10 примеров → лишние токены и деньги на входе; для закрытого формата хватит 1–2.
- Доверились формату без валидации → «до 60 символов» в промпте не гарантирует 60 в ответе; проверяйте длину/schema парсером.
- Вывели ответ модели как HTML → модель так же генерирует markup, как и пользователь вводит; вывод — через
esc_html/textContent. - Не занизили температуру для задачи с единственным ответом → slug/JSON «плывёт» от прогона к прогону; детерминизм снижает откаты валидатора.
- Не учли стоимость ретрая → каждая невалидная генерация жрёт токены; задайте лимиты ввода/вывода и квоту.
👤 Зарегистрируйтесь, чтобы добавить материал в закладки.
