В этой статье
Роль сообщения — это не подпись автора и не уровень доступа пользователя. Это часть контракта между вашим приложением и моделью: по роли API понимает, откуда пришёл текст и как его учитывать при построении ответа.
Для нового кода я бы начинал с Responses API. В нём правила приложения удобно передать отдельным полем instructions, а содержимое диалога — через input. Chat Completions остаётся совместимым форматом, но смешивать модели сообщений из двух API в одном примере не стоит.
Что означает каждая роль
developer — правила, которые задаёт ваше приложение: формат ответа, ограничения домена, последовательность действий. Это инструкции разработчика, а не текст конечного пользователя.
system — системные инструкции более высокого уровня. В Responses API system и developer относятся к инструкциям приложения и имеют приоритет над пользовательскими указаниями — это описано в справке о входных сообщениях и ролях Responses API. В Chat Completions для новых моделей роль developer используется вместо system для инструкций разработчика. Актуальные ограничения и доступные варианты лучше сверять с документацией конкретного метода: описание ролей Chat Completions.
user — запрос пользователя или данные, которые пользователь передал приложению. Если человек написал «ответь в JSON», это всё ещё пользовательская просьба. Она не превращается в правило приложения только потому, что вам хочется получить JSON.
assistant — сообщение в истории с ответом ассистента. Его можно передать при ручной сборке продолжения или few shot примера: API не требует, чтобы такой текст действительно был получен предыдущим запуском модели. Но в собственных логах и документации не выдавайте заранее написанный пример за фактически сгенерированный ответ.
Формат результата инструмента зависит от API. В Chat Completions это сообщение с role: "tool", полем tool_call_id и содержимым результата функции. В Responses API для той же операции передаётся отдельный input item с type: "function_call_output", call_id и output; роли tool там нет.
Полезная модель получается такой:
developer/system -> правила приложения
user -> задача и данные пользователя
assistant -> история или few shot ответ ассистента
Chat Completions: tool -> результат функции
Responses: function_call_output -> результат функции
Это иерархия инструкций, а не гарантия безошибочного поведения. Более высокий приоритет снижает влияние конфликтующего текста, но не отменяет необходимость проверять результат в коде.
Основной маршрут: Responses API
В Responses API правила приложения можно вынести в instructions. Вход пользователя передаётся в input как сообщение с ролью user:
from openai import OpenAI
client = OpenAI() # ключ берётся из переменной окружения OPENAI_API_KEY
response = client.responses.create(
model="YOUR_MODEL", # выберите доступную вам модель из актуальной документации
instructions=(
"Ты помощник службы поддержки. "
"Отвечай по-русски и не выдумывай статус заказа."
),
input=[
{
"role": "user",
"content": "Проверь, можно ли изменить адрес доставки заказа 1842.",
}
],
)
print(response.id)
print(response.status)
print(response.output_text)
Установите актуальный официальный пакет openai на дату установки и передайте ключ через окружение. В пример не нужно вставлять ключ строкой: так он легко попадёт в Git, логи или сообщение об ошибке.
Проверка здесь состоит из трёх разных уровней. Сначала проверьте, что запрос вообще собран: instructions — строка, input — ожидаемый массив, у сообщения есть корректные role и content. Затем посмотрите на response.id и response.status. И только после этого разбирайте output_text или структурированный результат. Минимальная проверка создания ответа описана в справке метода Responses API.
Что происходит с продолжением
Если вы продолжаете разговор с помощью previous_response_id, не считайте, что все правила приложения автоматически стали частью нового запроса. Важные инструкции передавайте явно на каждом вызове, где они нужны. Иначе цепочка может сохранить контекст предыдущего ответа, но потерять правило о формате, языке или допустимых действиях.
В многошаговом сценарии также храните отдельно состояние вашей бизнес-операции. response.id — идентификатор ответа API, а не доказательство, что внешний платёж, заказ или изменение записи успешно завершились. Повтор запроса после сетевого обрыва требует идемпотентности на стороне операции; роль tool этого не решает.
Chat Completions: тот же смысл в другом формате
Если код пока использует Chat Completions, сообщения собираются в массиве messages. Например:
from openai import OpenAI
client = OpenAI()
completion = client.chat.completions.create(
model="YOUR_MODEL",
messages=[
{
"role": "developer",
"content": "Отвечай по-русски. Если данных не хватает, скажи об этом.",
},
{
"role": "user",
"content": "Какие данные нужны для возврата заказа?",
},
],
)
print(completion.choices[0].message.content)
Ранее полученный ответ модели выглядит как сообщение assistant:
messages = [
{"role": "developer", "content": "Отвечай кратко и по-русски."},
{"role": "user", "content": "Что такое идемпотентность?"},
{"role": "assistant", "content": "Это безопасное повторение операции..."},
{"role": "user", "content": "Приведи пример для API."},
]
Связка с инструментом устроена так: сначала приложение или модель инициирует вызов функции, затем ваше приложение выполняет её и добавляет результат с ролью tool, после чего отправляет историю обратно модели. Не помещайте результат базы данных в developer, если это всего лишь данные операции. И наоборот, не кладите правило «никогда не раскрывай внутренние поля» в user — пользовательский текст не является надёжным местом для политики приложения.
Где чаще всего ломается логика
Первая ошибка — передать пользовательский текст как developer. Это даёт данным пользователя тот же смысл, что и вашим правилам. Разделяйте их до вызова API:
developer_rules = "Верни только поля name и status в JSON."
user_text = request.json["message"]
Здесь developer_rules — фиксированная конфигурация приложения, а user_text — недоверенный вход.
Вторая — перепутать историю с журналом фактически сгенерированных ответов. Заранее подготовленный текст можно передать с ролью assistant как few shot пример или часть ручной истории, но он не становится фактом, который модель уже сообщила. Подписывайте происхождение такого текста в своём коде и логах.
Третья — принять неожиданный текст за ошибку структуры. Сначала отделите ошибки валидации запроса, HTTP-ошибки и некорректные роли от нормального ответа модели, который просто не удовлетворил вашему ожиданию. Для обязательного JSON используйте подходящий структурированный формат и проверяйте результат схемой на своей стороне.
Наконец, не путайте роли сообщений с административными ролями в организации OpenAI и с ролями в системном prompt интерфейса ChatGPT. В API эти поля и типы элементов описывают происхождение частей конкретного запроса: developer, user и assistant используются в сообщениях, tool — в сообщении Chat Completions, а function_call_output — в Responses.
Быстрый чек перед отправкой
- Зафиксированы ли инструкции приложения отдельно от пользовательского текста?
- Не попал ли динамический ввод пользователя в
developerилиsystem? - Если есть история, отличаете ли вы заранее подготовленные
assistantfew shot примеры от фактически сгенерированных ответов в своих логах? - Если есть инструмент, соответствует ли формат результата выбранному API:
role: "tool"иtool_call_idв Chat Completions либоfunction_call_outputиcall_idв Responses? - Передаёте ли вы важные
instructionsявно при ручном продолжении черезprevious_response_id? - Проверяете ли вы
id,statusи содержимое ответа до изменения данных или запуска платной операции?
Если после настройки ролей нужна базовая интеграция на Python, можно перейти к инструкции с первым запросом GPT API. Для отдельного маршрута через curl есть пример OpenAI API без Python.
После технического примера остаётся независимый бытовой вопрос: как оплатить зарубежный цифровой сервис из России. Для него можно посмотреть каталог Amber Market: это отдельные предложения по оплате зарубежных цифровых сервисов, заказ оплачивается через СБП, а наличие и условия нужно проверить перед заказом. Каталог не выдаёт API-ключ и не заменяет биллинг OpenAI.