Роли в OpenAI API: system, developer, user и assistant

В этой статье

Роль сообщения — это не подпись автора и не уровень доступа пользователя. Это часть контракта между вашим приложением и моделью: по роли 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.

Быстрый чек перед отправкой

  1. Зафиксированы ли инструкции приложения отдельно от пользовательского текста?
  2. Не попал ли динамический ввод пользователя в developer или system?
  3. Если есть история, отличаете ли вы заранее подготовленные assistant few shot примеры от фактически сгенерированных ответов в своих логах?
  4. Если есть инструмент, соответствует ли формат результата выбранному API: role: "tool" и tool_call_id в Chat Completions либо function_call_output и call_id в Responses?
  5. Передаёте ли вы важные instructions явно при ручном продолжении через previous_response_id?
  6. Проверяете ли вы id, status и содержимое ответа до изменения данных или запуска платной операции?

Если после настройки ролей нужна базовая интеграция на Python, можно перейти к инструкции с первым запросом GPT API. Для отдельного маршрута через curl есть пример OpenAI API без Python.

После технического примера остаётся независимый бытовой вопрос: как оплатить зарубежный цифровой сервис из России. Для него можно посмотреть каталог Amber Market: это отдельные предложения по оплате зарубежных цифровых сервисов, заказ оплачивается через СБП, а наличие и условия нужно проверить перед заказом. Каталог не выдаёт API-ключ и не заменяет биллинг OpenAI.

Источники

Есть следующая задача?Ещё по теме «Работа с API» →