Workflow SDK 5: долгим AI-задачам нужен контракт отмены

Tech

Долгая AI-задача опасна не только длительностью. Опасно, когда после отмены непонятно, что еще работает: модель продолжает запрос, upload завершается после таймаута, retry снова тратит бюджет.

16 июня 2026 Vercel объявила, что Workflow SDK 5 beta поддерживает AbortController и AbortSignal через границы workflow и step. Более длинные sandbox-сессии усиливают тот же вывод: долгие агентные задачи должны уметь точно останавливаться.

Диаграмма Workflow SDK: сигнал AbortController передается шагам и срабатывает по таймауту, отмене пользователя или лимиту квоты
Главное — операционный контракт: какой шаг слушает какой сигнал, как завершается и какая очистка видна.

Что изменилось

Workflow может создать AbortController, передать signal в steps и вызвать abort() при таймауте, race, hook или лимите квоты. Документация говорит о durable signal через suspensions, replay и отдельные invocations.

Отмена кооперативна: step должен передать signal в fetch/API, вызвать throwIfAborted или проверить aborted.

Почему важно

OCR, отчеты, browser automation, multi-model agents и тестовые пайплайны требуют долгого выполнения. Без контракта отмены растут затраты, лишние API calls и неоднозначные статусы.

Вопросы сообщества о production-ready и cleanup показывают операционный пробел.

Влияние

Timeout становится продуктовой политикой. Параллельные задачи могут отменять проигравших. Abort errors без retry помогают отличать отмену от сбоя.

Чеклист

Назовите user, timeout, admin, quota, parent request.

Передавайте signal в дорогие steps.

Разделяйте run.cancel() и AbortSignal.

Храните cancelled_by_user, timed_out, quota_exceeded.

Тестируйте внешнюю очистку.

Риски

SDK 5 beta/pre-release: начинайте с внутренних workflow и rollback.

Signal не откатывает удаленные side effects и не останавливает код, который его игнорирует.

Что делать

Сначала нарисуйте границы отмены: стоимость, внешние мутации и неидемпотентные операции.

Проверка за 30 минут

Причины отмены названы явно

Дорогие шаги получают или проверяют signal

Abort и retry согласованы

Внешние эффекты имеют idempotency key

Мониторинг разделяет cancelled и failed

const controller = new AbortController();
const result = await Promise.race([
  expensiveStep(controller.signal),
  sleep("30s").then(() => null),
]);
if (result === null) controller.abort();

Источники