에이전트가 비디오 트랜스코딩 엔드포인트를 호출합니다. 엔드포인트는 202 Accepted와 작업 ID를 반환합니다. 시스템에서 202가 무엇을 의미하는지 모르는 에이전트는 트랜스코딩이 완료되었다고 보고하고, 아직 존재하지 않는 파일을 읽는 다음 단계로 넘어갑니다.
장기 실행 작업은 특정 방식으로 에이전트를 망가뜨립니다. 동기 호출은 명확한 계약을 가집니다: 보내고, 기다리고, 답변을 얻습니다. 비동기 호출은 이를 시작과 끝으로 나누며, 그 사이의 간격에서 에이전트가 혼란스러워합니다. 이들은 성공을 조기에 선언하거나, 꽉 짜인 루프에서 수천 번 폴링하거나, 대화 턴을 열어둔 채 6분 동안 블록되어 있습니다.
이 가이드는 에이전트가 비동기 계약을 따르도록 설계하는 방법, 언제 폴링하고 언제 작업을 넘겨야 하는지, 모델이 올바르게 작동하도록 도구를 작성하는 방법, 그리고 느리거나 실패한 경우를 포함한 전체 경로를 테스트하는 방법을 다룹니다. 에이전트 오류 복구에 대한 저희 게시물은 API 호출의 실패 측면을 다루며, 이 게시물은 느리게 성공하는 경우를 다룹니다.
Apidog는 에이전트가 4분 걸리는 작업을 처리한 후 실패하는 경우를 증명해야 할 때 적합하며, 이는 프로덕션에서 발견하고 싶지 않은 상황입니다.
에이전트가 비동기 작업을 잘못 처리하는 이유
세 가지 습관이 대부분의 문제를 야기합니다.
모델은 2xx를 완료된 것으로 간주합니다. 202는 요청이 처리용으로 수락되었음을 나타내며, HTTP 의미론 사양은 처리가 완료되지 않았을 수 있음을 명시합니다. 일반적인 요청/응답 트래픽으로 훈련된 모델은 응답 본문에 다른 내용이 명시되지 않는 한 모든 2xx를 완료로 해석하는 경향이 있습니다.
루프는 비용이 많이 듭니다. 에이전트가 추론 루프 내에서 폴링하면, 모든 확인은 모델 턴과 이전 대화의 토큰을 소비합니다. 4분짜리 작업에 대해 2초마다 폴링하면 120턴이 소요되며, 실행은 컨텍스트 또는 예산을 소진하게 됩니다. 도구 응답을 컨텍스트 창 밖에 두는 것에 대한 저희 게시물은 왜 예상보다 빨리 누적되는지 설명합니다.
에이전트가 작업 추적을 놓칩니다. 작업을 시작하고 작업 ID를 반환하는 도구는 에이전트가 계속 유지해야 하는 상태를 생성합니다. ID가 긴 대화 중간에 오면 압축되어 사라질 수 있으며, 에이전트는 진행 중인 작업이 있다는 사실을 잊어버립니다.
모델이 잘못 읽을 수 없도록 응답을 설계하세요
가장 효과적인 해결책은 아키텍처가 아니라 워딩입니다. 상태 코드가 무엇이든, 본문에 무슨 일이 일어났고 다음에 무엇을 해야 할지 명확하게 기술하세요.
{
"status": "processing",
"job_id": "job_7f21c",
"message": "트랜스코딩이 시작되었으며 완료되지 않았습니다. 성공을 보고하지 마십시오. 최소 30초 후에 getJobStatus(job_id)로 상태를 확인하십시오.",
"poll_after_seconds": 30,
"estimated_duration_seconds": 240,
"status_url": "/v1/jobs/job_7f21c"
}
이는 사람 API 소비자에게는 지나치게 강압적으로 들릴 수 있습니다. 이것은 모델을 대상으로 하며, 모델은 상태 코드에서 의미를 추론하는 것보다 응답 본문의 명시적인 지시를 훨씬 더 신뢰하여 따릅니다. 세 가지 세부 사항이 역할을 합니다: "완료되지 않음"이라는 단어, 명시된 다음 도구, 그리고 최소 대기 시간입니다.
Google의 장기 실행 작업에 대한 AIP-151은 'done', 'error', 'response' 필드를 포함하는 단일 Operation 객체로 깔끔한 리소스 형태를 설명합니다. 이 구조를 복사하면 모든 느린 엔드포인트에서 일관된 인터페이스를 얻을 수 있으며, 이는 하나의 폴링 패턴을 학습한 에이전트가 모든 패턴을 처리할 수 있기 때문에 중요합니다.
상태 응답도 마찬가지로 명확하게 유지하세요:
{
"job_id": "job_7f21c",
"status": "processing",
"done": false,
"progress_percent": 45,
"elapsed_seconds": 108,
"poll_after_seconds": 45,
"message": "아직 처리 중입니다. 다음 단계로 진행하지 마십시오."
}
그리고 완료 시에는 결과가 작을 경우 인라인으로 반환하여 에이전트가 세 번째 호출을 할 필요가 없도록 합니다:
{
"job_id": "job_7f21c",
"status": "succeeded",
"done": true,
"result": { "output_url": "https://cdn.example.com/out/7f21c.mp4", "duration_seconds": 372 }
}
```모델 내부가 아닌 외부에서 폴링하세요
가장 중요한 구현 선택: 대기 로직을 에이전트의 추론 루프가 아닌 도구 래퍼에 넣으세요.
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.",
}
모델의 관점에서 보면 이것은 시간이 걸리고 최종 답변을 반환하는 하나의 도구 호출입니다. 컨텍스트 내에 폴링 루프가 없고, 잊혀진 작업 ID도 없으며, 120번의 턴도 없습니다. 백오프는 요청 수를 적정하게 유지하고, 상한선은 멈춘 작업이 영원히 실행을 막는 것을 방지합니다. Amazon의 지터가 있는 타임아웃, 재시도 및 백오프에 대한 글은 이러한 수치를 조정하기 전에 읽어볼 가치가 있는 참고 자료입니다.
두 가지 규칙이 이를 안전하게 만듭니다. 항상 대기 시간에 상한선을 두어야 하며, 타임아웃 시에는 에이전트 또는 사람이 나중에 확인할 수 있도록 항상 작업 ID를 반환해야 합니다. 모호한 결과를 반환하지 마십시오: succeeded, failed, timed_out은 세 가지 다른 결과이며 모델은 세 가지 다른 단어를 보아야 합니다.
분 단위가 아닌 시간 단위로 측정되는 작업의 경우, 래퍼 내 폴링은 의미가 없습니다. 이 경우 올바른 형태는 시작 도구 하나와 확인 도구 하나, 그리고 대화 외부에 진행 중인 작업에 대한 영구적인 기록을 두어 압축으로 인해 아무것도 손실되지 않도록 하는 것입니다. job_id, 해당 작업, 시작 시간을 저장하고 에이전트가 각 실행 시작 시 해당 목록을 읽도록 하세요.
웹훅이 더 나은 답변인 경우
폴링은 간단하고 어디서든 작동합니다. 콜백은 더 효율적이지만 실행하는 데 더 많은 작업이 필요합니다. 이러한 장단점은 저희의 웹훅 vs 폴링 비교에서 잘 다루고 있으며, 에이전트별 버전은 더 범위가 좁습니다.
작업이 몇 초에서 몇 분 걸릴 때, 에이전트가 계속 진행하기 위해 결과를 기다릴 때, 또는 공개 엔드포인트를 호스팅할 수 없을 때 폴링을 사용하세요. 대부분의 에이전트 워크로드가 여기에 해당합니다.
작업이 몇 시간 걸릴 때, 에이전트가 작업을 시작하고 다른 작업을 할 때, 또는 많은 작업이 동시에 실행되어 각 작업을 폴링하는 것이 비효율적일 때 웹훅을 사용하세요. 비용이 실제로 발생합니다: 공개 수신기, 서명 확인, 재시도 처리, 그리고 콜백이 도착했을 때 에이전트를 깨울 방법이 필요합니다. 안정적인 웹훅 설계 및 웹훅 서명 확인에 대한 저희 가이드가 해당 기초 작업을 다룹니다.
중간 옵션도 알아둘 가치가 있습니다. 서버 전송 이벤트(SSE)를 통해 작업 진행 상황을 스트리밍하면 클라이언트가 연결을 유지하므로 공개 엔드포인트 없이 푸시 방식을 사용할 수 있습니다. 이는 사람이 지켜보는 대화형 에이전트에 적합하며, SSE를 이용한 API 응답 스트리밍에 대한 저희 가이드가 구현을 다룹니다.
어떤 방법을 선택하든, 완료 경로는 멱등성(idempotent)을 가져야 합니다. 웹훅은 재시도하고, 폴링은 경쟁하며, "성공"을 두 번 본 에이전트는 다운스트림 단계를 두 번 시작해서는 안 됩니다. AI 에이전트를 위한 멱등성에 대한 저희 게시물은 이를 안전하게 만드는 핵심 요소를 다룹니다.
빠른 경로뿐만 아니라 느린 경로도 테스트하세요
비동기 버그는 테스트 환경이 빠르기 때문에 숨겨집니다. 프로덕션에서 4분 걸리는 작업이 로컬 스텁에서는 200밀리초 만에 끝나기 때문에, 에이전트는 실제로 마주할 상태를 경험하지 못합니다.
네 가지 시나리오를 의도적으로 구축할 가치가 있습니다.
정말로 느린 작업. 상태 엔드포인트를 모의(mock)하여 처음 몇 번의 호출에는 processing을, 그 다음에는 succeeded를 반환하도록 하세요. 이는 래퍼가 폴링하고, 백오프하며, 결국 반환함을 증명합니다. Apidog에서는 요청 수 또는 제어 매개변수에 따라 달라지는 모의를 사용하여 이를 구동할 수 있으므로, 동일한 테스트가 매번 동일한 방식으로 실행됩니다.

늦게 실패하는 작업. 세 번은 processing을 반환하고, 그 다음에는 오류 본문과 함께 failed를 반환하세요. 에이전트는 완료된 폴링을 완료된 작업으로 간주하는 대신 실패를 보고해야 합니다. 이것은 잘못될 경우 조용한 데이터 손실을 야기하는 경우입니다.
타임아웃. 모의가 래퍼의 상한선을 넘어 계속 processing을 반환하도록 유지하고, 도구가 예외나 가짜 성공이 아닌 작업 ID가 온전히 유지된 채 timed_out을 반환하는지 확인하세요.
중복된 완료. 웹훅 재시도 또는 경쟁 폴링을 통해 성공을 두 번 전달하고, 다운스트림 단계가 한 번만 실행되는지 확인하세요.
네 가지 모두를 시나리오로 저장하여 CI에서 실행되도록 하세요. 재실행 비용이 들지 않으며, 누군가 타임아웃을 단축하거나 오류를 삼키는 회귀를 잡아냅니다. 더 넓은 접근 방식은 저희의 API 계약 테스트 가이드에 있습니다.
문제를 드러내는 세 가지 작업
보고서 생성. 재무 에이전트가 분기별 내보내기를 요청합니다. 이것은 90초가 걸립니다. 순진한 도구를 사용하면 에이전트는 작업 ID를 받고, 보고서가 준비되었다고 알린 다음, 사용자에게 깨진 다운로드 링크를 넘겨줍니다. 블로킹 래퍼를 사용하면 90초를 기다린 후 실제 URL을 반환합니다. 동일한 API, 상반된 결과, 그리고 유일한 차이는 대기가 발생하는 지점입니다.
대량 가져오기. 운영 에이전트가 20,000개의 레코드를 업로드합니다. 가져오기는 8분 동안 실행되며 14,000번째 행에서 부분적으로 실패합니다. 이것은 순진한 성공 확인을 처벌하는 경우입니다: 작업은 완료되었으므로 done 상태는 참이지만, 결과는 거부된 행 목록을 포함합니다. 부분적인 결과를 명확하게, 개수와 함께 반환하고, 에이전트가 다음으로 진행하기 전에 이를 읽도록 하세요.
모델 및 빌드 파이프라인. 에이전트가 40분 걸리는 학습 실행 또는 CI 빌드를 트리거합니다. 여기서는 래퍼 내 폴링이 잘못된 형태입니다; 실행이 턴을 너무 오랫동안 열어둘 것입니다. 작업을 시작하고, 영구 스토리지에 ID를 기록하고, 턴을 종료한 다음, 예약된 확인 또는 콜백이 후속 작업을 깨우도록 하세요. 다중 에이전트 핸드오프 및 컨텍스트 전달에 대한 저희 게시물은 해당 상태를 손실 없이 실행 간에 이동하는 방법을 다룹니다.
부분 결과에 형태를 부여하세요
오랜 시간 걸리는 작업은 종종 성공과 실패 사이의 어딘가에서 끝나며, 두 가지 상태 모델은 이에 대해 거짓말을 강요합니다. 세 번째 상태를 명시적으로 만드세요:
{
"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": "가져오기 완료. 140개 행이 실패하여 기록되지 않았습니다. 성공을 보고하기 전에 오류를 검토하십시오."
}
해당 페이로드에는 두 가지 중요한 점이 있습니다. 개수는 인라인으로 제공되므로 에이전트가 추가 호출 없이 결정할 수 있습니다. 실패한 행은 제한이 있는 URL 뒤에 있으므로 140개의 오류 객체가 컨텍스트에 원치 않게 포함되지 않습니다.
멈춘 작업을 누군가는 확인해야 합니다
타임아웃 경로는 작업 ID와 작업이 여전히 실행 중이라는 메시지로 끝납니다. 그것은 올바른 반환 값이지만, 사람에게 도달해야만 유용합니다.
에이전트가 귀하의 서비스인 경우, 팀이 이미 모니터링하는 대기열로 라우팅하세요. 에이전트가 할당된 작업을 수행하는 코딩 런타임인 경우, 이를 실행하는 플랫폼에는 일반적으로 이러한 정보가 도착할 곳이 있습니다. Sharkly에서는 블록된 상태로 끝나는 실행은 실행 상태와 결과와 함께 해당 작업에 남아 있으며, 받은 편지함은 사람의 답변이나 검토가 필요한 항목을 일반 업데이트와 분리합니다. 중요한 것은 특정 도구가 아닙니다. "여전히 실행 중, 나중에 확인"에는 담당자가 필요하며, 그렇지 않으면 "아무도 확인하지 않음"이 됩니다.

간단한 체크리스트
모든 느린 엔드포인트는 작업 ID, 상태 URL, 그리고 작업이 완료되지 않았음을 나타내는 쉬운 메시지를 반환합니다.상태 응답에는 모델이 해석해야 하는 문자열이 아닌, 불리언 done 필드가 포함되어야 합니다.폴링은 지수 백오프 및 엄격한 상한선과 함께 도구 래퍼 내에 존재해야 합니다.타임아웃은 작업 ID를 반환하여 작업이 손실되지 않고 재개될 수 있도록 합니다.성공, 실패, 타임아웃은 세 가지 별개의 반환 값입니다.몇 분보다 긴 모든 진행 중인 작업은 대화 외부에 기록됩니다.완료 처리는 신호가 폴링이든 콜백이든 멱등성을 가져야 합니다.느린, 늦게 실패하는, 타임아웃된, 그리고 중복된 완료에 대해 모두 저장된 테스트가 있어야 합니다.
응답 워딩과 래퍼를 올바르게 설정하면 장기 실행 작업은 에이전트에게 특별한 경우가 아니게 됩니다. 에이전트는 도구를 호출하고, 기다리고, 답변을 얻습니다. 이것이 에이전트가 가장 잘 처리하는 계약입니다. 테스트와 함께 느린 작업 모의를 구축하려면 Apidog를 다운로드하세요.
자주 묻는 질문
API는 비동기 시작에 대해 202 또는 200을 반환해야 하나요? 202 Accepted는 정직한 코드이며 표준 클라이언트에게 처리가 완료되지 않았음을 알립니다. 에이전트의 경우 모델이 가장 신뢰할 수 있게 읽는 것은 본문이므로, 202 Accepted 코드에만 의존하지 마십시오. 둘 다 사용하세요.
도구 래퍼는 포기하기 전에 얼마나 오래 기다려야 하나요? 상한선을 엔드포인트의 현실적인 최악의 경우보다 약간 높게 설정하세요. 일반적으로 2분에서 10분입니다. 그 이상은 래퍼가 대화 턴을 너무 오랫동안 막는 것이며, 나중에 확인하는 도구가 더 나은 형태입니다.
어떤 폴링 간격을 사용해야 하나요? 서버가 제공하는 poll_after_seconds 힌트부터 시작하여 약 1.5배의 백오프를 사용하고 60초 정도에서 상한선을 두세요. 고정된 1초 폴링은 요청을 낭비하고 속도 제한에 걸릴 수 있으며, 이는 저희의 속도 제한 초과 가이드에서 다루고 있습니다.
에이전트가 기다리는 동안 유용한 작업을 수행할 수 있나요? 오케스트레이터가 동시 도구 호출을 지원하는 경우에만 가능합니다. 지원한다면, 작업을 시작하고, 독립적인 작업을 수행한 다음 상태를 확인하세요. 지원하지 않는다면, 블로킹 래퍼가 직접 만든 스케줄러보다 간단하고 오류 발생 가능성이 적습니다.
에이전트가 성공을 조기에 주장하는 것을 어떻게 막을 수 있나요? 응답 본문에 명시적으로 표현하고, 불리언 done 필드를 노출하며, 완료 도구만이 결과가 나타나는 유일한 장소가 되도록 하세요. 시작 응답에 결과가 포함되어 있지 않다면, 모델이 결과로 보고할 것이 없습니다.
노트북에서 실행되는 에이전트에서 웹훅이 작동하나요? 공개 엔드포인트가 없으므로 직접 작동하지 않습니다. 개발을 위해 터널을 사용하거나(예: 웹훅 서비스로 로컬호스트 API 테스트 가이드), 에이전트가 주소 지정 가능한 곳에서 실행될 때까지 폴링 방식을 고수하세요.
