Workflow SDK 5: tác vụ AI chạy lâu cần một hợp đồng hủy rõ ràng

Tech

Tác vụ AI dài không chỉ khó vì chạy lâu. Nó khó khi không ai biết sau khi hủy còn gì tiếp tục chạy: model call, upload, retry hoặc quota monitor.

Ngày 16/06/2026, Vercel công bố Workflow SDK 5 beta hỗ trợ AbortController và AbortSignal qua biên workflow và step. Khi runtime dài hơn, agent workflow cũng cần dừng chính xác hơn.

Sơ đồ Workflow SDK truyền tín hiệu AbortController tới các bước và hủy khi timeout, người dùng hủy hoặc vượt quota
Điểm chính là hợp đồng vận hành: bước nào nhận signal nào, dừng ra sao và cleanup nào được ghi nhận.

Thay đổi chính

Workflow có thể tạo AbortController, truyền signal vào step và gọi abort() khi timeout, race, hook hoặc quota rule thắng. Tài liệu nói signal bền qua suspension, deterministic replay và invocation riêng.

Cancellation là cooperative: step phải truyền signal vào fetch/API, gọi throwIfAborted hoặc kiểm aborted.

Vì sao quan trọng

OCR, report, browser automation, multi-model agent và test pipeline cần chạy lâu. Không có hợp đồng hủy sẽ gây lãng phí token, API call và trạng thái mơ hồ.

Câu hỏi cộng đồng về production và cleanup cho thấy nhu cầu vận hành thực tế.

Tác động

Timeout thành policy sản phẩm; parallel work hủy phần thua; abort error không retry giúp log rõ hơn.

Checklist

Tách user, timeout, admin, quota, parent request.

Truyền signal cho step tốn kém.

Phân biệt run.cancel() và AbortSignal.

Lưu cancelled_by_user, timed_out, quota_exceeded.

Test cleanup ngoài hệ thống.

Rủi ro

SDK 5 còn beta/pre-release; bắt đầu nhỏ và có rollback.

Signal không đảo ngược side effect từ xa.

Bước tiếp theo

Vẽ ranh giới hủy trước khi thêm nút: chi phí, mutation ngoài và thao tác không idempotent.

Kiểm tra 30 phút

Lý do hủy được đặt tên rõ

Step tốn kém nhận hoặc kiểm signal

Abort và retry không mâu thuẫn

Side effect ngoài có idempotency key

Observability tách cancelled khỏi failed

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

Nguồn tham khảo