Первый рабочий клиент: код и обработка ответа

ИИ для разработчиков: API и агенты · Урок 2 / 20

Первый рабочий клиент

Напишем не «hello world», а минимальный клиент, который уже можно положить в проект: с таймаутом, проверкой ошибок и логированием расхода.

Node.js

const MODEL = "gpt-4o-mini";

export async function ask(messages, { maxTokens = 500, temperature = 0.2 } = {}) {
  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), 30_000);   // всегда ставьте таймаут
  try {
    const r = await fetch("https://api.openai.com/v1/chat/completions", {
      method: "POST",
      signal: ctrl.signal,
      headers: {
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ model: MODEL, messages, temperature, max_tokens: maxTokens }),
    });

    if (!r.ok) {
      const text = await r.text();
      throw new Error(`LLM ${r.status}: ${text.slice(0, 300)}`);
    }

    const data = await r.json();
    const choice = data.choices?.[0];
    if (choice?.finish_reason === "length") {
      console.warn("Ответ обрезан по max_tokens — увеличьте лимит или сократите вход");
    }
    console.info("usage", data.usage);            // логируем расход
    return choice?.message?.content ?? "";
  } finally {
    clearTimeout(timer);
  }
}

Python

import os, httpx

MODEL = "gpt-4o-mini"

def ask(messages, max_tokens=500, temperature=0.2):
    r = httpx.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"},
        json={"model": MODEL, "messages": messages,
              "temperature": temperature, "max_tokens": max_tokens},
        timeout=30.0,
    )
    r.raise_for_status()
    data = r.json()
    choice = data["choices"][0]
    if choice["finish_reason"] == "length":
        print("WARN: ответ обрезан")
    print("usage:", data["usage"])
    return choice["message"]["content"]

Что здесь важно

  • Таймаут. Без него запрос может висеть минутами и держать соединение. 30 секунд — разумный старт для коротких ответов.
  • Проверка r.ok. API отдаёт осмысленные ошибки в теле — не глотайте их, логируйте первые 300 символов.
  • Логирование usage. Одна строка, которая потом сэкономит вам разбор счёта.
  • Модель в константе. Не разбрасывайте строку модели по коду: менять придётся часто.

Где хранить ключ

Только в переменных окружения на сервере. Ни в репозитории, ни в клиентском бандле, ни в NEXT_PUBLIC_*. Если ключ хоть раз попал в git — отзывайте и создавайте новый, история остаётся навсегда.

Инсайт. Оберните вызов модели в одну функцию с первого дня. Позже сюда добавятся ретраи, кэш, метрики и смена провайдера — и всё это в одном месте.
Частая ошибка. Вызывать API напрямую из React-компонента. Ключ утечёт в браузер — прокси на своём бэкенде обязателен.
Про-совет. Добавьте в функцию параметр requestId и прокидывайте его в логи. Отладка проблем в проде станет в разы проще.

Шпаргалка

  • Таймаут обязателен.
  • Проверяйте статус и логируйте тело ошибки.
  • Логируйте usage с первого запроса.
  • Ключ — только на сервере.
1. Что обязательно добавить в HTTP-вызов модели?
2. Где может лежать API-ключ?
3. Зачем оборачивать вызов в одну функцию?
Задание — проверяет ИИ

Напишите минимальный клиент к LLM API на своём языке: с таймаутом, проверкой статуса, обработкой finish_reason и логированием usage. Сделайте один реальный запрос. Вставьте: код клиента и вывод (включая usage).

← Назад

🔒 Ответьте на вопрос верно, чтобы перейти к следующему уроку.

Первый рабочий клиент: код и обработка ответа — ИИ для разработчиков: API и агенты — Skilvy