Skip to content

Observability

Both halves land in the platform’s Victoria stack and Grafana; nothing here runs a second telemetry pipeline. Serving is watched per model, agents per run.

    flowchart LR
  subgraph serving["Serving"]
    V["vLLM pods<br/>vllm:* :8000"]
    AIG["ai-gateway extproc<br/>gen_ai_* :1064"]
  end
  subgraph agents["Agents"]
    RUN["Agent run<br/>OTLP spans, step log"]
    AGW["agentgateway agent-router<br/>access log, gen_ai_* :15020"]
    KSM["kube-state-metrics<br/>AgentRun state"]
  end
  VM[("VictoriaMetrics")]
  VL[("VictoriaLogs")]
  VT[("VictoriaTraces")]
  COL["agent-traces-collector<br/>allowlisted metadata"]
  KEDA["KEDA<br/>three triggers"]
  G["Grafana"]
  V --> VM
  AIG --> VM
  VM --> KEDA
  RUN --> COL --> VT
  RUN -->|step log| VL
  AGW --> VL
  AGW --> VM
  KSM --> VM
  VM --> G
  VL --> G
  VT --> G
  

Serving

SignalSourceRead it in
Engine metricsEach vLLM pod’s /metrics on port 8000, vllm:* labelled by model_name, scraped by the VMServiceScrape each claim renders (observability.metrics.interval, 30s on all four)LLM Platform — Self-hosted vLLM Fleet: replicas, queue depth, throughput, TTFT and decode latency, GPU KV-cache usage, prefix-cache hit rate, vLLM error logs
Gateway metricsThe AI Gateway extproc’s /metrics on port 1064, gen_ai_* (OpenTelemetry semantic conventions)LLM Gateway — Envoy AI Gateway GenAI Telemetry: token spend per model, base-vs-canary attribution, TTFT and time per output token
AutoscalingKEDA’s three triggers query the same vllm:* series every 15 sAutoscaling & GPUs

Both dashboards live in the Grafana folder llm (apps/base/ai/llm/grafana-folder.yaml), which only exists once the opt-in umbrella is resumed.

Alerts are lagging signals, deliberately not scale triggers:

FileAlerts
apps/base/ai/llm/vmrule-llm-slo.yamlServing: sustained queue depth, time to first token breached, a replica scaled to zero. Gateway: gen_ai telemetry missing, scrape down, high error rate, canary error-rate and latency regressions
apps/base/ai/llm/vmrule-ai-fleet.yamlFleet abort rate, Semantic Router down, Promptfoo regression and staleness

Agents

One trace, one step log and one dashboard page per run, so a human can open any run and see where it stands, what it did, what it cost and where its time went.

Metric names. Maku’s metrics are maku_factory_* and maku_rooms_*, and the dashboards and alerts read those names.

ComponentSoftwareWhat it does
LogsVictoriaLogsEvery run’s step log and every gateway call, attributed to the run
MetricsVictoriaMetricsTokens, cost, latency and errors per run, and each AgentRun’s state through kube-state-metrics
TracesVictoriaTraces, behind an OpenTelemetry Collector that keeps only allowlisted metadataOne trace per run: steps, model calls and tool calls. Metadata only: no prompts or outputs
DashboardsGrafanaIn observability/base/agent-platform/: agent-run, one page per run; agent-fleet; agents-overview; agent-room; agent-gateway; agent-identity, spend per charged principal; and agent-factory, the factory’s own page: PR outcomes, merge decisions, a Human path row (hand-off to merge, waiting on a human, Classes paused) and Cost by role over 7 days

The full transcript (prompts and outputs) stays in the room, behind room access. Alerting on runs belongs to the factory’s rules, per the observability design.

What you can inspect

QuestionWhere
Where is my run?kubectl get agentrun -n agents, or the agent-run dashboard’s status panel: phase, reason, PR, tokens against budget
What did it do?The step log, in kubectl logs or on the dashboard, with every gateway and MCP call of that run
What did it cost, how fast was it?The dashboard: tokens in and out, cost, model latency, error rate. agent-fleet shows every run
Where did the time go?A trace per run: steps, model calls and tool calls, with timings. Metadata only

A run’s page is /d/agent-run/agent-run?var-run=<id>; its This run’s window link sets the time range to the run. A resumed run counts each step once and has one trace per attempt. The room holds a run’s transcript to its end: before the harness stops, even on a reclaimed node, it asks maku-bridge to read its log to the last event.

The merge gate’s signals

The agent-factory dashboard’s Human path row reads the factory’s merge series; each series, its labels and what each decision means are on Run auto-merge safely. Cost by role reads maku_factory_run_cost_usd{tier,role}.

Alerts in observability/base/agent-platform/vmrule-agent-factory.yaml:

AlertFires when
AgentFactoryAutoMergeRevertedA merged agent PR was reverted. Unless it carries factory/drill, the breaker pauses its class
AgentFactoryMergeRegressionThe post-merge monitor found a regression after a merge; the class is demoted at its next decision
AgentFactoryClassPausedLongA live class of cloud-native-ref has been paused for 7 days

Spend per identity

The agent-identity dashboard (Spend per identity) shows one charged principal at a time, never All: a person or a sponsored service account. It attributes cost to the work a principal caused. It does not score the person: no panel groups, sorts or ranks by principal.

SectionReads
SpendToday against the principal’s daily cap and against the fleet’s, by kind, per day
TokensBy type, and the cache-read share
WorkRuns, rooms and tasks, each linked
OutcomesPull requests opened, merged and reverted, and cost per accepted pull request
LimitsRefusals and revocations
WaitsTasks waiting for a human now, and how long its tasks waited
SponsoredThe sponsor, and ceilings against spend

Every figure is in UTC, the ledger’s day. The sources are the factory’s per-payer series (maku_factory_charged_usd_total, the maku_factory_budget_*_usd gauges) and maku_factory_run_charge_info{run, principal, task, room}, which joins the gateway’s per-run tokens to the payer. The panels are held by test-agent-identity-dashboard and test-agent-identity-queries.

The agent gateway’s signals

agentgateway (gateways) emits under its own names. The scrape (infrastructure/base/agent-gateway/vmpodscrape.yaml) renames only the families the run meter, the budget alerts and the run page read, so they keep the gen_ai_* contract:

agentgateway emitsStored as
agentgateway_gen_ai_client_token_usage_{bucket,sum,count}gen_ai_client_token_usage_{bucket,sum,count}
gen_ai_token_type="input_cache_read"gen_ai_token_type="cached_input"
gen_ai_token_type="input_cache_write"gen_ai_token_type="cache_creation_input"
agentgateway_gen_ai_server_request_duration_*gen_ai_server_request_duration_seconds_*
gen_ai_systemgen_ai_provider_name

Every other series keeps its agentgateway_ name, and all carry namespace="agent-gateway", which keeps them apart from ai-gateway’s. Without the cache relabel the meter would price every cache read at the input rate.

Access logs are JSON with no msg, so LogsQL selects by pod labels and log.* fields:

kubernetes.pod_namespace:"agent-gateway" AND kubernetes.pod_labels.gateway.networking.k8s.io/gateway-name:"agent-router"
  | unpack_json | log.x_ar_agent:"system:serviceaccount:agents:<run>"

Useful fields: log.http.status, log.http.path, log.reason, log.route, log.x_ar_agent, log.mcp.method.name, log.gen_ai.tool.name.

Alerts in observability/base/agent-platform/vmrule.yaml, read on the Agent gateway dashboard (uid: agent-gateway):

AlertFires when
AgentRouterMetricContractBrokenModel calls succeed but no gen_ai_client_token_usage series follows, or a token type outside the four the meter reads appears: an upgrade renamed something the relabel maps
AgentGateway5xxRatioHighOver 5% of requests answer 5xx for 10 minutes
AgentGatewayModelErrorRatioHighOver 10% of model calls fail for 10 minutes
AgentGatewayUnauthorizedBurstMore than 20 refusals with reason JwtAuth or Authorization in 5 minutes
AgentRateLimitDownagent-ratelimit is down: budgets B1–B2 count nothing while the router fails open
AgentBudgetShadowOverrun (info)In shadow, a budget admitted calls over its limit: what enforcing would refuse
AgentBudgetRefusedEnforcing, a budget refused a run’s calls with 429
AgentCacheReadZero (critical)An internal run read nothing from the prompt cache after 100k input tokens: a dropped cache marker, every step at full price
AgentCacheReadShareLowAn internal run read under 60% of its input from the cache over 15 minutes, with 200k input in the window
AgentCacheMarkerMissingA run’s harness logged prompt cache NOT applied (a LogsQL rule): the SDK changed under the harness’s patch