Skip to content

Exemplos de Integração A2A

Os exemplos abaixo seguem o schema real do a2a-sdk (kind como discriminador de parts, messageId obrigatório, resposta como objeto Task). Para uma referência viva e testada, veja examples/a2a_test_cli.py (login + agent card + envio) e examples/a2a_echo_agent.py + examples/a2a_outbound_roundtrip_test.py (servidor de teste A2A independente + script que valida o roundtrip completo) no repositório.

Autenticação nos exemplos

Os exemplos funcionam com os dois modos de autenticação (ver Para Parceiros) — só muda o dicionário de headers:

# Opção A — API client iFriend: os 3 headers em TODA requisição, sem chamar endpoint de token
AUTH_HEADERS = {
    "X-Client-Id": "seu_client_id",
    "X-Client-Secret": "seu_client_secret",
    "X-Integrator-Id": "seu_integrator_id",
}

# Opção B — JWT de conta de serviço (obtido via POST /authentication_token)
AUTH_HEADERS = {"Authorization": "Bearer seu_token_aqui"}

Python (requests)

import requests
import uuid

A2A_BASE_URL = "https://agents.theifriend.com/trip/a2a"
AUTH_HEADERS = {
    "X-Client-Id": "seu_client_id",
    "X-Client-Secret": "seu_client_secret",
    "X-Integrator-Id": "seu_integrator_id",
}  # ou {"Authorization": "Bearer <jwt>"}


def send_message(message: str, context_id: str | None = None):
    payload = {
        "jsonrpc": "2.0",
        "id": str(uuid.uuid4()),
        "method": "message/send",
        "params": {
            "message": {
                "role": "user",
                "messageId": str(uuid.uuid4()),
                "parts": [{"kind": "text", "text": message}],
                **({"contextId": context_id} if context_id else {}),
            }
        },
    }

    resp = requests.post(f"{A2A_BASE_URL}/", json=payload, headers=AUTH_HEADERS)
    resp.raise_for_status()
    return resp.json()["result"]


def extract_text(task: dict) -> str:
    """Task.status.message.parts (resposta síncrona) é onde o texto do
    agente normalmente aparece; artifacts é o fallback para resultados
    "finais" de tasks mais longas."""
    status_message = (task.get("status") or {}).get("message") or {}
    for part in status_message.get("parts", []):
        if part.get("kind") == "text":
            return part["text"]
    for artifact in task.get("artifacts") or []:
        for part in artifact.get("parts", []):
            if part.get("kind") == "text":
                return part["text"]
    return ""


task = send_message("Quero experiências em São Paulo")
print(extract_text(task))
print("task id:", task["id"], "| context id:", task.get("contextId"))

JavaScript/Node.js

const { v4: uuidv4 } = require('uuid');

const A2A_BASE_URL = 'https://agents.theifriend.com/trip/a2a';
const AUTH_HEADERS = {
  'X-Client-Id': 'seu_client_id',
  'X-Client-Secret': 'seu_client_secret',
  'X-Integrator-Id': 'seu_integrator_id',
}; // ou { Authorization: 'Bearer <jwt>' }

async function sendMessage(message, contextId) {
  const messagePayload = {
    role: 'user',
    messageId: uuidv4(),
    parts: [{ kind: 'text', text: message }],
  };
  if (contextId) messagePayload.contextId = contextId;

  const response = await fetch(`${A2A_BASE_URL}/`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...AUTH_HEADERS,
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: uuidv4(),
      method: 'message/send',
      params: { message: messagePayload },
    }),
  });
  const body = await response.json();
  return body.result; // objeto Task — ver status.message.parts / artifacts
}

const task = await sendMessage('Quero experiências em São Paulo');
console.log(task);

cURL

curl -X POST https://agents.theifriend.com/trip/a2a/ \
  -H "Content-Type: application/json" \
  -H "X-Client-Id: SEU_CLIENT_ID" \
  -H "X-Client-Secret: SEU_CLIENT_SECRET" \
  -H "X-Integrator-Id: SEU_INTEGRATOR_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "messageId": "msg-001",
        "parts": [{"kind": "text", "text": "Quero experiências em São Paulo"}]
      }
    }
  }'

Com JWT, troque os três headers X-* por -H "Authorization: Bearer SEU_TOKEN".

Consultando e cancelando uma task

Os exemplos abaixo usam JWT; com API client iFriend, use os três headers X-* no lugar do Authorization.

# tasks/get — note "id", não "taskId"
curl -X POST https://agents.theifriend.com/trip/a2a/ \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"id":"<task_id_da_resposta_anterior>"}}'

# tasks/cancel — cooperativo: nunca falha, mas não interrompe uma geração já em andamento
curl -X POST https://agents.theifriend.com/trip/a2a/ \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"id":"<task_id_da_resposta_anterior>"}}'

CLI de teste (examples/a2a_test_cli.py)

# API client iFriend
CLIENT_ID="<client_id>" CLIENT_SECRET="<client_secret>" INTEGRATOR_ID="<integrator_id>" \
A2A_URL="https://stage.agents.theifriend.com/trip/a2a" \
python examples/a2a_test_cli.py

# JWT de conta de serviço (o script faz o login em /authentication_token)
EMAIL="<email>" PASSWORD="<senha>" \
A2A_URL="https://stage.agents.theifriend.com/trip/a2a" \
python examples/a2a_test_cli.py

Os mesmos valores podem ir como --client-id/--client-secret/--integrator-id ou --email/--password.