Создание подготовленных к производственному использованию промптов для LLM: семислойная модель
Изучите структурированную, вдохновлённую API схему из семи уровней промптов — инструкции, контекст, ограничения и другие элементы — для создания надёжных систем LLM промышленного уровня.
21 дня продвинутой разработки промптов
Практическое обучение, которое проводит вас от основ промптов до проектирования полноценных ИИ-систем.
Большинство людей считают, что промпт — это всего лишь вопрос, который вы передаете большой языковой модели.
Например:
Classify this customer support ticket.
И это работает.
Пока что-то идёт не так.
Несколько входных данных могут дать именно то, чего вы ожидали. Затем поступает немного иной запрос, и вдруг модель:
- берёт неверную категорию,
- создаёт детали, которых не было в входных данных,
- возвращает формат, который вы не просили,
- пишет абзац объяснения вместо структурированных данных,
- или ведёт себя неконсистентно просто из-за изменения формулировки.
Именно здесь начинает проявляться разница между запросом, протестированным случайно и запросом, разработанным для промышленного использования.
В производственной системе запрос — это не просто вопрос.
Он служит частью контракта между вашим приложением и моделью.
Разработчики уже знают, как проектировать API с четко определенными границами:
Request → Validation → Business Logic → Response
Системы, построенные на запросах, требуют такой же дисциплины:
Instructions
↓
Context
↓
Constraints
↓
Task
↓
Examples
↓
Output Contract
↓
Validation
Далее в этом тексте рассматриваются каждый из этих слоев по отдельности, а затем они объединяются в один рабочий пример.
1. Проблема подхода «Просто спросите у модели»
Представьте функцию с использованием ИИ для платформы поддержки клиентов. Каждый поступающий запрос должен быть направлен в один из четырех категорий:
billing
technical
account
general
Минимальный запрос может выглядеть так:
Classify this customer support ticket:
"I was charged twice for my subscription."
И модель может ответить:
Billing
На первый взгляд всё кажется в порядке.
Но вашему бэкенду обычно нужно нечто большее, чем просто одна метка. Скорее всего, он ожидает структурированную информацию, например:
{
"category": "billing",
"priority": "high"
}
Именно здесь всё становится сложнее.
Как на самом деле определяется приоритет?
Рассмотрим заявку, в которой просто написано:
«Я не могу войти».
Относится ли это к разделу account, или это действительно техническая проблема?
Что произойдёт, если клиент упомянет одновременно проблему с оплатой и проблему с входом в одном сообщении?
Что, если категория действительно двусмысленна?
Каково ожидаемое поведение в таком случае?
Ни на один из этих вопросов модель не сможет дать надёжный ответ, если в запросе изначально не указаны правила.
Именно поэтому создание промптов для производственного использования начинается с определения спецификации, а не с оттачивания формулировок.
2. Структура промпта производственного уровня
Качественный промпт для производственного использования можно разделить на семь отдельных компонентов.
Эти разделы не обязательно должны появляться буквально в виде маркированных заголовков внутри самого текста промпта.
Важно понимать, какую функцию выполняет каждый слой.
Рассмотрим их по одному.
3. Инструкции системы — определение роли и поведения
Первый слой определяет объем ответственности модели.
В примере с классификатором тикетов:
You are a customer support classification assistant.
Your job is to classify incoming support tickets
according to the provided category definitions.Return only information supported by the ticket.
Do not invent customer information.
Это гораздо полезнее, чем что-то общее вроде:
You are an intelligent AI assistant.
Почему это имеет значение?
Поскольку называние модели «интеллектуальным ИИ-ассистентом» не определяет конкретного поведения.
Более точная версия указывает:
- какую задачу выполняет модель
- в какой области она действует
- какую информацию ей разрешено использовать
- какое поведение запрещено
Инструкции системы можно рассматривать как договор о поведении для взаимодействия.
4. Контекст — предоставьте модели то, что ей нужно
Следующий уровень предоставляет всю необходимую информацию для выполнения задачи.
Например:
Available categories:
billing:
Questions about charges, invoices, refunds, or payments.technical:
Problems with product functionality, errors, or system behavior.account:
Login, password, profile, access, or account-management issues.general:
Questions that don't clearly belong to the above categories.
Затем следует само содержимое заявки:
Customer ticket:
"I was charged twice for my subscription this month."
Здесь важно четко обозначить это различие:
Instructions = What to do
Context = Information needed to do it
Если заменить контекст, оставив инструкции без изменений, модель должна по-прежнему корректно обрабатывать новый входной данный.
Разделение их также значительно упрощает поддержку шаблонов запросов со временем.
5. Ограничения — указание модели, где остановиться
Ограничения — это слой, в котором устраняется неоднозначность.
Продолжая пример:
Rules:
1. Select exactly one category.
2. Use only the four categories provided.
3. Do not create new categories.
4. Do not infer facts that are not present.
5. If the issue is unclear, use "general".
6. Priority must be one of: low, medium, high.
7. Return only the requested JSON.
Эти правила существенно сужают пространство возможных ответов.
Без них запрос вроде:
Classify the ticket.
может привести к чему-то вроде:
This appears to be a billing-related issue because
the customer mentions being charged twice.
Это совершенно разумный ответ для человека-читателя.
Но ваш API, скорее всего, ожидает чистый JSON, а не прозу.
Явно укажите ограничение:
Return only the requested JSON.
и ожидаемое поведение станет однозначным.
6. Задание — определите точную операцию
После того как установлены инструкции и ограничения, необходимо четко описать конкретную операцию, которую вы ожидаете выполнения.
Task:
1. Identify the most appropriate category.
2. Determine the priority based on the rules.
3. Provide a short reason.
4. Return the result using the specified JSON structure.
Сравните это с передачей нечеткой директивы вроде:
Understand this customer issue.
Когда задание сформулировано столь точно, появляется возможность проверить впоследствии, действительно ли модель выполнила то, что было запрошено.
Представьте себе конвейер вот такой формы:
Input
↓
Classify
↓
Determine priority
↓
Generate reason
↓
Return JSON
В этот момент задание перестает быть предположением и становится чем-то измеримым и проверяемым.
7. Примеры — покажите желаемое поведение
Инструкции говорят модели, что делать, с помощью слов.
Примеры демонстрируют это непосредственно.
Возьмем этот пример:
Example 1
Example 1Input:
"I was charged twice for the same subscription."Output:
{
"category": "billing",
"priority": "medium",
"reason": "The customer reports a duplicate subscription charge."
}
Второй пример может иллюстрировать совершенно другую ситуацию, например отчет о технической ошибке:
Example 2Input:
"The application crashes every time I upload an image."Output:
{
"category": "technical",
"priority": "high",
"reason": "The customer reports a repeatable application failure."
}
Примеры особенно полезны, когда поведение, которого вы хотите добиться, трудно определить только с помощью правил, поскольку они позволяют модели зафиксироваться на конкретном шаблоне.
Тем не менее существует нюанс, о котором стоит помнить.
Больше примеров ≠ автоматически лучшие запросы
Накопление примеров один за другим имеет реальные издержки. Это приводит к увеличению:
- Размера запроса
- Потребления токенов
- Задержек
- Возможных противоречий
Более умной стратегией является тщательный выбор небольшого количества примеров.
Отдавайте приоритет примерам, представляющим собой значимо разные ситуации, особенно неоднозначные и крайние случаи.
Например, такая тройка:
Clear billing issue
Clear technical issue
Ambiguous issue
обычно превосходит десять практически идентичных примеров расчета счетов, сложенных друг на друга.
8. Формат вывода — рассматривайте его как контракт API
В промышленных запросах мало что имеет такое же большое значение, как именно этот уровень структурирования.
Если какая-то другая система должна обрабатывать результаты, возвращаемые моделью, нельзя оставлять форму этого ответа на усмотрение случая в виде свободного текста.
Вместо этого необходимо четко определить схему.
Например:
{
"category": "billing | technical | account | general",
"priority": "low | medium | high",
"reason": "string"
}
Как только это будет реализовано, у модели появится фиксированный целевой формат, к которому она будет стремиться, а последующий код сможет рассчитывать на определенный порядок выполнения операций:
LLM
↓
JSON
↓
Parser
↓
Schema validation
↓
Business logic
вместо чего-то гораздо более хаотичного, например:
LLM
↓
Some paragraph
↓
Regex
↓
Hope it works
Такая вторая схема представляет собой настоящий кошмар с точки зрения поддержки в долгосрочной перспективе.
Хорошо специфицированный структурированный результат создает четкое разделение между тем, что генерирует большая языковая модель, и тем, что действительно требуется вашему приложению.
9. Проверка корректности — модель не является инструментом проверки
Вот ошибка, которая постоянно встречается в системах на основе ИИ.
Предположим, модель возвращает следующий результат:
{
"category": "billing",
"priority": "urgent",
"reason": "The customer has a billing issue."
}
Синтаксически этот JSON корректен.
Проблема кроется здесь:
"urgent"
«urgent» никогда не был одним из разрешенных значений.
Задача вашего приложения — обнаруживать такие несоответствия, а не модели.
Вот как это выглядит с использованием Pydantic:
from pydantic import BaseModel
from typing import Literal
class TicketClassification(BaseModel):
category: Literal[
"billing",
"technical",
"account",
"general"
]
priority: Literal[
"low",
"medium",
"high"
]
reason: str
Затем вы вызовете:
result = TicketClassification.model_validate(llm_response)
А если модель вернет что-то вроде:
{
"category": "billing",
"priority": "urgent",
"reason": "Duplicate charge."
}
шаг проверки должен отклонить его немедленно.
Именно в этом и заключается смысл.
Никогда не позволяйте выводу модели проходить без проверки.
Это приводит к правилу, которое стоит соблюдать при проектировании систем на основе LLM:
LLM генерирует. Ваше приложение проверяет.
Модель может помочь с принятием решений, но любые требования, которые действительно являются неоспоримыми, должны находиться в детерминистическом коде приложения, который напрямую их реализует.
10. Полная инструкция, ориентированная на производство
Собрав все слои вместе, получается нечто подобное:
SYSTEM
You are a customer support classification assistant.
Your job is to classify incoming support tickets
according to the provided category definitions.
Return only information supported by the ticket.
Do not invent customer information.
CONTEXT
Available categories:
billing:
Charges, invoices, refunds, or payment-related issues.
technical:
Product functionality, errors, crashes, or system behavior.
account:
Login, password, profile, access, or account-management issues.
general:
Issues that do not clearly belong to another category.
Customer ticket:
{{ticket_text}}
CONSTRAINTS
1. Select exactly one category.
2. Use only the categories provided above.
3. Do not create new categories.
4. Do not infer unsupported facts.
5. If the issue is unclear, use "general".
6. Priority must be "low", "medium", or "high".
7. Return only the requested JSON.
TASK
1. Classify the ticket.
2. Determine the priority.
3. Provide a short reason.
4. Return the result in the required JSON format.
EXAMPLE
Input:
"I was charged twice for the same subscription."
Output:
{
"category": "billing",
"priority": "medium",
"reason": "The customer reports a duplicate subscription charge."
}
OUTPUT FORMAT
{
"category": "billing | technical | account | general",
"priority": "low | medium | high",
"reason": "string"
}
Теперь разместите это рядом с тем местом, где начиналось всё это упражнение:
Classify this customer support ticket.
Разница между этими двумя инструкциями заключается не столько в количестве слов.
Изменилось то, насколько всё стало конкретным.
Каждый блок более длинной версии выполняет определенную функцию:
- Часть, описывающая систему, определяет, как должна вести себя модель
- Часть, описывающая контекст, предоставляет информацию, с которой работает модель
- Часть, описывающая ограничения, указывает правила, которые модель не может нарушить
- Часть, описывающая задачу, точно определяет, что модель должна выполнить
11. Проектирование запросов похоже на проектирование API
Для инженеров по программному обеспечению такое сравнение делает использование запросов в производственных условиях почти мгновенным.
Представьте типичный конец точки REST.
Вы бы определили что-то вроде:
POST /tickets/classify
Тело запроса:
{
"ticket": "I was charged twice."
}
И тело ответа:
{
"category": "billing",
"priority": "medium",
"reason": "Duplicate charge reported."
}
Теперь примените ту же логику к запросу для LLM.
Запрос фактически представляет собой логику реализации, находящуюся за этим концом точки.
API
↓
Input
↓
Prompt Template
↓
LLM
↓
Structured Output
↓
Validation
↓
API Response
Именно поэтому инжиниринг запросов постепенно превращается в дисциплину инженерии программного обеспечения, а не в упражнение в мастерстве слова.
Вы не ищете идеальную формулировку.
Вы создаете надежный интерфейс на основе системы, которая ведет себя вероятностным образом.
12. Распространенные ошибки в промптах для производственного использования
1. Косвенность формулировок
Analyze the ticket carefully.
Что считается «тщательностью» в данном случае?
Укажите точное поведение, которого вы хотите, вместо того чтобы оставлять его на усмотрение интерпретации.
2. Запросы того, что вам не нужно
Укажите, что ваше приложение потребляет только:
{
"category": "billing"
}
Затем не запрашивайте:
category
reason
summary
sentiment
customer mood
recommended response
next action
если только ваше приложение действительно не использует эти поля далее в своей работе.
Каждое дополнительное поле, которое вы запрашиваете, — это еще одна причина, по которой результат может отклоняться или стать несогласованным.
3. Полное размещение бизнес-логики в промпте
Пример этой ошибки:
If the customer has been waiting more than 48 hours,
has contacted support three times, and is a premium customer,
set priority to high...
Подобные правила обычно должны находиться в детерминистическом коде вашего приложения, а не в промпте.
Более чистое разделение выглядит так:
LLM → classify issue
Application → calculate priority
Такое разделение элементов обычно значительно упрощает тестирование всей системы.
4. Изменение промптов без оценки
Предположим, версия 1 хорошо работает в производственной среде.
Затем кто-то вносит изменения:
Classify the ticket.
в следующий вид:
Analyze and intelligently classify the ticket.
Это кажется простой перефразировкой.
Однако поведение в производстве может существенно измениться.
Именно поэтому промпты требуют контроля версий и оценки, такой же дисциплины, какая применяется к коду приложения.
5. Предположение, что модель всегда будет следовать инструкциям
Помните, что ЯЗЫКИ больших моделей по своей природе являются вероятностными.
Даже тщательно составленный промпт может всё равно вернуть что-то, чего вы не просили.
Вот почему настоящей производственной системе нужно нечто большее, чем хороший промпт — ей требуется:
Prompt
+
Structured output
+
Validation
+
Monitoring
+
Fallback handling
Опираться только на формулировку промпта — небезопасная стратегия.
13. Для производственных промптов необходима оценка
Основная разница между случайными экспериментами с ЯИ и внедрением реальной функции ИИ заключается в оценке.
Представьте, что у вас есть 1 000 исторических заявок на поддержку.
Вы можете создать тестовый набор из них в следующей структуре:
200 billing
200 technical
200 account
200 general
100 ambiguous
100 edge cases
Затем вы применяете разные версии промптов к этому же набору и сравниваете результаты.
Например:
Prompt V1 Prompt V2
----------------------------------------
Category accuracy 88% 93%
Schema failures 4% 1%
Invalid values 3% 0.5%
Average latency 1.8s 2.1s
Token usage 650 820
В этот момент работа с промптами перестает быть субъективной.
Вы больше не задаёте вопрос:
"Кажется ли этот промпт лучше?"
Вместо этого вы задаёте вопрос:
«Получает ли эта версия лучшие результаты в тех случаях, которые действительно важны для нашего приложения?»
Это гораздо более практичный вопрос, который стоит задать.
14. Компромисс: больше инструкций против большей сложности
Не существует жесткого правила, гласящего:
«Чем длиннее запрос, тем лучше его результаты».
Запросы могут стать чрезмерно сложными.
Что-то вроде:
30 rules
+
20 examples
+
multiple exceptions
+
long explanations
+
repeated instructions
может превратиться в бремя для обслуживания, а не в помощь.
Полезной привычкой является постоянное задавание себе вопроса:
Решает ли эта инструкция реальную проблему, которую я наблюдал?
Если ответ «нет», то такая инструкция, скорее всего, не должна там находиться.
Лучший запрос для практического использования — это не тот, который содержит наибольше информации.
Это тот вариант, который обеспечивает правильный контекст, правильные ограничения и правильное ожидаемое поведение, при этом снижая до минимума ненужную сложность.
15. Практическая модель мышления
При создании запроса полезно рассмотреть семь ведущих вопросов.
1. Кто является моделью в этом рабочем процессе?
Инструкции системы
2. Что должна знать модель?
Контекст
3. Чего она ни в коем случае не должна делать?
Ограничения
4. Что именно она должна выполнить?
Задача
5. Могу ли я показать ожидаемое поведение?
Примеры
6>Каким должен быть ответ?
Формат вывода
7. Как мое приложение будет понимать, что ответ приемлем?
Проверка
В совокупности эти семь вопросов образуют единую структуру.
Эта когнитивная модель стоит гораздо больше, чем запоминание любого отдельного шаблона запроса.
16. Инженерия запросов движется в направлении проектирования систем
Возможно, это самая важная идея всего этого обсуждения.
На ранних этапах работа с большими языками модели обычно сводится к вопросу вроде:
How can I phrase this question better?
Когда системы становятся более сложными, этот вопрос превращается совершенно в другой:
What does the model need to know?
What should it be allowed to do?
What should it return?
How do I validate it?
What happens when it fails?
How do I evaluate changes?
Это уже не вопросы формулировки — это вопросы инженерии программного обеспечения.
Именно поэтому инженерия запросов производственного уровня мало связана с поиском волшебной фразы и гораздо больше — с проектированием надежного взаимодействия между вашим приложением и моделью, ведущей себя вероятностным образом.
Основные выводы
Есть несколько ключевых идей, которые стоит запомнить из всего этого:
1. Готовый к использованию промпт — это не просто один вопрос.
Он определяет поведение, предоставляет контекст, устанавливает границы, формулирует задачу, приводит примеры и определяет, каким должен быть результат.
2. Разделяйте инструкции и данные.
Модель должна сразу понимать, что от неё требуется сделать, и какие данные ей необходимо использовать.
3. Чёткие ограничения сокращают количество догадок.
Не оставляйте на усмотрение модели решение проблем, возникающих при отсутствии данных, их неоднозначности или некорректной структуре.
4. Примеры нужны для иллюстрации поведения.
Используйте их целенаправленно, особенно в сложных крайних случаях.
5. Структурированный результат следует обрабатывать как контракт API.
Каждый раз, когда служба нижнего уровня считывает ответ модели, необходимо четко определить, в каком именно формате должен быть этот ответ.
6. Не позволяйте промпту служить единственным средством проверки безопасности.
Настоящая валидация должна осуществляться внутри вашего приложения с помощью проверок шаблонов и детерминистических бизнес-правил.
7. Рассматривайте промпты как код, который необходимо оценить.
Версионируйте их, тестируйте на репрезентативных примерах и отслеживайте их производительность.
8. Более длинный промпт не обязательно лучше.
Каждая добавленная строка должна иметь свое обоснование.
Заключение
Создание промптов производственного уровня не сводится к попыткам заставить большую языковую модель звучать более умно.
Речь идет о том, чтобы сделать взаимодействие более предсказуемым, более прозрачным и удобным для интеграции в реальную систему.
Как правило, этот сдвиг выглядит следующим образом:
Simple question
↓
Structured instructions
↓
Clear context
↓
Explicit constraints
↓
Defined task
↓
Useful examples
↓
Structured output
↓
Application validation
↓
Evaluation + monitoring
Как только такой подход становится очевидным, инжиниринг промптов перестаёт казаться процессом проб и ошибок при формулировке текста и начинает больше напоминать разработку контракта API для компонента, ведущего себя вероятностным образом.
Этот сдвиг имеет огромное значение, когда вы переходите от демонстраций к созданию систем, предназначенных для работы в производственной среде.
Связанная литература
- Создание ИИ-агента с нуля: шаблоны, ReAct и LangGraph — Узнайте о основных концепциях ИИ-агентов — планировании, использовании инструментов, рефлексии и шаблоне ReAct — а также о том, как LangChain и LangGraph помогают вручную создавать такие агенты.