Skip to main content

Observability

Agent-to-Agent (A2A) traffic uses the same telemetry pipelines as model traffic. Usage-event fields identify the caller, agent, method, and outcome, while Prometheus labels let you separate agent traffic from model traffic.

Use these signals to measure agent call volume and monitor failures. Rate limits and upstream errors appear in the same observability tools you already use for model traffic. In AISIX Cloud, budget rejections appear there as well.

Usage Events

AISIX emits an A2A usage event once the gateway can attribute the request to an enabled agent and caller. This includes calls rejected by unsupported upstream authentication, rate limits, upstream failures, or budgets configured through AISIX Cloud. Malformed request bodies can be rejected before a usage event is emitted.

The event goes into the same sink as model usage and identifies the caller, agent, method, outcome, and timing:

FieldValue
inbound_protocola2a
a2a_agent_nameThe registered agent that was called.
a2a_methodThe JSON-RPC method exactly as the caller wrote it, such as message/send or its 1.0 spelling SendMessage, when AISIX can read it from the request body.
a2a_operationThe canonical operation a2a_method names. Group by this rather than by the raw method: A2A 0.3 and 1.0 spell one operation two ways, and an unrecognized method becomes unknown.
a2a_protocol_versionThe wire version AISIX announced to the agent, 0.3 or 1.0.
a2a_task_idThe task the call created or acted on. Use it to gather every request that touched one task across message/send, tasks/get, and tasks/resubscribe. Empty when the call named no task.
a2a_context_idThe conversation the task belongs to, which ties a multi-turn exchange's tasks together.
a2a_task_stateThe last state the agent reported, normalized to submitted, working, input-required, auth-required, completed, canceled, failed, rejected, or unknown. Empty when no response carried a state.
a2a_stream_event_countEvents relayed to the caller on a streamed call. 0 on a non-streaming call.
upstream_ttft_msTime to the agent's first streamed event. Read with upstream_latency_ms it separates a stream that said nothing for a long time from one that said plenty.
prompt_tokens, completion_tokensToken counts for the message text, counted by the gateway. See Token counts.
api_key_idThe caller API key that made the call.
status_codeThe call's outcome status.
upstream_latency_msTime spent on the upstream agent call.
downstream_latency_msTotal time the caller waited for the agent call.
request_id, occurred_atCorrelation id and timestamp.

Token Counts

An A2A agent reports no usage of its own, because the protocol has no usage block. AISIX therefore counts the message text itself, using the gateway's own tokenizer over the text parts of the caller's message and of the agent's reply. Those counts land in prompt_tokens and completion_tokens. The event also carries usage_estimated: true, which says the gateway produced the counts rather than an upstream reporting them. Filter on that flag when you need provider-billed exactness.

Only message/send and message/stream are counted. A read such as tasks/get returns an answer that was already counted when the agent produced it, so counting it again would report one answer once per poll.

cost_usd remains zero. What an agent charges is not something the gateway can observe. These counts are reported only, never charged: they do not add to the caller key's tpm / tpd token windows. Budgets are a separate mechanism — they limit USD spend, and a zero cost adds none. See Traffic Controls.

File and data parts are not counted. Their bytes are not language, and including a base64 blob would distort the estimate.

Agent Gateway currently makes a single upstream attempt that spans the request, so upstream_latency_ms and downstream_latency_ms report the same duration. The separate fields keep A2A records consistent with other gateway traffic, where retries and gateway processing can make the two values differ.

The event does not include request or response message content. Message text reaches an observability exporter only when that exporter is configured for full content capture, and it never reaches the AISIX Cloud control plane.

A2A usage events follow the same delivery paths as model usage events. Any configured observability exporter receives them, so A2A traffic appears alongside the rest of your gateway traffic. In AISIX Cloud, they also flow to the control plane's usage sink.

Metrics

A2A requests appear in the gateway's Prometheus metrics with labels that distinguish them from model traffic. Use the labels below to filter the relevant metric series:

GoalMetricFilter
Count A2A requests by outcome.aisix_requests_totalprovider="a2a" and model="a2a"
Track active A2A requests.aisix_proxy_in_flight_requestsendpoint="/a2a" and inbound_protocol="a2a"
Check A2A usage-event emission.aisix_usage_events_emitted_totalhandler="a2a"

The aisix_a2a_* family carries the dimension the shared families cannot: which agent was reached and which operation was invoked.

MetricLabelsWhat it answers
aisix_a2a_requests_totalagent, operation, statusCall volume and failure rate for one agent's operation.
aisix_a2a_ttfb_secondsagent, operationHow long an agent takes to send its first streamed event.
aisix_a2a_stream_events_totalagent, operationEvents relayed. Divided by the request count over the streaming operations, it gives events per call.
aisix_a2a_task_state_totalagent, stateThe rate at which calls end on each state, such as a rising share of failed.

aisix_a2a_requests_total does not agree with aisix_proxy_requests_total{endpoint="/a2a"}. Two differences are deliberate. A call refused before its agent is resolved has no agent to file under, so it is counted in the proxy family only. That covers a bad key, a denied agent, and an unknown one. A stream the caller abandons is a 4xx here but a 2xx there, because the response really did begin as a 200. Read this family for agent health and the proxy family for route traffic.

Task ids, context ids, and JSON-RPC request ids are never metric labels. They are what makes a call traceable, and that is exactly what makes them unusable as label values; they appear in usage events and traces instead.

For usage-event emission, filter by handler. The emission counter keeps its protocol label bounded and groups A2A events under other.

This label behavior applies only to the Prometheus emission counter. The delivered usage event still identifies the traffic as A2A and includes the agent name and method.

Metrics are exposed on GET /metrics through the dedicated metrics listener. For the full metric catalog and label semantics, see Metrics Reference.

Verify Metrics

To verify that A2A metrics are emitted, send one A2A call through the gateway, then scrape the dedicated metrics listener. The example below uses the default listener address and path. If your startup configuration sets a different observability.metrics.prometheus.addr, use that address instead.

Metric families register on first observation, so the A2A series appears only after a call is recorded:

curl -sS "http://127.0.0.1:9090/metrics" | grep -E 'handler="a2a"|provider="a2a"'

The output should include both metric samples:

MetricLabel
aisix_usage_events_emitted_totalhandler="a2a"
aisix_requests_totalprovider="a2a"

Next Steps

You now know where A2A calls appear in usage events and metrics. Use these guides to review the full metric catalog or adjust the traffic that produces those signals: