DEV Community

Cover image for Đặc vụ AI và Cuộc gọi API Kéo dài: Polling so với Webhooks
Sebastian Petrus
Sebastian Petrus

Posted on Originally published at apidog.com

Đặc vụ AI và Cuộc gọi API Kéo dài: Polling so với Webhooks

Thiết kế API bất đồng bộ để agent không báo thành công quá sớm

Agent gọi điểm cuối chuyển mã video, nhận 202 Accepted cùng ID công việc, rồi báo rằng quá trình đã hoàn tất và chuyển sang bước đọc một tệp chưa tồn tại. Đây là lỗi điển hình khi agent hiểu mọi phản hồi 2xx là thành công cuối cùng.

Thử Apidog ngay hôm nay

Hoạt động chạy dài tách hợp đồng API thành hai phần: bắt đầuhoàn tất. Nếu không thiết kế rõ ràng, agent có thể:

  • tuyên bố thành công ngay sau 202;
  • thăm dò liên tục, làm tốn token và lượt suy luận;
  • bị treo trong một lượt hội thoại quá lâu;
  • quên job_id sau khi ngữ cảnh bị nén.

Bài viết này tập trung vào đường dẫn “thành công chậm”; với lỗi API tổng quát, xem hướng dẫn về khôi phục lỗi agent.

Minh họa quy trình xử lý tác vụ bất đồng bộ

Vì sao agent xử lý bất đồng bộ sai?

Agent đọc 2xx là đã xong

202 Accepted chỉ có nghĩa là máy chủ đã nhận yêu cầu để xử lý, không đảm bảo công việc đã hoàn thành. Đặc tả ngữ nghĩa HTTP cũng nêu rõ điều đó.

Vì vậy, đừng buộc agent phải suy luận chỉ từ mã trạng thái.

Thăm dò trong vòng lặp suy luận rất tốn kém

Thăm dò mỗi 2 giây cho một tác vụ 4 phút tạo ra 120 lần kiểm tra. Mỗi lần có thể tiêu tốn một lượt mô hình và thêm ngữ cảnh hội thoại. Xem thêm: giữ phản hồi công cụ ra khỏi cửa sổ ngữ cảnh.

job_id dễ bị mất

Nếu công cụ trả về ID công việc nhưng agent phải tự nhớ nó trong một cuộc hội thoại dài, ID có thể bị nén hoặc bị quên trước khi công việc hoàn thành.

Thiết kế phản hồi không thể bị đọc sai

Phần thân phản hồi cần nói rõ ba điều:

  1. Công việc chưa hoàn tất.
  2. Công cụ hoặc URL cần dùng tiếp theo.
  3. Khoảng thời gian tối thiểu trước khi kiểm tra lại.
{
  "status": "processing",
  "job_id": "job_7f21c",
  "message": "Quá trình chuyển mã đã BẮT ĐẦU và CHƯA hoàn tất. Đừng báo cáo thành công. Kiểm tra trạng thái bằng getJobStatus(job_id) sau ít nhất 30 giây.",
  "poll_after_seconds": 30,
  "estimated_duration_seconds": 240,
  "status_url": "/v1/jobs/job_7f21c"
}
Enter fullscreen mode Exit fullscreen mode

Các từ như “chưa hoàn tất”, tên công cụ kế tiếp và thời gian chờ tối thiểu hiệu quả hơn việc để mô hình tự diễn giải 202.

Bạn có thể áp dụng hình dạng tài nguyên của AIP-151 của Google về các hoạt động chạy dài: một đối tượng vận hành nhất quán với các trường done, errorresponse.

Phản hồi kiểm tra trạng thái nên đơn giản:

{
  "job_id": "job_7f21c",
  "status": "processing",
  "done": false,
  "progress_percent": 45,
  "elapsed_seconds": 108,
  "poll_after_seconds": 45,
  "message": "Vẫn đang xử lý. Không chuyển sang bước tiếp theo."
}
Enter fullscreen mode Exit fullscreen mode

Khi thành công, trả kết quả nhỏ trực tiếp để tránh thêm một lệnh gọi API:

{
  "job_id": "job_7f21c",
  "status": "succeeded",
  "done": true,
  "result": {
    "output_url": "https://cdn.example.com/out/7f21c.mp4",
    "duration_seconds": 372
  }
}
Enter fullscreen mode Exit fullscreen mode

Thăm dò bên ngoài mô hình, không phải bên trong mô hình

Với tác vụ kéo dài vài giây đến vài phút, hãy đặt vòng lặp chờ trong trình bao bọc công cụ:

import time

def start_and_await_transcode(client, source_url, max_wait=600):
    job = client.post("/v1/transcode", json={"source_url": source_url}).json()
    job_id = job["job_id"]
    delay = job.get("poll_after_seconds", 5)
    waited = 0

    while waited < max_wait:
        time.sleep(delay)
        waited += delay
        status = client.get(f"/v1/jobs/{job_id}").json()

        if status.get("done"):
            if status["status"] == "succeeded":
                return {"status": "succeeded", "result": status["result"]}
            return {"status": "failed", "error": status.get("error")}

        delay = min(int(delay * 1.5), 60)

    return {
        "status": "timed_out",
        "job_id": job_id,
        "message": f"Still running after {max_wait}s. Job {job_id} continues in the background.",
    }
Enter fullscreen mode Exit fullscreen mode

Với agent, đây chỉ là một lệnh gọi công cụ: gọi, chờ, nhận kết quả cuối cùng. Không có 120 lượt thăm dò trong ngữ cảnh, không mất job_id, và không có vòng lặp suy luận lãng phí.

Áp dụng hai quy tắc:

  • Luôn có max_wait.
  • Khi hết thời gian, luôn trả về job_id.

Phân biệt rõ ba kết quả:

  • succeeded
  • failed
  • timed_out

Dùng backoff theo cấp số nhân, giới hạn tối đa khoảng 60 giây, và cân nhắc jitter theo hướng dẫn của Amazon về thời gian chờ, thử lại và lùi thời gian với jitter.

Với công việc kéo dài hàng giờ, không nên giữ lượt hội thoại mở. Hãy dùng hai công cụ:

  1. startJob() để khởi tạo.
  2. getJobStatus(job_id) để kiểm tra sau.

Đồng thời lưu bền vững job_id, tác vụ liên quan và thời điểm bắt đầu ở ngoài ngữ cảnh hội thoại.

Ví dụ về theo dõi trạng thái tác vụ chạy dài

Khi nào nên dùng webhook?

Tình huống Lựa chọn phù hợp
Công việc mất vài giây đến vài phút Thăm dò
Agent cần kết quả để tiếp tục ngay Thăm dò trong trình bao bọc
Công việc kéo dài hàng giờ Webhook hoặc lịch kiểm tra
Có nhiều công việc chạy đồng thời Webhook
Không thể có điểm cuối công khai Thăm dò hoặc SSE
Người dùng đang theo dõi tiến độ trực tiếp SSE

Xem so sánh chi tiết giữa webhook và thăm dò.

Webhook hiệu quả nhưng yêu cầu:

  • điểm cuối công khai;
  • xác minh chữ ký;
  • xử lý retry;
  • cơ chế đánh thức agent khi callback đến.

Tham khảo hướng dẫn về thiết kế webhook đáng tin cậyxác minh chữ ký webhook.

Nếu client có thể giữ kết nối, truyền phát phản hồi API với SSE là lựa chọn trung gian tốt: có ngữ nghĩa đẩy mà không cần webhook công khai.

Dù dùng polling, webhook hay SSE, xử lý hoàn tất phải idempotent. Một tín hiệu thành công đến hai lần không được chạy bước tiếp theo hai lần. Xem thêm về tính bất biến cho các agent AI.

Kiểm tra cả đường dẫn chậm

Stub cục bộ hoàn thành trong 200 ms không phản ánh công việc thật mất 4 phút. Hãy lưu các kịch bản sau trong CI:

  1. Công việc chậm: trả processing vài lần rồi succeeded. Kiểm tra polling, backoff và kết quả cuối.
  2. Thất bại muộn: trả processing ba lần, sau đó failed với nội dung lỗi.
  3. Hết thời gian: luôn trả processing đến khi vượt max_wait; công cụ phải trả timed_out và giữ nguyên job_id.
  4. Hoàn thành trùng lặp: gửi thành công hai lần qua webhook retry hoặc polling cạnh tranh; bước tiếp theo chỉ được chạy một lần.

Bạn có thể dùng Apidog để tạo mock thay đổi theo số lượt gọi hoặc tham số điều khiển, giúp kiểm thử luôn lặp lại được. Xem thêm hướng dẫn về kiểm thử hợp đồng API.

Xử lý kết quả một phần

Một công việc có thể hoàn thành nhưng vẫn có bản ghi thất bại. Đừng ép nó vào mô hình nhị phân thành công/thất bại.

{
  "job_id": "job_a11f",
  "status": "completed_with_errors",
  "done": true,
  "summary": {
    "processed": 20000,
    "succeeded": 19860,
    "failed": 140
  },
  "errors_url": "/v1/jobs/job_a11f/errors?limit=50",
  "message": "Nhập dữ liệu hoàn tất. 140 hàng bị lỗi và không được ghi. Hãy xem xét lỗi trước khi báo cáo thành công."
}
Enter fullscreen mode Exit fullscreen mode

Đặt số liệu tổng hợp trực tiếp trong phản hồi để agent có thể quyết định ngay. Đặt danh sách lỗi sau URL phân trang hoặc có giới hạn để không làm đầy ngữ cảnh.

Ba ví dụ thực tế

Tạo báo cáo

Agent tài chính yêu cầu xuất dữ liệu quý, mất 90 giây. Nếu chỉ nhận job_id, agent có thể gửi liên kết tải xuống quá sớm. Với trình bao bọc chặn, agent chỉ nhận URL thật khi báo cáo đã sẵn sàng.

Nhập dữ liệu hàng loạt

Agent vận hành nhập 20.000 bản ghi trong 8 phút và thất bại một phần ở hàng 14.000. done: true không đủ để gọi đây là thành công; hãy trả completed_with_errors, số lượng thành công/thất bại và URL lỗi.

Huấn luyện mô hình hoặc xây dựng CI

Tác vụ kéo dài 40 phút không phù hợp với trình bao bọc chặn. Hãy bắt đầu công việc, lưu job_id, kết thúc lượt hiện tại và dùng callback hoặc lịch kiểm tra để chạy bước sau. Xem cách duy trì trạng thái giữa các lần chạy trong hướng dẫn về bàn giao và truyền ngữ cảnh giữa nhiều agent.

Đừng để công việc bị đình trệ vô chủ

timed_out là kết quả đúng nếu tác vụ vẫn chạy, nhưng chỉ hữu ích khi có người hoặc hệ thống nhận trách nhiệm theo dõi nó.

Hãy đưa trạng thái đó vào hàng đợi vận hành, hệ thống cảnh báo hoặc nền tảng thực thi. Ví dụ, Sharkly hiển thị lần chạy bị chặn trong Nhiệm vụ cùng trạng thái và kết quả. Điểm quan trọng không phải công cụ cụ thể: “vẫn đang chạy, kiểm tra sau” phải có người chịu trách nhiệm.

Danh sách kiểm tra

  • Mỗi điểm cuối chậm trả về job_id, status_url và thông báo rõ rằng công việc chưa hoàn thành.
  • Phản hồi trạng thái có done boolean.
  • Polling nằm trong trình bao bọc công cụ, có exponential backoff và giới hạn cứng.
  • Timeout luôn trả về job_id.
  • succeeded, failedtimed_out là ba kết quả khác nhau.
  • Công việc dài hơn vài phút được lưu bên ngoài hội thoại.
  • Đường dẫn hoàn tất idempotent, bất kể nhận tín hiệu qua polling hay callback.
  • CI kiểm tra tác vụ chậm, thất bại muộn, timeout và hoàn thành trùng lặp.

Câu hỏi thường gặp

API nên trả 202 hay 200 khi khởi động tác vụ bất đồng bộ?

Dùng 202 Accepted. Tuy nhiên, đừng chỉ dựa vào nó cho agent: phản hồi cũng phải nói rõ công việc chưa hoàn thành.

Trình bao bọc nên chờ bao lâu?

Đặt giới hạn cao hơn một chút so với trường hợp xấu nhất thực tế, thường từ 2 đến 10 phút. Nếu lâu hơn, chuyển sang mô hình bắt đầu rồi kiểm tra sau.

Khoảng thăm dò nên là bao lâu?

Bắt đầu bằng poll_after_seconds từ máy chủ nếu có. Sau đó nhân khoảng chờ với khoảng 1.5, giới hạn ở khoảng 60 giây. Thăm dò cố định mỗi giây dễ lãng phí yêu cầu và gây giới hạn tỷ lệ; xem hướng dẫn về vượt quá giới hạn tỷ lệ.

Agent có thể làm việc khác trong khi chờ không?

Có, nếu bộ điều phối hỗ trợ lệnh gọi công cụ đồng thời. Nếu không, trình bao bọc chặn đơn giản thường ít lỗi hơn một scheduler tự xây.

Làm sao ngăn agent báo thành công sớm?

Nói rõ trong phản hồi rằng công việc chưa hoàn thành, cung cấp done: false, và chỉ trả kết quả ở công cụ hoàn tất hoặc phản hồi trạng thái thành công.

Webhook có dùng được với agent chạy trên máy tính xách tay không?

Không trực tiếp vì máy tính xách tay thường không có điểm cuối công khai. Dùng tunnel khi phát triển, như trong hướng dẫn kiểm thử API localhost bằng dịch vụ webhook, hoặc tiếp tục dùng polling.

Thiết kế phản hồi rõ ràng và đặt phần chờ trong trình bao bọc công cụ sẽ biến tác vụ chạy dài thành hợp đồng mà agent xử lý tốt nhất: gọi công cụ, chờ, nhận kết quả. Tải xuống Apidog để xây dựng mock cho các tác vụ chậm và đưa các kịch bản này vào kiểm thử.

Top comments (0)