Документация
Как работать с ChatGPT API
Обновлено:
Первый запрос отправить просто. Сложности начинаются дальше: как вести диалог, чтобы модель помнила контекст, что делать с ошибкой 429, какие параметры влияют на цену. Собрал здесь всё, что нужно знать после первого запроса.
Из чего состоит запрос
Запрос это массив сообщений. У каждого своя роль, и модель читает их по порядку. Роли определяют, что модель считает инструкцией, а что репликой собеседника.
system- Инструкция, которая задаёт поведение на весь диалог: тон, формат ответа, ограничения. Ставится первой и не меняется от реплики к реплике.
user- Сообщение пользователя. То, на что модель отвечает прямо сейчас.
assistant- Предыдущие ответы самой модели. Нужны, чтобы она помнила, что уже говорила.
curl https://api.llm-gate.tech/v1/chat/completions \
-H "Authorization: Bearer $CHATGPT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6.1-sol",
"messages": [
{ "role": "system", "content": "Отвечай кратко, по-русски." },
{ "role": "user", "content": "Что такое токен?" }
]
}'Как вести диалог
API не хранит состояние между запросами. Модель не помнит ничего из прошлого вызова, поэтому историю диалога вы отправляете заново каждый раз: системную инструкцию, все прошлые реплики и новый вопрос. Ответ модели добавляете в массив сами, иначе на следующем шаге она его не увидит.
messages = [
{"role": "system", "content": "Отвечай кратко, по-русски."},
{"role": "user", "content": "Что такое токен?"},
# ответ модели возвращаем обратно в массив
{"role": "assistant", "content": "Токен это кусок текста примерно в 4 символа."},
{"role": "user", "content": "А сколько их в слове «программирование»?"},
]
response = client.chat.completions.create(
model="gpt-6.1-sol",
messages=messages,
max_completion_tokens=300,
)История растёт, а вместе с ней растёт счёт: вы платите за все входные токены при каждом запросе. На длинных диалогах обрезайте старые реплики или сжимайте их в краткое резюме.
Стриминг: ответ по мере генерации
По умолчанию ответ приходит целиком, когда модель договорила. На длинных ответах это выглядит как зависание на десятки секунд. Со стримингом текст идёт кусками сразу, как в веб-интерфейсе ChatGPT.
stream = client.chat.completions.create(
model="gpt-6.1-sol",
messages=messages,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)- Чат-интерфейсы, где пользователь ждёт ответ и смотрит на экран
- Длинные ответы: тексты, код, разборы
- Не нужен для фоновых задач: классификации, извлечения полей, обработки по расписанию
Параметры, которые реально влияют
Их больше, но на практике вы будете трогать эти четыре. Остальные имеют смысл в редких сценариях.
| Параметр | Что делает | Когда менять |
|---|---|---|
model | Выбирает модель | Главный рычаг цены и качества. Начинайте с дешёвой, поднимайтесь при провале |
max_completion_tokens | Потолок длины ответа | Ставьте всегда: защищает от неожиданно длинного и дорогого ответа |
temperature | Разброс формулировок | Ниже для фактов и кода, выше для текстов и идей |
stream | Отдавать ответ потоком | Включайте в интерфейсах, где человек ждёт |
Ошибки и что они значат
Код ответа говорит, чья это проблема и надо ли повторять запрос. Половина ошибок лечится ретраем, половина нет.
| Код | Причина и что делать |
|---|---|
| 401 | Ключ неверный или не передан. Проверьте заголовок Authorization и сам ключ. Повторять бесполезно |
| 404 | Модель не найдена. Сверьте написание с нашим списком моделей. Повторять бесполезно |
| 429 | Превышен лимит запросов. Повторите с задержкой, увеличивая её с каждой попыткой |
| 5xx | Сбой на стороне сервиса. Повторите с задержкой, обычно проходит |
| Таймаут | Ответ не пришёл вовремя. Поднимите таймаут клиента: длинные ответы с рассуждением идут дольше |
Ретраи с нарастающей задержкой
Повторять сразу бессмысленно: при лимите вы просто получите ту же ошибку и потратите квоту. Увеличивайте паузу с каждой попыткой и ограничьте их число. Официальные SDK так умеют из коробки, но если ходите в API напрямую, схема такая.
import time
for attempt in range(5):
try:
response = client.chat.completions.create(
model="gpt-6.1-sol",
messages=messages,
max_completion_tokens=300,
)
break
except Exception:
if attempt == 4:
raise
time.sleep(2 ** attempt) # 1, 2, 4, 8 секундКак не переплачивать
Счёт растёт не от числа запросов, а от объёма токенов. Четыре вещи, которые дают основную экономию.
Ставьте потолок ответа
Без max_completion_tokens модель может выдать максимум длины, и вы за это заплатите. Один параметр защищает от самых дорогих случаев.
Не отправляйте лишнюю историю
Каждый запрос оплачивает весь массив сообщений целиком. На длинных диалогах обрезайте старое или заменяйте кратким резюме.
Держите стабильную часть промпта в начале
Повторяющийся префикс попадает в кеш и стоит в разы дешевле обычного ввода. Переменные данные ставьте в конец.
Берите модель под задачу
Классификацию и извлечение полей незачем гонять на флагмане: разница в цене доходит до ста раз при одинаковом результате.
Частые вопросы
Почему модель не помнит предыдущие сообщения?+
Потому что API не хранит состояние. Каждый запрос обрабатывается с нуля, и всё, что модель знает о диалоге, вы передаёте в массиве messages. Добавляйте туда её прошлые ответы с ролью assistant.
Что делать с ошибкой 429?+
Это превышение лимита запросов. Повторите запрос через паузу, увеличивая её с каждой попыткой: секунда, две, четыре. Если 429 приходит постоянно, снизьте частоту обращений или распределите нагрузку во времени.
Нужно ли переписывать код ради нашего API?+
Нет. Формат запросов и ответов совпадает с OpenAI, поэтому официальные SDK работают после замены base URL и ключа. Весь код из этой статьи одинаково работает и с официальным API, и с нашим.
Как узнать, сколько токенов съел запрос?+
Ответ API содержит поле usage с числом входных и выходных токенов. Сохраняйте его в логах: это единственный способ понять, куда уходят деньги, до того как придёт счёт.
Стриминг стоит дороже?+
Нет, цена за токены одинаковая. Меняется только способ доставки ответа: кусками по мере генерации вместо одного пакета в конце.