태스크의 수명주기

태스크의 수명주기 (Life of a Task)

Agent2Agent(A2A) 프로토콜에서 상호작용은 매우 다양해요. 단순하고 무상태한 교환부터 복잡하고 오래 실행되는 프로세스까지 있죠. 에이전트가 클라이언트로부터 메시지를 받으면 두 가지 방식 중 하나로 응답할 수 있어요.

  • 무상태 Message로 응답: 즉각적이고 자체 완결적인 상호작용에 사용해요. 관리할 추가 상태 없이 끝나요.
  • 상태를 가진 Task 시작: 에이전트가 정의된 수명주기를 거쳐 Task를 실행해요. 진행 상황을 보고하고 필요에 따라 입력을 요청하며, 중단 상태(예: input-required, auth-required)나 종료 상태(예: completed, canceled, rejected, failed)에 도달할 때까지 진행해요.

출처: 문서

본문

관련 상호작용 묶기

contextId는 핵심 식별자예요. 여러 Task 객체와 독립적인 Message 객체들을 묶어서, 일련의 상호작용에 걸쳐 연속성을 제공해요.

  • 클라이언트가 처음으로 메시지를 보내면 에이전트는 새 contextId로 응답해요. 태스크가 시작되면 taskId도 갖게 되죠.
  • 이전 상호작용을 계속하려면 클라이언트는 이후 메시지에 같은 contextId를 사용해 보내요.
  • 클라이언트는 선택적으로 이후 메시지에 taskId를 붙여, 그 특정 태스크를 계속한다는 것을 나타낼 수 있어요.

contextId는 공통 목표를 향한 협업, 또는 동시에 실행될 수 있는 여러 태스크에 걸친 공유 세션을 가능하게 해요. 내부적으로 A2A 에이전트(특히 LLM을 쓰는 에이전트)는 contextId를 사용해 자기 대화 상태나 LLM 컨텍스트를 관리해요.

에이전트 응답: 메시지 또는 태스크

Message로 응답할지 Task로 응답할지는 상호작용의 성격과 에이전트의 역량에 따라 달라져요.

  • 사소한 상호작용에는 메시지(Messages for Trivial Interactions): 트랜잭션형 상호작용에는 Message 객체를 사용해요. 장기 실행 처리나 복잡한 상태가 필요 없어요. 에이전트는 Task 객체를 시작하기 전에 태스크 범위에 합의하기 위해 메시지를 쓸 수 있어요.
  • 상태를 가진 상호작용에는 태스크(Tasks for Stateful Interactions): 에이전트는 들어온 메시지를 지원되는 역량에 매핑해요. 그 역량이 더 긴 기간에 걸쳐 상당하고 추적 가능한 작업이 필요할 때, 에이전트는 Task 객체로 응답해요.

개념적으로 에이전트는 서로 다른 복잡성 수준에서 동작해요.

  • 메시지 전용 에이전트(Message-only Agents): 항상 Message 객체로 응답해요. 대개 복잡한 상태나 장기 실행을 관리하지 않고, contextId를 사용해 메시지를 묶어요. 이 에이전트들은 LLM 호출과 단순 도구를 직접 감쌀 수 있어요.
  • 태스크 생성 에이전트(Task-generating Agents): 항상 Task 객체로 응답해요. 단순한 응답조차 완료된 태스크로 모델링하죠. 태스크가 생성되면 에이전트는 Task 객체만 반환해요. 태스크가 완료되면 더 이상 메시지를 보낼 수 없어요. 이 방식은 Task 대 Message 결정을 피하지만, 단순한 상호작용에도 완료된 태스크 객체를 만들게 돼요.
  • 하이브리드 에이전트(Hybrid Agents): Message와 Task 객체를 모두 생성해요. 이 에이전트들은 메시지를 사용해 에이전트 역량과 작업 범위를 협상하고, 그다음 Task 객체를 보내 실행을 추적하고 input-required나 오류 처리 같은 상태를 관리해요. 다시 말해 Message 객체는 무엇을 해야 하는지 확립하는 가벼운 왕복을 담당하고, 실행하고 관찰할 확정된 작업이 생기면 Task가 만들어져요. 태스크 생성 에이전트와 마찬가지로 태스크가 생성되면 이후 메시지에 대해서는 에이전트가 Task 객체만 반환하고, 태스크가 완료되면 더 이상 메시지를 보낼 수 없어요.

메시지 vs 태스크를 언제 써야 하는지에 대한 더 깊은 논의는 A2A protocol: Demystifying Tasks vs Messages를 참고하세요.

태스크 개선(Task Refinements)

클라이언트는 종종 태스크 결과를 바탕으로 새 요청을 보내거나 이전 태스크의 출력을 개선해야 해요. 이는 원래 태스크와 같은 contextId를 사용해 또 다른 상호작용을 시작하는 것으로 모델링돼요. 클라이언트는 Message 객체의 referenceTaskIds에 원래 태스크 참조를 제공해 에이전트에게 힌트를 더 주고, 에이전트는 새 Task나 Message로 응답해요.

태스크 불변성(Task Immutability)

태스크가 종료 상태(completed, canceled, rejected, failed)에 도달하면 다시 시작할 수 없어요. 그 태스크와 관련된 이후 상호작용(예: 개선)은 같은 contextId 안에서 새 태스크를 시작해야 해요. 이 원칙은 여러 이점을 제공해요.

  • 태스크 불변성(Task Immutability): 클라이언트는 태스크와 그 상태, 아티팩트, 메시지를 안정적으로 참조해요. 이는 입력에서 출력으로의 깔끔한 매핑을 제공해 오케스트레이션과 추적성(traceability)에 도움이 돼요.
  • 명확한 작업 단위(Clear Unit of Work): 모든 새 요청, 개선, 후속 작업은 별개의 태스크가 돼요. 이는 부기(bookkeeping)를 단순화하고 에이전트 작업의 세밀한 추적을 가능하게 하며 각 아티팩트를 특정 작업 단위로 추적하게 해요.
  • 구현 용이성(Easier Implementation): 이는 에이전트 개발자가 새 태스크를 만들지 기존 태스크를 재시작할지에 대한 모호함을 없애줘요.

병렬 후속 작업(Parallel Follow-ups)

A2A는 같은 contextId 안에서 보내지는 각 후속 메시지에 대해 별개의 병렬 태스크를 만들 수 있게 해 병렬 작업을 지원해요. 이렇게 하면 클라이언트가 개별 태스크를 추적하고, 선행 태스크가 완료되는 즉시 새 종속 태스크를 만들 수 있어요. 예를 들어:

  • 태스크 1: 헬싱키행 항공편 예약.
  • 태스크 2: 태스크 1을 기반으로 호텔 예약.
  • 태스크 3: 태스크 1을 기반으로 스노모빌 액티비티 예약.
  • 태스크 4: 태스크 2를 기반으로 호텔 예약에 스파 예약 추가.

이전 아티팩트 참조(Referencing Previous Artifacts)

서빙 에이전트는 참조된 태스크나 contextId에서 관련 아티팩트를 추론해요. 도메인 전문가로서 모호함을 해소하거나 누락된 정보를 찾아내는 데 가장 적합한 위치에 있어요. 무언가 모호하면 에이전트는 input-required 상태를 반환해 클라이언트에게 명확화를 요청해요. 그러면 클라이언트는 응답에서 아티팩트를 명명하고, Part 메타데이터에 아티팩트 참조(artifactId, taskId)를 추가할 수 있어요.

아티팩트 변경 추적(Tracking Artifact Mutation)

후속 또는 개선 태스크는 종종 이전 아티팩트를 기반으로 새 아티팩트를 만들어요. 이후 상호작용이 가장 최신 버전만 사용하도록 이런 변경을 추적하세요. 각 새 아티팩트가 그 이전 아티팩트에 연결되는 버전 이력으로 생각하면 돼요. 클라이언트가 이 아티팩트 연결을 관리하기에 가장 적합해요. 클라이언트는 무엇이 허용 가능한 결과인지 결정하고 새 버전을 수락하거나 거부할 수 있어요. 따라서 서빙 에이전트가 아티팩트 변경을 추적할 필요는 없고, 이 연결은 A2A 프로토콜 스펙의 일부가 아니에요. 클라이언트는 자기 쪽에 버전 이력을 유지하고 사용자에게 최신 허용 버전을 보여줘야 해요. 클라이언트 측 추적을 돕기 위해, 서빙 에이전트는 기존 아티팩트의 개선 버전을 생성할 때 일관된 artifact-name을 재사용해야 해요. 후속 또는 개선 태스크에서 클라이언트는 개선하려는 정확한 아티팩트(가급적 자기 관점에서의 "최신" 버전)를 명명해야 해요. 클라이언트가 아티팩트 참조를 제공하지 않으면 서빙 에이전트는 다음을 할 수 있어요.

  • 현재 contextId를 기반으로 의도된 아티팩트를 추론하려 시도.
  • 모호하거나 컨텍스트가 부족하면 클라이언트에게 명확화를 요청하기 위해 input-required 태스크 상태로 응답.

예시 후속 시나리오

다음 예시는 후속 작업이 있는 전형적인 태스크 흐름을 보여줘요.

클라이언트가 에이전트에 메시지를 보내요:

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "SendMessage",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "text": "Generate an image of a sailboat on the ocean."
        }
      ],
      "messageId": "msg-user-001"
    }
  }
}

에이전트가 보트 이미지로 응답해요(완료된 태스크):

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "task": {
      "id": "task-boat-gen-123",
      "contextId": "ctx-conversation-abc",
      "status": {
        "state": "TASK_STATE_COMPLETED"
      },
      "artifacts": [
        {
          "artifactId": "artifact-boat-v1-xyz",
          "name": "sailboat_image.png",
          "description": "A generated image of a sailboat on the ocean.",
          "parts": [
            {
              "filename": "sailboat_image.png",
              "mediaType": "image/png",
              "raw": "base64_encoded_png_data_of_a_sailboat"
            }
          ]
        }
      ]
    }
  }
}

클라이언트가 보트를 빨간색으로 칠해 달라고 요청해요. 이 개선 요청은 이전 taskId를 참조하고 같은 contextId를 사용해요.

{
  "jsonrpc": "2.0",
  "id": "req-002",
  "method": "SendMessage",
  "params": {
    "message": {
      "role": "user",
      "messageId": "msg-user-002",
      "contextId": "ctx-conversation-abc",
      "referenceTaskIds": [
        "task-boat-gen-123"
      ],
      "parts": [
        {
          "text": "Please modify the sailboat to be red."
        }
      ]
    }
  }
}

에이전트가 새 이미지 아티팩트로 응답해요(새 태스크, 같은 컨텍스트, 같은 아티팩트 이름): 에이전트는 같은 contextId 안에서 새 태스크를 만들어요. 새 보트 이미지 아티팩트는 이름은 같지만 새 artifactId를 가져요.

{
  "jsonrpc": "2.0",
  "id": "req-002",
  "result": {
    "task": {
      "id": "task-boat-color-456",
      "contextId": "ctx-conversation-abc",
      "status": {
        "state": "TASK_STATE_COMPLETED"
      },
      "artifacts": [
        {
          "artifactId": "artifact-boat-v2-red-pqr",
          "name": "sailboat_image.png",
          "description": "A generated image of a red sailboat on the ocean.",
          "parts": [
            {
              "filename": "sailboat_image.png",
              "mediaType": "image/png",
              "raw": "base64_encoded_png_data_of_a_RED_sailboat"
            }
          ]
        }
      ]
    }
  }
}

더 알아보기 (Learn more)