AI-инжиниринг
Интеграция с LLM API: OpenAI, Anthropic, open-source модели
Цели урока
- Научиться вызывать Chat Completions API из Node.js
- Понять роли сообщений (system, user, assistant) и управление контекстом
- Реализовать streaming для real-time отображения ответов
- Освоить обработку ошибок и retry-стратегии для LLM API
- Спроектировать абстракцию для переключения между провайдерами
Предварительные знания
- Как работают LLM изнутри
Первый вызов OpenAI API - 10 строк кода. Первый production-ready вызов с retry, timeout и fallback - 200 строк. Разница между demo и продуктом, который не падает, когда стартап попадает на HackerNews и за ночь получает 10 000 пользователей. API key - не просто строка: это прямой доступ к счёту. Утечка в GitHub - и через час придёт счёт на тысячи долларов. Именно эту разницу - между «работает у меня» и «работает в production» - и разбирает этот урок.
- Notion AI - интегрировал GPT в существующий продукт, +100M ARR за первый год, без единого ML-инженера
- Stripe использует GPT-4 для автоматической классификации support-тикетов - минус 40% нагрузки на команду
- Duolingo перевёл объяснения ошибок на GPT-4 - сэкономил месяцы работы контент-команды
- Cursor (AI IDE) - 100M ARR на правильно выстроенной архитектуре поверх LLM API
API, открывший индустрию
**2020 год**: OpenAI запустил первый в мире коммерческий LLM API на базе GPT-3. Модель стала доступна через HTTP - без GPU, без обучения, без PhD. Это был момент, когда AI перестал быть инструментом исследователей и стал инструментом разработчиков. **30 ноября 2022**: ChatGPT как публичное демо - 1 миллион пользователей за 5 дней. **1 марта 2023**: API для GPT-3.5-turbo по цене `0.002` за 1K токенов - настоящий старт рынка AI-as-a-Service. За первую неделю зарегистрировались сотни тысяч разработчиков. К концу 2023 года через API проходило больше запросов, чем через сам ChatGPT. OpenAI создал не просто модель - он создал рынок.
Chat Completions API - основной интерфейс
Первый вызов OpenAI API - 10 строк кода. Первый production-ready вызов с retry, timeout, fallback - 200 строк. Именно этот разрыв разделяет demo и продукт, который не падает ночью.
Все современные LLM работают через **Chat Completions API** - отправляется массив сообщений, приходит ответ. OpenAI, Anthropic, локальная модель через Ollama - интерфейс почти одинаковый. Это не случайность: OpenAI задал стандарт, остальные его приняли.
Ответ - не просто строка. Это объект с метаданными, и самое важное в нём - `usage`:
**usage.total_tokens** - именно это считается в счёте. При 100 000 запросах в день по 800 токенов - это 80M токенов в месяц. gpt-4o обойдётся в `200 USD.` gpt-4o-mini - в `12 USD.` Всегда логируй usage в production с первого дня.
Какое поле в ответе Chat Completions API показывает количество потраченных токенов?
Роли сообщений: system, user, assistant
Chat API принимает массив **messages**. У каждого сообщения есть **role** - и от этого зависит, как модель его интерпретирует.
| Role | Назначение | Когда использовать |
|---|---|---|
| system | Инструкция для модели. Задаёт поведение, тон, ограничения | Один раз, первым сообщением |
| user | Сообщение от пользователя | Каждый запрос пользователя |
| assistant | Ответ модели (или пример ответа) | История диалога, few-shot примеры |
Модель **stateless**. Она не помнит ничего между запросами - как HTTP. Разработчик сам передаёт весь контекст каждый раз. Это не баг - это архитектурное решение, которое делает API масштабируемым.
**System prompt - главный инструмент.** Хороший system prompt превращает generic модель в специализированного ассистента. Плохой - даёт галлюцинации и нерелевантные ответы. Character.ai строит на system prompt'ах персонажей с 20 миллиардами сообщений в день. Подробнее - в уроке про промпт-паттерны.
Модель «помнит» предыдущие сообщения в диалоге потому что:
Streaming: ответ по токенам в реальном времени
Без streaming пользователь ждёт 5-15 секунд пустого экрана, пока модель генерирует полный ответ. С streaming - видит текст, который появляется токен за токеном, как в ChatGPT. TTFT (time-to-first-token) падает с 10 секунд до 200-500 мс. Один параметр меняет восприятие продукта.
В **NestJS** стриминг отдаётся клиенту через Server-Sent Events (SSE) - один из стандартных способов доставить поток данных по HTTP без WebSocket:
**Streaming не меняет стоимость** - количество токенов одинаковое. Но метрика TTFT (time-to-first-token) - это разница между продуктом, который ощущается живым, и продуктом, который «завис». Notion AI, Linear, Cursor - все используют streaming именно за это ощущение отклика.
Streaming в LLM API нужен для:
Ошибки API: retry, rate limits, fallbacks
2023 год. Стартап запустил AI-фичу. Первые 500 пользователей - всё работает. Ночью продукт попал в HackerNews. За час пришло 5000 одновременных запросов. Каждый запрос бил в OpenAI напрямую. Прилетело 429. Фронтенд показал пустой экран. Продукт ушёл в оффлайн. Утром основатели смотрели в логи и не понимали что случилось.
LLM API - это **внешний сервис**. Он падает, тормозит и возвращает ошибки. Production-ready интеграция обязана это учитывать с первого коммита.
| HTTP Code | Причина | Что делать |
|---|---|---|
| 400 | Невалидный запрос (плохой prompt) | Исправить запрос. Не ретраить |
| 401 | Невалидный API key | Проверить .env. Не ретраить |
| 429 | Rate limit - слишком много запросов | Ретрай с exponential backoff |
| 500 | Ошибка на стороне OpenAI | Ретрай 2-3 раза с задержкой |
| 503 | Сервис перегружен | Ретрай или fallback на другую модель |
**Rate limits у OpenAI** зависят от тарифа: от 500 RPM (requests per minute) на Tier 1 до 10 000 RPM на Tier 5. Если продукт масштабируется - закладывай BullMQ-очередь с самого начала, не после первого инцидента.
При получении 429 (Rate Limit) ошибки от LLM API правильная стратегия:
OpenAI vs Anthropic vs Open Source: выбор провайдера
Привязка к одному провайдеру - это **vendor lock-in**. OpenAI поднял цены в 2023 - и сотни компаний обнаружили, что их юнит-экономика сломалась за неделю. Умный AI-инженер проектирует систему так, чтобы переключиться за 30 минут, а не за 30 дней.
**Ключевые различия API:**
| Аспект | OpenAI | Anthropic |
|---|---|---|
| System prompt | В массиве messages | Отдельное поле system |
| Ответ | choices[0].message.content | content[0].text |
| Лимит выходных токенов | max_tokens (опционально) | max_tokens (обязательно!) |
| Streaming | stream: true | stream: true |
| Function calling | tools + tool_choice | tools + tool_choice |
| Модели | GPT-4o, GPT-4o-mini | Claude Sonnet, Claude Haiku |
Начни с OpenAI (больше документации, примеров). Но сразу пиши абстракцию - добавить Anthropic или локальную llama через Ollama займёт 30 минут вместо дней рефакторинга. Это не overengineering - это страховка от vendor lock-in.
Зачем создавать абстрактный LLMProvider интерфейс вместо прямого вызова OpenAI SDK?
API key - это просто строка для авторизации
API key - это прямой доступ к платёжному счёту. Утечка в публичный репозиторий = счёт на тысячи долларов за несколько часов
Боты сканируют GitHub каждые несколько минут в поисках открытых API ключей. OpenAI не возмещает расходы от утечек. Правило: API ключи только в .env, .env в .gitignore, никогда в коде, никогда в логах. Для production - переменные окружения через секрет-менеджер (AWS Secrets Manager, Vault).
Итоги
- Chat Completions API - универсальный интерфейс: messages in - text out. OpenAI задал стандарт, Anthropic и open-source его приняли
- System prompt задаёт поведение, user - запрос, assistant - история. Модель stateless: каждый вызов независим
- Streaming через SSE даёт TTFT 200-500мс вместо 5-15с ожидания - это разница в восприятии продукта
- 429/500/503 - retry с exponential backoff (2с - 4с - 8с). 400/401 - чини код, не ретрай
- Абстракция LLMProvider - переключение провайдера за 30 минут, а не за дни рефакторинга
- API key в .env, usage.total_tokens в логи - это не опционально, это базовая гигиена production
Вопросы для размышления
- Если LLM API вернул 429 три раза подряд с exponential backoff - что делать дальше? Fallback на другую модель или вернуть ошибку пользователю?
- При каком объёме запросов в день имеет смысл добавить очередь (BullMQ/Bull) между API и LLM?
- Как защитить API key, если бекенд развёрнут в Docker на VPS - какой путь от .env до контейнера без попадания ключа в образ?
Что дальше
API подключён, ответы приходят, ошибки обработаны. Следующий шаг - научиться писать промпты, которые стабильно работают в production: не ad-hoc, а архитектурно.
- Промпт-паттерны для production — Архитектурные паттерны промптов - few-shot, chain-of-thought, structured output
- Streaming: продвинутые паттерны — Продвинутые паттерны streaming - backpressure, cancellation, WebSocket
- Стоимость и оптимизация — Как не разориться на LLM API - кеширование, model routing, prompt compression
Связанные уроки
- aie-03-llm-fundamentals — Понимание работы LLM лежит в основе каждого вызова API
- aie-08-streaming — Стриминг ответов строится поверх chat completions API
- aie-07-structured-output — Function calling здесь открывает типизированный JSON-вывод
- aie-29-cost-management — Использование API определяет стоимость токенов под контроль
- net-21-http-basics — LLM API это HTTP запрос-ответ поверх JSON
- sd-10-microservices — Провайдер LLM это ещё одна внешняя сервисная зависимость