Первый рабочий клиент: код и обработка ответа
ИИ для разработчиков: 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).