본문 바로가기
AI/모델,학습

A2A 프로토콜 개발 가이드(TypeScript)

by 주호파파 2025. 5. 14.
728x90
반응형

A2A 프로토콜 개발 가이드

개요

A2A(Agent-to-Agent) 프로토콜은 에이전트 상호 작용을 위해 설계된 JSON-RPC 기반 통신 프로토콜입니다. 이 가이드는 A2A 프로토콜 사양을 준수하는 서버 및 클라이언트 구성 요소를 모두 개발하기 위한 포괄적인 지침을 제공합니다.

목차

  1. 프로토콜 기본 사항
  2. 서버 구현
  3. 클라이언트 구현
  4. 코더 데모 실행

프로토콜 기본 사항

A2A 프로토콜은 JSON-RPC 2.0을 기반으로 하며 에이전트 통신을 위한 일련의 메소드를 정의합니다. 주요 구성 요소는 다음과 같습니다.

메시지 구조

모든 A2A 메시지는 다음과 같은 기본 구조를 가진 JSON-RPC 2.0 형식을 따릅니다.

interface JSONRPCMessage {
  jsonrpc?: "2.0";
  id?: number | string | null;
  method?: string;
  params?: unknown;
  result?: unknown;
  error?: JSONRPCError;
}

프로토콜 흐름

다음 시퀀스 다이어그램은 A2A 프로토콜의 기본 상호 작용 흐름을 보여 줍니다.

핵심 방법

이 프로토콜은 다음과 같은 몇 가지 핵심 방법을 지원합니다.

  • tasks/send: 에이전트에게 작업 메시지를 보냅니다.
  • tasks/get: 작업 상태 검색
  • tasks/cancel: 실행 중인 작업을 취소합니다.
  • tasks/pushNotification/set: 작업에 대한 푸시 알림 구성
  • tasks/pushNotification/get: 푸시 알림 구성 가져오기
  • tasks/resubscribe: 작업 업데이트 재구독
  • tasks/sendSubscribe: 작업 메시지를 보내고 업데이트를 구독합니다.

작업 상태

작업은 다음 상태 중 하나일 수 있습니다.

  • submitted
  • working
  • input-required
  • completed
  • canceled
  • failed
  • unknown

서버 구현

핵심 구성 요소

서버 구현은 다음과 같은 몇 가지 주요 구성 요소로 구성됩니다.

  1. 서버(server.ts): HTTP 요청을 처리하는 메인 서버 구현
  2. 처리기(handler.ts): A2A 프로토콜 메시지를 처리하기 위한 요청 처리기입니다
  3. Store (store.ts): 작업 저장 및 관리
  4. Utils (utils.ts) : 유틸리티 함수
  5. 오류 처리(error.ts): 오류 정의 및 처리

기본 사용법(개념적)

import {
  A2AServer,
  InMemoryTaskStore,
  TaskContext,
  TaskYieldUpdate,
} from "./index"; // Assuming imports from the server package

// 1. Define your agent's logic as a TaskHandler
async function* myAgentLogic(
  context: TaskContext
): AsyncGenerator<TaskYieldUpdate> {
  console.log(`Handling task: ${context.task.id}`);
  yield {
    state: "working",
    message: { role: "agent", parts: [{ text: "Processing..." }] },
  };

  // Simulate work...
  await new Promise((resolve) => setTimeout(resolve, 1000));

  if (context.isCancelled()) {
    console.log("Task cancelled!");
    yield { state: "canceled" };
    return;
  }

  // Yield an artifact
  yield {
    name: "result.txt",
    mimeType: "text/plain",
    parts: [{ text: `Task ${context.task.id} completed.` }],
  };

  // Yield final status
  yield {
    state: "completed",
    message: { role: "agent", parts: [{ text: "Done!" }] },
  };
}

// 2. Create and start the server
const store = new InMemoryTaskStore(); // Or new FileStore()
const server = new A2AServer(myAgentLogic, { taskStore: store });

server.start(); // Starts listening on default port 41241

console.log("A2A Server started.");

클라이언트 구현

주요 특징들:

  • JSON-RPC 통신: JSON-RPC 2.0 사양에 따라 요청 전송 및 응답 수신(표준 및 서버 전송 이벤트를 통한 스트리밍 모두)을 처리합니다.
  • A2A 방법: , , , , , 및 와 같은 표준 A2A 메서드를 구현합니다.'sendTask' 'sendTaskSubscribe' 'getTask' 'cancelTask' 'setTaskPushNotification' 'getTaskPushNotification' 'resubscribeTask'
  • 오류 처리: 네트워크 문제 및 JSON-RPC 오류에 대한 기본 오류 처리를 제공합니다.
  • 스트리밍 지원: 실시간 작업 업데이트를 위한 SSE(Server-Sent Events)를 관리합니다(, ).'sendTaskSubscribe' 'resubscribeTask'
  • 확장성: 다양한 환경(예: Node.js)에 대한 사용자 지정 구현을 제공할 수 있습니다.fetch

기본 사용법

import { A2AClient, Task, TaskQueryParams, TaskSendParams } from "./client"; // Import necessary types
import { v4 as uuidv4 } from "uuid"; // Example for generating task IDs

const client = new A2AClient("http://localhost:41241"); // Replace with your server URL

async function run() {
  try {
    // Send a simple task (pass only params)
    const taskId = uuidv4();
    const sendParams: TaskSendParams = {
      id: taskId,
      message: { role: "user", parts: [{ text: "Hello, agent!" }] },
    };
    // Method now returns Task | null directly
    const taskResult: Task | null = await client.sendTask(sendParams);
    console.log("Send Task Result:", taskResult);

    // Get task status (pass only params)
    const getParams: TaskQueryParams = { id: taskId };
    // Method now returns Task | null directly
    const getTaskResult: Task | null = await client.getTask(getParams);
    console.log("Get Task Result:", getTaskResult);
  } catch (error) {
    console.error("A2A Client Error:", error);
  }
}

run();

스트리밍 사용량

 

import {
  A2AClient,
  TaskStatusUpdateEvent,
  TaskArtifactUpdateEvent,
  TaskSendParams, // Use params type directly
} from "./client"; // Adjust path if necessary
import { v4 as uuidv4 } from "uuid";

const client = new A2AClient("http://localhost:41241");

async function streamTask() {
  const streamingTaskId = uuidv4();
  try {
    console.log(`\n--- Starting streaming task ${streamingTaskId} ---`);
    // Construct just the params
    const streamParams: TaskSendParams = {
      id: streamingTaskId,
      message: { role: "user", parts: [{ text: "Stream me some updates!" }] },
    };
    // Pass only params to the client method
    const stream = client.sendTaskSubscribe(streamParams);

    // Stream now yields the event payloads directly
    for await (const event of stream) {
      // Type guard to differentiate events based on structure
      if ("status" in event) {
        // It's a TaskStatusUpdateEvent
        const statusEvent = event as TaskStatusUpdateEvent; // Cast for clarity
        console.log(
          `[${streamingTaskId}] Status Update: ${statusEvent.status.state} - ${
            statusEvent.status.message?.parts[0]?.text ?? "No message"
          }`
        );
        if (statusEvent.final) {
          console.log(`[${streamingTaskId}] Stream marked as final.`);
          break; // Exit loop when server signals completion
        }
      } else if ("artifact" in event) {
        // It's a TaskArtifactUpdateEvent
        const artifactEvent = event as TaskArtifactUpdateEvent; // Cast for clarity
        console.log(
          `[${streamingTaskId}] Artifact Update: ${
            artifactEvent.artifact.name ??
            `Index ${artifactEvent.artifact.index}`
          } - Part Count: ${artifactEvent.artifact.parts.length}`
        );
        // Process artifact content (e.g., artifactEvent.artifact.parts[0].text)
      } else {
        console.warn("Received unknown event structure:", event);
      }
    }
    console.log(`--- Streaming task ${streamingTaskId} finished ---`);
  } catch (error) {
    console.error(`Error during streaming task ${streamingTaskId}:`, error);
  }
}

streamTask();

코더 데모 실행

코더 데모는 코드 관련 작업을 처리할 수 있는 A2A 에이전트의 구현 예입니다.

설치

  1. 종속성 설치:
git clone https://github.com/sing1ee/a2a-agent-coder.git
#or
git clone git@github.com:sing1ee/a2a-agent-coder.git

bun install
  1. 필요한 환경 변수가 있는지 확인합니다.
# set .env first
export $(cat .env | xargs)

데모 실행

  1. a2a 서버를 시작합니다(Node.js 환경 필요).
bun run agents:coder
  1. a2a 클라이언트를 시작합니다.
bun run a2a:cli

# result
$ bun x tsx src/cli.ts
A2A Terminal Client
Agent URL: http://localhost:41241
Attempting to fetch agent card from: http://localhost:41241/.well-known/agent.json
✓ Agent Card Found:
  Name:        Coder Agent
  Description: An agent that generates code based on natural language instructions and streams file outputs.
  Version:     0.0.1
Starting Task ID: a1a608b3-3015-4404-a83f-6ccc05083761
Enter messages, or use '/new' to start a new task.
Coder Agent > You: implement binary search
Sending...

Coder Agent [4:28:00 PM]: ⏳ Status: working
  Part 1: 📝 Text: Generating code...

Coder Agent [4:28:02 PM]: ⏳ Status: working
  Part 1: 📄 File: Name: src/algorithms/binary_search.py, Source: """
Implementation of the binary search algorithm in Python.
"""

def binary_search(arr, target):
    """
    Performs a binary search on a sorted array to find the index of a target value.

    Args:
        arr (list): A sorted list of elements.
        target: The value to search for in the array.

    Returns:
        int: The index of the target value if found, otherwise -1.
    """
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) // 2  # Integer division to find the middle index

        if arr[mid] == target:
            return mid  # Target found at index mid
        elif arr[mid] < target:
            low = mid + 1  # Target is in the right half
        else:
            high = mid - 1  # Target is in the left half

    return -1  # Target not found in the array


Coder Agent [4:28:02 PM]: ✅ Status: completed
SSE stream finished for method tasks/sendSubscribe.
--- End of response for this input ---
Coder Agent > You: 
Exiting terminal client. Goodbye!

오류 처리

A2A 프로토콜은 다음과 같은 몇 가지 표준 오류 코드를 정의합니다.

  • -32700: 구문 분석 오류
  • -32600: 잘못된 요청
  • -32601: 메서드를 찾을 수 없음
  • -32602: 잘못된 params
  • -32603: 내부 오류
  • -32000: 작업을 찾을 수 없음
  • -32001: 작업을 취소할 수 없습니다.
  • -32002: 푸시 알림이 지원되지 않습니다.
  • -32003: 지원되지 않는 작업

권장사항

  1. 오류 처리: 모든 A2A 프로토콜 메서드에 대해 항상 적절한 오류 처리를 구현합니다.
  2. 인증: 보안 통신을 위한 적절한 인증 메커니즘 구현
  3. 작업 관리: 적절한 작업 상태 관리 및 정리 유지
  4. 푸시 알림: 지원되는 경우 실시간 업데이트에 대한 푸시 알림을 구현합니다.
  5. 로깅: 디버깅 및 모니터링을 위한 포괄적인 로깅을 구현합니다

 

728x90
반응형