A2A 태스크 라이프사이클의 이해 (그리고 모든 클라이언트를 멈추게 하는 버그)
요약
A2A(Agent-to-Agent) 프로토콜의 태스크 중심적 라이프사이클과 상태 머신 모델링을 설명합니다. 에이전트 작업의 비동기적 특성을 반영하여 제출부터 종료까지의 6가지 상태와 필수 메서드를 정의합니다.
핵심 포인트
- A2A는 요청-응답 방식이 아닌 상태를 가진 태스크(Task) 단위로 동작함
- 모든 태스크는 반드시 종료 상태(completed, failed, canceled)에 도달해야 함
- input-required 상태를 통해 작업 중 클라이언트의 추가 입력을 지원함
- 태스크 중심 모델링을 통해 긴 실행 시간이 필요한 에이전트 작업을 효율적으로 관리함
A2A는 태스크 중심적(task-centric)입니다. 클라이언트가 작업을 전송하면, 해당 작업은 제출부터 완료까지 관찰 가능한 라이프사이클을 가진 하나의 태스크(Task)가 됩니다 — 만약 당신의 에이전트가 이를 정직하게 보고한다면 말이죠.
태스크(Task)는 A2A에서 상태를 가지는(stateful) 중심 작업 단위입니다. 클라이언트가 첫 번째 메시지(Message)를 보낼 때 생성되며, 종료 상태(terminal state)에 도달할 때까지 정의된 상태들을 거치며 이동합니다.
상태는 다음과 같습니다: submitted (제출됨), working (작업 중), input-required (입력 필요), completed (완료됨), failed (실패함), 그리고 canceled (취소됨). 마지막 세 가지는 종료 상태입니다.
가장 중요한 규칙: 모든 태스크는 반드시 종료 상태에 도달해야 합니다. 종료 상태를 듣지 못한 클라이언트는 영원히 기다리게 되며 — 이것이 초기 A2A 구현에서 가장 흔하게 발생하는 단일 버그입니다.
A2A가 호출(call) 대신 태스크를 모델링하는 이유
REST에서의 본능은 각 호출을 독립적인 것으로 취급하는 것입니다: 요청을 보내고, 응답을 받고, 끝냅니다. 하지만 에이전트의 작업은 그렇게 동작하는 경우가 드뭅니다.
보고(report)에는 몇 분이 걸릴 수 있습니다. 데이터 파이프라인(data pipeline)은 한 시간이 걸릴 수도 있습니다. 디자인 태스크는 중간에 명확한 질문이 필요할 수도 있습니다. 이 모든 것을 단일 요청-응답(request-response) 호출로 모델링한다는 것은 모든 호출이 즉시 반환된다고 가정하는 것을 의미하며, 그렇지 않을 경우 임시방편(workarounds)을 덧붙이게 됩니다.
A2A는 대신 이를 정직하게 모델링합니다: 작업은 명시적인 상태 머신(state machine)을 가진 태스크(Task)가 되며, 이를 통해 양측은 상황이 어디에 와 있는지 항상 합의할 수 있습니다.
6가지 상태
- submitted — 태스크가 수신되었으나 아직 작업이 시작되지 않은 상태.
- working — 원격 에이전트가 태스크를 활발히 처리 중인 상태.
- input-required — 에이전트가 더 많은 정보가 필요하며, 계속 진행하기 전에 클라이언트의 응답을 기다리는 상태.
- completed — 태스크가 성공적으로 종료됨; 아티팩트(Artifacts)를 사용할 수 있음.
- failed — 태스크가 오류와 함께 종료됨.
- canceled — 요청에 의해 태스크가 중단됨.
마지막 세 가지는 종료 상태입니다: 태스크가 이 중 하나에 도달하면 종료됩니다.
필요한 4가지 메서드
클라이언트는 메시지(Message)를 보냄으로써 태스크(Task)를 시작합니다; 응답에는 식별자(identifier)와 현재 상태를 포함한 태스크가 담겨 있습니다. 그 이후 단계는 다음과 같습니다:
message/send # 메시지 전송; 태스크(task) 생성 또는 계속
tasks/get # 태스크의 현재 상태 및 아티팩트(artifacts) 조회
...
이것이 표면적인 전부입니다. 프로토콜의 강력한 기능은 메서드의 개수가 아니라, 이 네 가지 메서드가 작동하는 대상인 상태를 가지며 관찰 가능한(observable) 태스크(Task)에서 나옵니다.
input-required는 에러가 아니라 기능입니다
진정한 위임(delegation)은 단 한 번의 실행으로 끝나는 경우가 드뭅니다. input-required 상태를 통해 원격 에이전트(remote agent)는 태스크 진행 도중에 일시 중지하고 명확한 질문을 던진 뒤, 클라이언트(client)가 답변하면 다시 재개할 수 있습니다.
하나의 태스크를 끝까지 따라가며 살펴보겠습니다. 클라이언트가 에이전트에게 보고서 생성을 요청하면, 태스크는 submitted 상태로 생성됩니다. 에이전트가 작업을 시작하면 상태는 working으로 이동합니다. 작업 도중 날짜 범위가 필요하다는 것을 깨달은 에이전트는 태스크를 input-required 상태로 변경하고 질문을 던집니다. 클라이언트가 해당 범위를 제공하면 태스크는 다시 working 상태로 돌아갑니다. 보고서가 준비되면 에이전트는 이를 아티팩트(Artifact)로 첨부하고 태스크를 completed 상태로 변경합니다.
event: task state -> working
event: message -> "어느 시간 범위를 포함해야 할까요?"
event: task state -> input-required
...
전 과정 동안 동일한 태스크가 유지됩니다. 새로운 태스크를 만들지도, 컨텍스트(context)를 잃어버리지도 않습니다. 이를 통해 경직된 호출이 진정한 협업으로 변모합니다.
모든 클라이언트를 멈추게 하는 버그
문제는 명확합니다. 원격 에이전트가 작업을 마쳤음에도 종료 상태(terminal state)를 보고하지 않아, 클라이언트가 무한정 대기하게 되는 것입니다.
작업은 완료되었습니다. 로그도 정상입니다. 에이전트는 유휴(idle) 상태이며 만족하고 있습니다. 하지만 호출자(caller)는 여전히 그 자리에 앉아 있습니다. 프로토콜 관점에서는 해당 태스크(Task)가 결코 끝나지 않았기 때문입니다.
항상 종료 이벤트(terminal event)를 발생시키십시오. 에러 경로(error paths)를 포함하여, 특히 에러 경로에서는 반드시 그래야 합니다. 만약 당신의 클라이언트가 멈춘다면, 이것이 가장 먼저 확인해야 할 사항이며 대개 이것이 정답일 것입니다.
모든 태스크는 반드시 종료 상태에 도달해야 합니다. 대부분의 태스크가 아니라, 모든 경로에서의 모든 태스크가 그래야 합니다.
태스크를 추적하는 세 가지 방법
기저에 있는 태스크(Task)는 세 가지 방식 모두 동일합니다. 오직 업데이트를 받는 방식만 달라질 뿐입니다.
| 사용 시점 (When to use) | 방법 (How) | |
|---|---|---|
| 폴링 (Polling) | 실시간 진행 상황이 중요하지 않은 단순하고 짧은 작업 | 태스크 ID로 tasks/get 호출 |
| ... |
가장 오래 걸리는 작업의 경우, 스트림 (stream)을 몇 시간 동안 열어두는 것보다 푸시 알림 (push notifications)을 선호하십시오. 이를 통해 취약한 장기 연결 (long-lived connection)을 피할 수 있으며 태스크 모델도 변경되지 않습니다.
명시적인 라이프사이클 (lifecycle)이 가치 있는 이유
태스크를 상태가 있고 관찰 가능한 (stateful, observable) 객체로 모델링하는 것이야말로 A2A를 단순한 데모용이 아닌 실제 업무에 적합하게 만드는 핵심입니다.
- 진행 중인 모든 태스크를 보여주는 **대시보드 (dashboards)**를 구축할 수 있습니다.
failed상태에 반응하는 **재시도 로직 (retry logic)**을 작성할 수 있습니다.- 다음 단계를 트리거하기 전
completed상태를 기다리는 **오케스트레이션 (orchestration)**을 구축할 수 있습니다. - 추측하는 대신, 태스크가 접촉한 모든 에이전트(agent)를 가로질러 **실패를 추적 (trace a failure)**할 수 있습니다.
라이프사이클은 관료주의가 아닙니다. 이는 멀티 에이전트 시스템 (multi-agent systems)이 단순히 실행되는 것을 넘어 운영될 수 있게 해주는 관찰 가능성 계층 (observability layer)입니다.
컨텍스트 (Contexts)는 관련 태스크를 그룹화합니다
실제 업무는 단일 태스크인 경우가 드뭅니다. A2A는 관련 태스크가 컨텍스트를 공유할 수 있게 하여, 에이전트가 후속 요청을 이전 요청들과 연결하고 각 요청을 고립시켜 처리하는 대신 전체 스레드 (thread)에 대해 추론할 수 있도록 합니다.
이것이 일회성 위임의 연속을 일관성 있고 지속적인 협업으로 바꾸는 요소입니다. 이는 에이전트가 지금까지의 대화를 기억하는 것과 같습니다.
자주 묻는 질문 (Frequently asked questions)
A2A 태스크 상태 (task states)는 무엇인가요?
submitted, working, input-required, completed, failed, 그리고 canceled입니다. 마지막 세 가지는 종료 상태 (terminal)입니다. 태스크가 이 중 하나에 도달하면 종료됩니다.
A2A에서 input-required는 무엇을 의미하나요?
원격 에이전트가 더 많은 정보가 필요하여 일시 중지하고 클라이언트(client)를 기다리고 있음을 의미합니다. 이는 오류가 아니라 기능입니다. 에이전트가 태스크 도중에 명확한 질문을 던지고, 클라이언트가 답변하면 동일한 태스크에서 재개할 수 있게 해줍니다.
왜 내 A2A 클라이언트가 영원히 멈춰(hang) 있나요?
거의 항상 원격 에이전트(remote agent)가 작업을 마쳤음에도 종료 상태(terminal state)를 발행(emit)하지 않았기 때문입니다. 프로토콜에는 태스크가 종료되었음을 알 수 있는 방법이 없으므로, 클라이언트는 계속 기다리게 됩니다. 에러를 포함한 모든 경로에서 반드시 완료(completed), 실패(failed), 또는 취소(canceled) 상태를 발행하세요.
A2A 태스크를 폴링(poll)해야 하나요, 아니면 스트리밍(stream)해야 하나요?
단순하고 짧은 작업에는 tasks/get을 사용하여 폴링(poll)하세요. 누군가가 기다리고 있는 대화형 작업에는 message/stream을 사용하여 스트리밍(stream)하세요. 연결을 몇 시간 동안 열어두는 것이 낭비인, 사람이 지켜보지 않는 장기 작업에는 푸시 알림(push notifications)을 사용하세요.
A2A 태스크가 HTTP 요청과 같나요?
아니요. HTTP 요청은 상태가 없으며(stateless) 한 번 반환됩니다. A2A 태스크는 상태가 유지되며(stateful), 오랫동안 실행될 수 있고, 입력을 위해 일시 중지될 수 있으며, 종료 상태에 도달하기 전까지 많은 업데이트를 스트리밍(stream)할 수 있습니다.
이 시리즈의 다른 글들
- A2A vs MCP — A2A vs MCP: 모든 진지한 AI 에이전트 시스템 뒤에 있는 두 가지 프로토콜
- What Is A2A? — Agent2Agent 프로토콜이란 무엇인가? A2A에 대한 완전한 입문서
- The Agent Card — Agent Card: AI 에이전트들이 서로를 발견하는 방법
더 깊이 알고 싶으신가요?
이 주제에 대해 두 권의 가이드를 작성했습니다.
A2A Quick-Start — 무료, 6페이지. 15분 만에 배우는 Agent2Agent 프로토콜: 정의, 5가지 구성 요소, 태스크 라이프사이클(task lifecycle), 그리고 MCP가 어디에 위치하는지 알아봅니다.
A2A: The Complete Guide — 42페이지. 15개 장과 5개의 부록으로 구성되었습니다. 발견(discovery), 보안(security), SDK를 이용한 첫 번째 에이전트 구축, 오케스트레이션 패턴(orchestration patterns), 확장(extensions) 및 AP2, 프로덕션 및 스케일링(scaling)을 다룹니다. 또한 두 에이전트가 대화하는 전체 실습 예제와 30일 도입 경로가 포함되어 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기