Технический разбор
Batch задачи OpenAI-совместимого API: надёжный запуск и сверка результатов
Batch задачи OpenAI-совместимого API полезны, когда нужно обработать каталог, набор документов или ночную очередь, а не ждать ответ на один экранный клик. Риск здесь не в размере массива JSON, а в повторной отправке, смешении tenant, потере соответствия между входом и ответом и неконтролируемом расходе. Надёжный процесс делит загрузку на внутренние операции, закрепляет manifest, проверяет результаты и передаёт запросы в gateway только с сервера. RussiaAPI может предоставлять разные асинхронные возможности в зависимости от текущего каталога и контракта; сначала сверяйте доступный сценарий в документации.
RUSSIAAPI_API_KEY на сервере; не передавайте внешние ключи, cookie, пароли, коды подтверждения или лишние персональные данные.Сформулируйте единицу работы
Не называйте batch «списком из десяти тысяч prompt». Для продукта это набор независимых записей с владельцем, целью, версией преобразования и допустимым состоянием. Сначала создайте внутренний batch ID, укажите tenant, маршрут, разрешённую модель и лимит единиц. Затем зафиксируйте manifest: для каждой строки храните свой item ID, хеш нормализованного входа и безопасный статус. Сам текст документа, пользовательский файл и токен авторизации не обязаны оказаться в журнале manifest.
Такая единица делает повторяемость измеримой. Если worker перезапускается, он читает незавершённые item ID, а не собирает новый список по памяти. Если заказчик спрашивает о конкретном результате, backend находит его по внутреннему ID и проверяет владельца. Не связывайте результат с позицией в массиве: при частичном сбое порядок может измениться. Для подготовки моделей пригодится контролируемое кеширование каталога, но наличие ID в каталоге ещё не подтверждает доступ к batch-функции.
Разделите подготовку и отправку
На этапе подготовки проверьте схему, размер записи, разрешённую модель и права на источник. Ошибку конкретной строки помечайте локально как invalid, а не посылайте весь набор с надеждой, что provider объяснит всё за вас. На этапе отправки worker берёт небольшую порцию только после резервирования лимита и устанавливает deadline. Вход из браузера не должен сам выбирать внешний URL, передавать Authorization или создавать batch от имени другого tenant.
Размер порции выбирают по измерениям своего приложения: длительности, параллелизму, памяти worker и фактическим ограничениям текущего контракта. Не подставляйте выдуманный максимум в интерфейс. Лучше начать с малой партии и метрик успешной единицы, чем создать одну огромную транзакцию, которую невозможно безопасно повторить. Если gateway не документирует асинхронный batch endpoint, ваша очередь всё равно может выполнять обычные запросы с контролируемой concurrency; не выдавайте внутреннюю реализацию за функцию производителя модели.
Используйте внутреннюю идемпотентность
Одна строка должна иметь один внутренний ключ операции. Он создаётся сервером и привязан к tenant, версии обработки и item ID. При повторной доставке worker сначала проверяет таблицу операций: завершённый item не отправляется снова, работа в состоянии running получает наблюдаемый статус, а конфликтующий payload требует нового item ID. Это защищает от сетевой ошибки между отправкой и записью ответа лучше, чем повтор с тем же prompt или именем файла.
Не воспринимайте идемпотентность как разрешение на бесконечные ретраи. Timeout означает неопределённость: возможно, gateway уже принял запрос. Сохраните внутренний request ID, переведите запись в проверяемое состояние и сверяйте допустимый статус по контракту. Практику повторов без дублей раскрывает отдельное руководство. Ключ операции не равен API key, не должен быть секретом поставщика и не даёт доступ к чужим данным.
Серверный worker с ограниченной очередью
Пример ниже написан для Node.js 18+ и показывает форму внутреннего worker, а не неподтверждённый контракт batch endpoint. Он отправляет обычные chat-completions из allowlist, устанавливает deadline и возвращает нейтральный результат. Функции хранилища должны выполняться транзакционно с уникальным индексом tenant + item ID. Перед запуском задайте существующий в вашем каталоге RUSSIAAPI_TEXT_MODEL и собственный ключ RussiaAPI только в окружении worker.
const allowed = new Set([process.env.RUSSIAAPI_TEXT_MODEL]);
export async function runBatchItem({ tenantId, itemId, model, prompt }) {
if (!tenantId || !itemId || !allowed.has(model)) return { status: 'rejected' };
const existing = await findItem(tenantId, itemId); // unique index: tenantId + itemId
if (existing?.status === 'completed') return existing;
if (existing?.status === 'running') return { status: 'checking', itemId };
await reserveItem({ tenantId, itemId, model, status: 'running' });
try {
const response = await fetch('https://russiaapi.com/v1/chat/completions', {
method: 'POST',
headers: { authorization: `Bearer ${process.env.RUSSIAAPI_API_KEY}`, 'content-type': 'application/json' },
body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }] }),
signal: AbortSignal.timeout(15_000)
});
if (!response.ok) return await markRetryable(itemId, response.status);
const data = await response.json();
return await completeItem(itemId, data.choices?.[0]?.message?.content ?? '');
} catch { return await markChecking(itemId); }
}Код не печатает prompt и Authorization. В рабочем сервисе добавьте аутентификацию запуска batch, лимит дневного расхода, шифрованное хранение результата при необходимости и алерт на рост ошибок. Не запускайте тысячи Promise.all: собственный пул должен принимать столько задач, сколько ваша база, сеть и подтверждённый лимит реально выдерживают. Технические тайм-ауты и отмена описаны в руководстве по deadline.
Сверяйте результат по item ID
После ответа сохраните status, внутренний item ID, версию маршрута, model ID, длительность и безопасный checksum результата. При необходимости сам результат хранится в отдельном защищённом хранилище с политикой доступа, а не в общем текстовом логе. Сверка проверяет, что каждый принятый ответ относится к ожидаемому tenant и item ID, что нет лишних или повторных записей и что число terminal-состояний совпадает с manifest. Только затем batch получает статус completed.
Разделяйте технический успех HTTP и бизнес-успех. Ответ 200 с пустым или невалидным для вашего сценария полем может потребовать локальной проверки схемы. Нельзя незаметно подменять его второй моделью, если это меняет обещание пользователю, стоимость или правила обработки данных. Для структурированного вывода применяйте серверную валидацию из статьи о JSON Schema; она не отменяет тестовый набор на реальных обезличенных примерах.
Закройте batch наблюдаемостью и доступом
Оператору нужны показатели: сколько item подготовлено, зарезервировано, успешно завершено, отклонено локальной проверкой, требует сверки или завершилось окончательно с ошибкой. Пользователю достаточно статуса его собственной задачи и понятного действия; не показывайте чужие item ID, внутренние заголовки или ответы upstream. Логи связывайте через request ID и редактируйте секреты, как в безопасной диагностике ошибок.
Перед выпуском проведите тесты: повтор worker после искусственного timeout, одновременный запуск одного manifest, неверная строка среди корректных, отказ 429 и чтение результата другим tenant. Проверьте, что расходы считаются по завершённой единице согласно вашей политике, а не по оптимистичному счётчику в браузере. Batch-процесс повышает предсказуемость, но не служит способом обхода лимитов, условий поставщика, прав на данные или применимых требований.
Проверьте сценарий в RussiaAPI
Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и начните с обезличенного server-side smoke test. Расширяйте доступ и нагрузку только после измеримой проверки.
FAQ
Нужен ли специальный batch endpoint?
Нет. Сначала уточните документацию и договор RussiaAPI. Если специальной функции нет, внутренний worker может обрабатывать обычные запросы ограниченным пулом. Не называйте это официальной возможностью производителя модели.
Можно ли повторить весь batch после timeout?
Нет, не автоматически. Повторяйте только строки, у которых внутреннее состояние и контракт позволяют это сделать. Для неопределённой отправки сначала выполняйте сверку по своей таблице операций.
Что отдавать пользователю?
Только статус и результат его авторизованной операции. Внутренние идентификаторы, ключи, заголовки, необработанные ошибки и данные других tenant не должны попадать в клиентский ответ.