Vai al contenuto principale
OpenData AI — progetto sperimentale

← Documentazione

A2A — Agent-to-Agent

Il backend OpenData AI è un server A2A: pubblica una AgentCard e accetta chiamate JSON-RPC da qualunque agente che parla il protocollo Agent-to-Agent. È il modo giusto per delegare task “cerca questi dataset” o “classifica questo dataset” da un orchestratore esterno.

Discovery — AgentCard

La AgentCard è esposta su /.well-known/agent-card.json (SDK 1.0) e su /.well-known/agent.json come alias legacy 0.3. Contiene metadata, skill esposte e supported versions.

curl -s https://api.opendata-ai.it/.well-known/agent-card.json | jq

{
  "name": "opendata-ai",
  "version": "1.0.0",
  "skills": [
    { "id": "search_open_data",   "name": "Cerca open data multi-fonte" },
    { "id": "find_geo_resources", "name": "Cerca risorse geografiche" },
    { "id": "classify_dataset",   "name": "Classifica un dataset" }
  ],
  "endpoints": { "jsonrpc": "https://api.opendata-ai.it/a2a/" }
}

Skill esposte

SkillCosa faInput principale
search_open_dataFan-out su CKAN + SDMX, sintesi narrativa + lista risorse.Una query in linguaggio naturale.
find_geo_resourcesStessa cosa ma bias geografico (Shapefile, GeoJSON, KML, WMS).Una query in linguaggio naturale.
classify_datasetClassifica un dataset rispetto a una tassonomia data. Cache Redis 24h + Postgres durable.source, dataset_id, taxonomy[].

SDK 1.0 — SendMessage (raccomandato)

PascalCase, role come enum protobuf:

curl -sX POST https://api.opendata-ai.it/a2a/ \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <clerk_jwt>' \
  -H 'A2A-Version: 1.0' \
  -d '{
    "jsonrpc":"2.0","id":"1","method":"SendMessage",
    "params":{"message":{
      "messageId":"m-1",
      "role":"ROLE_USER",
      "parts":[{"text":"qualità aria a Milano"}],
      "metadata":{"skill":"search_open_data"}
    }}
  }'

SDK 0.3 (compat) — message/send

slash-case, role lowercase, kind: text esplicito nei parts:

curl -sX POST https://api.opendata-ai.it/a2a/ \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <clerk_jwt>' \
  -H 'A2A-Version: 0.3' \
  -d '{
    "jsonrpc":"2.0","id":"1","method":"message/send",
    "params":{"message":{
      "messageId":"m-2",
      "role":"user",
      "parts":[{"kind":"text","text":"qualità aria a Milano"}],
      "metadata":{"skill":"search_open_data"}
    }}
  }'

Python — A2A SDK ufficiale

pip install a2a-sdk

# client.py
import os
from a2a.client import A2AClient

client = A2AClient(
    "https://api.opendata-ai.it/a2a/",
    headers={"Authorization": f"Bearer {os.environ['OPENDATA_JWT']}"},
)

reply = client.send_message(
    text="popolazione di Milano per età, ultimi 5 anni",
    metadata={"skill": "search_open_data"},
)

# Il primo artifact è la sintesi narrativa
print(reply.artifacts[0].parts[0].text)

# Il secondo è il payload strutturato {text, resources}
import json
data = json.loads(reply.artifacts[1].parts[0].data)
for r in data["resources"]:
    print("-", r["title"], "→", r["url"])

Vincoli da rispettare

  • messageId obbligatorio — il SDK valida con pydantic strict.
  • Header A2A-Version deve coincidere col metodo (1.0SendMessage, 0.3message/send). In alternativa puoi omettere l'header: il server lo deduce dal nome del metodo.
  • Skill si seleziona via message.metadata.skill (default search_open_data).
  • Auth: in produzione gli endpoint A2A sono dietro JWT Clerk. In dev locale con AUTH_ENABLED=false l'header diventa opzionale.

Shape della risposta

result.task.status.state = completed / TASK_STATE_COMPLETED, più una lista di artifacts:

  • artifact[0] — la sintesi narrativa testuale.
  • artifact[1] — un JSON strutturato {text, resources} con la stessa shape di POST /datasets/search.

Quando usare A2A vs MCP

  • MCP espone tool a un singolo LLM. Sceglilo se vuoi costruire il tuo agente che chiama i nostri server CKAN / ISTAT / OSM uno per volta.
  • A2A espone l'intero agente OpenData AI a un altro agente. Sceglilo se vuoi delegare l'intera domanda “cerca tra gli open data” al nostro orchestratore, ricevendo la sintesi pre-fatta.