AI 에이전트 병렬 실행, 느린 작업을 언제까지 기다릴까?

AI 에이전트 병렬 실행에서 느린 작업을 언제까지 기다릴지는 필수 결과의 범위와 응답 마감으로 정해야 합니다. 일부 결과만으로 초안을 낼 수 있다면 마감 시점에 완료분을 모으되, 보안 점검처럼 빠지면 안 되는 작업이 남아 있다면 완료로 처리하지 않습니다.
2026년 9월 21일 기준 Anthropic 연구 시스템 설계 글과 Python 3.11 asyncio 공식 문서를 바탕으로 설명합니다. 최신 출시 뉴스가 아닌 기술 해설입니다.
- 독립 작업과 순서가 필요한 작업을 나누는 기준
- 느린 작업이 전체 응답을 지연시키는 이유
- 마감 시점에 완료·미완료 결과를 구분하는 구조
- 전부 대기·부분 수집·먼저 채택 정책 비교
- 취소 요청 뒤 확인해야 하는 실행·외부 상태
주요 원문: Anthropic 멀티에이전트 연구 시스템 설계 사례
1. AI 에이전트 병렬 실행은 어떤 작업에 적합한가?

서로의 결과 없이 시작할 수 있는 조회
병렬 실행은 여러 에이전트나 도구 호출을 겹쳐 진행하고 결과를 모으는 방식입니다. 같은 제품의 API 문서·라이선스·운영 제약을 각자 조사하는 일은 공통 입력만 전달하면 시작할 수 있습니다. 반면 제품명을 확정한 뒤 해당 제품의 가격을 조회하는 일은 앞선 판단에 의존합니다.
Anthropic은 고정 작업을 나누는 병렬화와 조정자가 하위 작업을 동적으로 정하는 패턴을 구분합니다.
동시에 실행하면 충돌하는 쓰기
입력이 독립적이어도 같은 문서나 계정 설정에 쓰면 충돌할 수 있습니다. 작업별 산출물을 분리하고 최종 반영을 직렬화해야 합니다.
2. 병렬로 돌려도 전체 응답이 느려지는 이유
전부 기다리면 마지막 작업이 종료 시점을 결정
모든 결과가 있어야 합성할 수 있는 구조에서는 가장 늦게 끝나는 작업이 병목이 됩니다. 빠른 작업이 먼저 끝나도 조정자는 마지막 결과를 기다립니다. 병렬화로 개별 작업 시간이 같아지는 것은 아니며, 합성·검증에 필요한 시간도 별도로 남습니다.
Anthropic의 2025년 설계 글은 묶음 대기의 병목과 비동기화에 따른 상태 조정·오류 전파의 복잡성을 설명합니다. 당시 사례를 최신 제품의 현재 구현으로 일반화하면 안 됩니다.
요청 수를 늘릴수록 대기열도 늘어날 수 있음
외부 API 한도나 도구 서버 처리량이 낮으면 작업 수를 늘려도 대기열만 길어집니다. 동시 실행 수와 실제 호출 시작 시각, 서버 응답 대기를 따로 기록해야 합니다.
3. 마감 시점에 완료 결과와 미완료 작업을 나누는 구조

작업별 제한과 전체 마감은 다름
뒤늦게 시작한 작업마다 제한 시간을 새로 주면 전체 요청 시간이 늘어납니다. 요청 시작 시 공유 마감을 정하고, 합성 시간을 제외한 남은 예산을 하위 작업에 전달해야 합니다.
작업 상태와 답변에 쓸 수 있는 결과를 분리
조정자는 성공·실패·미완료·취소 상태를 구별하고 작업 식별자와 함께 보관해야 합니다. done에 속한다는 사실만으로 정상 결과라고 간주할 수는 없습니다. 예외로 끝났거나 취소된 작업도 끝난 작업이므로, 결과를 읽을 때 오류를 분리해야 합니다.
Python의 asyncio.wait()는 타임아웃 시 done과 pending을 반환하며, 미완료 작업을 자동 취소하지 않습니다. wait_for()는 타임아웃 시 대상 작업 취소를 시도하고 취소 완료를 기다리므로 실제 대기 시간이 지정한 제한을 넘을 수 있습니다. 두 API의 차이는 Python 3.11 공식 대기 문서에서 확인할 수 있습니다.
4. 로컬 실험: 모두 대기와 부분 수집은 어떻게 다른가?
가상 작업 세 개를 모두 기다린 결과

이번 글에서는 Python의 asyncio.sleep()으로 A·B·C 작업에 각각 0.1초·0.2초·1.0초 지연을 주었습니다. 실제 에이전트나 모델의 성능 측정이 아니라 대기 동작만 재현한 실험입니다. 제한 없이 기다린 실행에서는 completed=A,B,C, pending=none이 출력됐습니다.
0.4초 마감 뒤 미완료 작업을 정리한 결과

같은 작업에 0.4초 대기를 적용한 실행은 A·B를 수집하고 C를 미완료로 남겼습니다. 취소 요청 전에는 pending_cancelled_before_request=False였고, 명시적으로 취소한 뒤 정리를 기다리자 cancelled_after_cleanup=C와 all_tasks_done=True가 확인됐습니다.
아래 코드를 parallel-demo.py로 저장하고 캡처의 명령을 실행하면 됩니다. 운영용 오류 복구·영속 저장·원격 취소 기능은 제외했습니다.
"""Local waiting-policy simulation; no LLM, API or external writes."""
import asyncio
import sys
async def worker(name, delay):
await asyncio.sleep(delay)
return name
async def main(mode):
tasks = [asyncio.create_task(worker(n, d), name=n)
for n, d in [("A", 0.1), ("B", 0.2), ("C", 1.0)]]
try:
if mode == "all":
done, pending = await asyncio.wait(tasks)
else:
done, pending = await asyncio.wait(tasks, timeout=0.4)
print("simulation_only=True; no LLM/API calls")
print("policy=" + mode)
print("completed=" + ",".join(sorted(t.result() for t in done)))
print("pending=" + (",".join(sorted(t.get_name() for t in pending)) or "none"))
print("pending_cancelled_before_request=" + str(any(t.cancelled() for t in pending)))
finally:
for task in tasks:
if not task.done():
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
print("cancelled_after_cleanup=" + (",".join(sorted(t.get_name() for t in tasks if t.cancelled())) or "none"))
print("all_tasks_done=" + str(all(t.done() for t in tasks)))
asyncio.run(main(sys.argv[1]))
위 수치는 실험에 직접 지정한 지연과 마감입니다. 실제 LLM 응답 속도·서비스 제한 시간·추천 운영값을 뜻하지 않으며, 시스템 부하에 따라 완료 작업 구분이 달라질 수 있습니다.
5. 전부 대기·부분 수집·먼저 채택 중 무엇을 선택할까?

결과의 완전성이 중요한 작업
| 정책 | 적합한 예 | 필요한 판정 |
|---|---|---|
| 전부 대기 | 배포 전 필수 점검 | 모든 필수 검사 성공 |
| 부분 수집 | 탐색용 조사 초안 | 핵심 근거 확보·누락 표시 |
| 먼저 채택 | 동등한 후보 경로 탐색 | 먼저 도착한 유효 결과 검증 |
표는 특정 제품의 기본값이 아닌 설계 제안입니다. 필요한 검사를 빠뜨릴 수 없는 배포와 불완전함을 허용하는 조사 초안을 구분했습니다.
먼저 끝난 결과가 정답인 것은 아님
FIRST_COMPLETED는 어떤 작업이 끝나거나 취소되면 반환하는 조건입니다. 성공이나 품질을 보장하지 않으므로 반환 후 검증을 통과한 결과인지 확인해야 합니다. 여러 초안을 경쟁시키는 경우에도 채택 기준 없이 첫 문장만 고르면 빠른 오답이 이길 수 있습니다.
완료 여부를 판정하는 평가 기준은 AI 코딩 에이전트 검증 루프: 테스트 통과와 완료의 차이에서 다룬 요구사항 단위 검증과 연결됩니다.
6. 타임아웃과 취소가 해결하지 못하는 문제

취소 요청 이후에도 정리 시간이 필요
asyncio 취소는 코루틴에 취소 예외를 전달하는 협력적 방식입니다. 작업이 예외를 삼키거나 이벤트 루프를 오래 막으면 기대한 시점에 종료되지 않을 수 있습니다. 공식 문서는 try/finally로 정리를 수행하고, CancelledError를 잡았다면 일반적으로 정리 후 다시 전파하도록 권고합니다.
외부 프로세스나 원격 모델 호출이 로컬 코루틴 취소와 함께 중단되는지도 별도 확인해야 합니다. 클라이언트에서 기다리기를 멈춘 것만으로 서버 연산·과금이 끝났다고 단정할 수 없습니다. SDK와 서버가 제공하는 취소·상태 조회 계약을 확인해야 합니다.
이미 반영된 외부 변경은 별도 확인
업로드나 메시지 전송이 끝난 뒤 응답만 늦게 도착하는 상황에서는 타임아웃이 발생해도 변경이 이미 반영됐을 수 있습니다. 이때 재호출부터 하면 중복 작업이 생길 수 있으므로, 원래 작업의 식별자로 외부 상태를 먼저 조회해야 합니다. 재시도 설계는 AI 에이전트 재시도 중복 실행 방지: 멱등성 설계에서 이어서 확인할 수 있습니다.
코루틴 취소는 트랜잭션 롤백 명령이 아닙니다. 결제·발행·삭제 같은 쓰기를 여러 경로로 경쟁 실행하는 방식은 읽기 전용 검색과 같은 정책으로 다루면 안 됩니다.
7. 운영 전에 확인할 병렬 실행 체크리스트
정상 실행보다 실패 조합을 먼저 검증
- 한 작업만 예외를 내도 나머지 결과를 의도대로 보존하는가?
- 필수 작업이 마감을 넘으면 성공 대신 보류 또는 실패로 분류되는가?
- 취소 요청 후 실제 종료와 정리 완료를 확인하는가?
- 늦게 도착한 결과가 이미 확정한 답변을 덮어쓰지 않는가?
빠르다는 평가에 누락률도 포함
어려운 작업을 버린 정책이 유리해지지 않도록 지연 시간·필수 항목 충족·누락·오류·소비 자원을 함께 기록합니다. 동시 실행 수를 바꿀 때도 입력과 판정 기준은 고정합니다.
8. Q&A와 정리
Q1. TaskGroup이면 한 작업의 실패를 자동으로 격리하나요?
아닙니다. Python 3.11의 TaskGroup은 작업 하나가 취소 예외 외의 예외로 실패하면 나머지 작업을 취소하므로, 개별 실패를 보존하는 요구와 맞는지 먼저 판단해야 합니다.
Q2. gather의 return_exceptions=True는 실패를 성공으로 바꾸나요?
예외를 결과 목록에 포함해 반환하도록 바꾸는 옵션입니다. 호출자는 각 항목이 정상 값인지 예외인지 별도로 분류해야 합니다.
Q3. CPU 연산도 asyncio만 쓰면 빨라지나요?
같은 이벤트 루프를 오래 점유하는 동기 CPU 작업은 다른 코루틴 진행을 막을 수 있습니다. 이번 실험은 대기를 겹치는 예이며, CPU 병렬 처리를 검증한 벤치마크가 아닙니다.
Q4. 서로 다른 서버에 절대 마감을 그대로 전달해도 되나요?
이벤트 루프의 단조 시계 값은 다른 프로세스나 서버에서 같은 기준이라고 가정하지 않는 편이 안전합니다. 분산 실행에서는 남은 예산을 전달할지, 동기화된 시각을 사용할지와 전송 지연 처리 방식을 정해야 합니다.
Q5. 작업자가 중간 산출물을 직접 저장해도 되나요?
가능하며, Anthropic 설계 글도 산출물을 저장하고 조정자에게 가벼운 참조를 넘기는 방식을 설명합니다. 실무에서는 작업별 경로와 버전을 분리하고, 미완성 파일이 최종 결과로 읽히지 않도록 게시 시점을 구분하는 것이 좋습니다.
정책을 적용하기 전에는 느린 작업 하나를 의도적으로 넣고, 사용자에게 어떤 상태가 전달되는지부터 확인해 보십시오.
9. 참고 자료
- Anthropic — How we built our multi-agent research system (2025년 6월 13일, 조정·대기 병목·산출물 설계)
- Anthropic — Building effective agents (2024년 12월 19일, 병렬화와 orchestrator-workers 패턴)
- Python 3.11 — Coroutines and Tasks (wait·wait_for·gather·TaskGroup·취소 의미)
'AI Agent' 카테고리의 다른 글
| AI 에이전트 컨텍스트 압축, 무엇을 남겨야 할까? (0) | 2026.09.20 |
|---|---|
| AI 에이전트 재시도 중복 실행 방지: 멱등성 설계 (0) | 2026.09.18 |
| AI 코딩 에이전트 검증 루프: 테스트 통과와 완료의 차이 (0) | 2026.09.17 |
| Claude Smart reports 비용·권한·분석 범위 정리 (1) | 2026.09.14 |
| MCP Elicitation 작동 원리: 입력 요청과 URL 승인 차이 (0) | 2026.09.13 |