$ sudo teach IT

Пока что модель была для нас условной функцией llm(prompt). Пора заменить её настоящим вызовом. Любой агент, сколько бы шагов и инструментов в нём ни было, в основе делает одно и то же: отправляет запрос к модели и разбирает ответ. Если этот базовый кирпичик написан небрежно — ключ лежит в коде, инструкции перемешаны с вопросом, длина ответа не ограничена, — проблемы будут во всём, что вы построите сверху.

После урока вы сможете безопасно подключить API-ключ, отправить запрос через Responses API, задать модели роль и правила, управлять длиной и разнообразием ответа и узнать из ответа точное число потраченных токенов.

Что уже умеем

В прошлом уроке вы разобрались, что модель измеряет текст токенами, что входные и выходные токены оплачиваются по разным тарифам и как оценить стоимость запроса заранее. Сегодня эти знания станут практикой: API сам скажет, сколько токенов ушло на запрос, и вы посчитаете его точную цену.

Что вы узнаете в рамках статьи

  • как установить пакет openai и безопасно передать API-ключ;
  • как отправить запрос методом client.responses.create и получить текст ответа;
  • чем instructions отличаются от input;
  • как параметры max_output_tokens и temperature влияют на ответ;
  • как прочитать из ответа число токенов и посчитать стоимость.

Ключевые термины

  • API — способ, которым ваша программа обращается к внешнему сервису: отправляет запрос и получает ответ.
  • API-ключ — секретная строка, по которой сервис узнаёт вас и выставляет счёт.
  • Responses API — современный интерфейс OpenAI для запросов к моделям; в Python это метод client.responses.create.
  • instructions — указания модели о роли, стиле и правилах ответа, отделённые от вопроса пользователя.
  • input — то, на что модель должна ответить: вопрос, текст для обработки.
  • usage — поле ответа с фактическим числом входных и выходных токенов.

Подключение: пакет и ключ

Работа с моделями OpenAI из Python идёт через официальный пакет openai. Установите его в виртуальное окружение проекта:

pip install openai

Ключ создаётся в личном кабинете платформы OpenAI. Главное правило: ключ никогда не пишется в коде. Код попадает в репозиторий, в чаты, на скриншоты, а с утёкшим ключом кто угодно будет делать запросы за ваш счёт. Ключ хранится в переменной окружения OPENAI_API_KEY:

export OPENAI_API_KEY="sk-..."      # macOS и Linux
setx OPENAI_API_KEY "sk-..."        # Windows, затем перезапустите терминал

Клиент сам находит ключ в окружении, передавать его в код не нужно:

from openai import OpenAI

client = OpenAI()  # ключ берётся из переменной OPENAI_API_KEY

Если переменная не задана, при создании клиента появится ошибка о том, что ключ не найден. Это хороший знак: программа не стартует молча с неправильной настройкой.

Первый запрос

Запрос отправляется методом client.responses.create. Обязательны два аргумента: model — имя модели и input — текст, на который нужно ответить.

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-6-luna",
    input="Объясни в двух предложениях, что такое токен.",
)

print(response.output_text)

Что увидим в консоли: два предложения о том, что токен — это фрагмент текста, с которым работает модель. Запустите программу ещё раз — формулировка, скорее всего, изменится. Модель генерирует текст, а не достаёт готовый ответ из базы, поэтому одинаковый запрос может давать разные, но близкие по смыслу ответы.

Объект response содержит гораздо больше, чем текст: идентификатор ответа, имя модели, статус, список элементов вывода. Свойство output_text — удобный способ сразу получить итоговый текст, не разбирая структуру вручную.

instructions: роль и правила отдельно от вопроса

Можно было бы писать всё в одну строку: «Ты преподаватель Python, отвечай кратко. Вопрос: ...». Но правильнее разделять. Параметр instructions задаёт роль, стиль и ограничения, а input — сам вопрос. Модель относится к инструкциям как к правилам поведения с более высоким приоритетом, а вам проще менять вопрос, не трогая правила.

response = client.responses.create(
    model="gpt-6-luna",
    instructions=(
        "Ты преподаватель Python для начинающих. "
        "Отвечай не длиннее трёх предложений и приводи один короткий пример кода."
    ),
    input="Что такое list comprehension?",
)

print(response.output_text)

Что увидим: короткое объяснение и однострочный пример вроде [x * 2 for x in range(5)]. Замените input на другой вопрос — стиль ответа сохранится, потому что правила заданы отдельно. Именно так в агентах задаётся «характер»: один и тот же набор инструкций применяется ко всем запросам пользователя.

Пишите инструкции конкретно. «Отвечай хорошо» модели ничего не говорит, а «отвечай не длиннее трёх предложений, на русском, без вводных фраз» — ясные правила, которые легко проверить.

Параметры ответа и подсчёт токенов

Два параметра пригодятся сразу. max_output_tokens ограничивает длину ответа в токенах — это страховка от неожиданно длинного и дорогого вывода. temperature управляет разнообразием: значения около 0 делают ответы более предсказуемыми, значения ближе к 1 и выше — более разнообразными. Для извлечения данных и классификации берите низкую температуру, для генерации идей — повыше. Некоторые модели поддерживают не все параметры; если API вернёт ошибку о неподдерживаемом параметре, просто уберите его.

После запроса поле usage сообщает точное число токенов:

PRICE_IN = 0.10    # долларов за 1M входных токенов
PRICE_OUT = 0.50   # долларов за 1M выходных токенов

response = client.responses.create(
    model="gpt-6-luna",
    instructions="Ты маркетолог. Отвечай нумерованным списком.",
    input="Придумай 5 названий для кофейни у университета.",
    temperature=0.9,
    max_output_tokens=200,
)

print(response.output_text)
usage = response.usage
cost = (usage.input_tokens * PRICE_IN + usage.output_tokens * PRICE_OUT) / 1_000_000
print(f"Вход: {usage.input_tokens}, выход: {usage.output_tokens}, стоимость: ${cost:.6f}")

Что увидим в консоли:

1. Зачётный кофе
2. Пара до пары
...
Вход: 41, выход: 63, стоимость: $0.000036

Числа у вас будут другими, но порядок тот же. Обратите внимание: входные токены — это инструкции плюс вопрос, именно их вы оценивали в прошлом уроке через tiktoken, а теперь видите точное значение.

Если ответ упёрся в max_output_tokens, он оборвётся на полуслове, а поле response.status будет равно "incomplete". Проверяйте статус, если обрезанный ответ для вас критичен, и не ставьте лимит слишком низким.

Частые ошибки

  • Ключ прямо в коде. Строка OpenAI(api_key="sk-...") рано или поздно окажется в репозитории. Храните ключ только в переменной окружения OPENAI_API_KEY.
  • Правила внутри вопроса. Когда роль и вопрос смешаны в input, их трудно менять по отдельности. Роль и правила — в instructions, вопрос — в input.
  • Печать объекта вместо текста. print(response) выведет громоздкую структуру. Текст ответа лежит в response.output_text.
  • Слишком маленький max_output_tokens. Ответ оборвётся на середине. Ставьте лимит с запасом и проверяйте response.status.

Наиболее частые вопросы

  • Почему на один и тот же вопрос приходят разные ответы? — Модель генерирует текст с долей случайности. Понизьте temperature, чтобы ответы стали стабильнее, но полной одинаковости не ждите.
  • Чем Responses API отличается от chat.completions? — Chat Completions — более старый интерфейс. Responses API — актуальный основной: в нём удобнее работать с инструкциями, диалогами и инструментами, его мы и используем в курсе.
  • Нужен ли платный аккаунт? — Для запросов к API нужен аккаунт на платформе OpenAI с пополненным балансом. Учебные запросы к недорогой модели стоят доли цента.
  • Можно ли не указывать instructions? — Можно, модель ответит в стиле по умолчанию. Но в агентах инструкции почти всегда нужны, чтобы поведение было предсказуемым.

Итоги

Поздравляем, вы отправили первый настоящий запрос к языковой модели! Условная функция llm из первого урока превратилась в рабочий код: вы безопасно подключили ключ, разделили правила и вопрос, ограничили длину ответа и научились считать точную стоимость по полю usage. Это фундамент, на котором будет стоять каждый ваш агент.

Пока каждый запрос живёт сам по себе: модель не помнит, о чём вы спрашивали минуту назад. В следующем уроке вы научите программу вести диалог — передавать историю сообщений и использовать previous_response_id, чтобы модель отвечала с учётом контекста.

На следующий урок