Idee in 30 Sekunden
Tool usage metrics zeigen nicht nur, ob ein Agent funktioniert, sondern wie er Tools nutzt und wo der Tool-Layer bricht.
Sie helfen zu sehen, welche Tools am haeufigsten aufgerufen werden, wo latency waechst und wo wiederholte oder fehlerhafte Aufrufe starten.
Ohne diese Metriken ist es schwer, Ueberlastung im Tool-Layer und steigende Kosten rechtzeitig zu erkennen.
Hauptproblem
Allgemeine Run-Metriken zeigen nicht, was genau auf Tool-Ebene passiert.
Zwei Runs koennen aehnliche Gesamt-latency haben, aber in einem Fall liegt das Problem bei langsamem search, im anderen bei wiederholten fetch-Aufrufen.
Ohne Tool-Metriken ist das bis zum Incident schwer sichtbar.
Als Naechstes schauen wir, wie man diese Signale liest und Probleme findet.
In Production sieht das meist so aus:
- ein Tool wird unbemerkt zum Hotspot;
- retries steigen, aber die Ursache ist unklar;
- ein Teil der Runs verbraucht zu viele Schritte nur fuer Tools;
- das Team sieht das Problem erst, wenn error rate oder Budget steigen.
Darum sollte der Tool-Layer separat ueberwacht werden, nicht nur ueber allgemeine Run-Metriken.
Wie es funktioniert
Tool usage metrics bauen auf tool_call- und tool_result-Events auf.
Tool-Metriken teilen sich in:
- infra metrics (
tool_latency_p95,tool_error_rate); - behavior metrics (
repeated_tool_calls,tool_calls_per_run,unique_tools_per_run).
Diese Metriken beantworten, "wie sich der Tool-Layer ueber die Zeit verhaelt". Logs und Tracing bleiben noetig, um einen konkreten problematischen Run zu erklaeren.
Retries entstehen meist auf Runtime-Ebene, nicht auf Code-Ebene: der Agent bekommt einen Tool-Fehler als Observation und versucht es erneut. Retries sind nicht nur Wiederholungen, sondern ein Signal, dass der Agent versucht, sich an Tool-Fehler anzupassen.
Typische Production-Tool-Metriken
| Metrik | Was sie zeigt | Warum sie wichtig ist |
|---|---|---|
| tool_calls_total | Gesamtzahl der Tool-Aufrufe | Lastkontrolle fuer den Tool-Layer |
| tool_calls_per_run | wie viele Tool-Aufrufe ein Run hat | Erkennung ueberfluessiger oder zyklischer Aufrufe |
| unique_tools_per_run | wie viele unterschiedliche Tools ein Run nutzt | Bewertung der workflow-Komplexitaet |
| tool_error_rate | Anteil fehlerhafter Tool-Aufrufe | fruehe Erkennung instabiler Tools |
| tool_latency_p50 / p95 | typische und Tail-latency pro Tool | Lokalisierung langsamer Abhaengigkeiten |
| repeated_tool_calls | Aufrufe mit demselben Tool und denselben args | Erkennung von Tool-Spam |
| tool_cost_per_run | geschaetzte Tool-Kosten pro Run | Budgetkontrolle und Erkennung teurer Tools |
Damit Metriken praktisch nutzbar sind, werden sie meist nach tool, release und bei Bedarf model segmentiert.
Wichtig: keine hoch-kardinalen Felder (run_id, request_id, args_hash) in Labels aufnehmen, sonst wird das Metrik-Storage schnell ueberlastet.
Wie man den Tool-Layer liest
Was aufgerufen wird -> wie sich der Agent verhaelt -> was sich ueber Zeit veraendert. Diese drei Ebenen sollte man immer zusammen betrachten.
Wichtig sind Zeittrends und Unterschiede zwischen Releases, nicht Einzelwerte.
Jetzt zu Signal-Kombinationen:
tool_error_rateâ +repeated_tool_callsâ -> Tool ist instabil, Agent retriedtool_latency_p95â +tool_cost_per_runâ -> Degradation in einem teuren Tooltool_calls_per_runâ +unique_tools_per_runâ -> uebermaessige workflow-Komplexitaet
Wann einsetzen
Ein voller Satz an Tool-Metriken ist nicht immer noetig.
Fuer einfache Agenten mit 1-2 Tools reichen manchmal tool_calls_total und tool_error_rate.
Detaillierte Tool usage metrics werden kritisch, wenn:
- der Agent stark externe APIs oder DBs nutzt;
- retries haeufig auftreten;
- Tool-Kosten kontrolliert werden muessen;
- Tool-Spam erkannt werden muss, bevor Nutzer betroffen sind.
Implementierungsbeispiel
Unten ist ein vereinfachtes Prometheus-Beispiel fuer Instrumentierung von Tool-Usage-Metriken. Das Beispiel zeigt Basis-Kontrolle: Aufrufanzahl, latency, error classes, Wiederholungen und Tool-Last pro Run.
import hashlib
import json
import time
from prometheus_client import Counter, Histogram
TOOL_CALL_TOTAL = Counter(
"agent_tool_call_total",
"Total tool calls",
["tool", "status", "release"],
)
TOOL_ERROR_TOTAL = Counter(
"agent_tool_error_total",
"Total tool errors by class",
["tool", "error_class", "release"],
)
TOOL_LATENCY_MS = Histogram(
"agent_tool_latency_ms",
"Tool latency in milliseconds",
["tool", "release"],
buckets=(20, 50, 100, 250, 500, 1000, 2000, 5000),
)
TOOL_CALLS_PER_RUN = Histogram(
"agent_tool_calls_per_run",
"Number of tool calls per run",
["release"],
buckets=(0, 1, 2, 4, 8, 12, 16, 24, 32),
)
UNIQUE_TOOLS_PER_RUN = Histogram(
"agent_unique_tools_per_run",
"Number of unique tools used in run",
["release"],
buckets=(0, 1, 2, 3, 4, 6, 8, 12),
)
REPEATED_TOOL_CALL_TOTAL = Counter(
"agent_repeated_tool_call_total",
"Repeated tool calls with same tool+args signature",
["tool", "release"],
)
TOOL_COST_USD_TOTAL = Counter(
"agent_tool_cost_usd_total",
"Estimated total tool cost in USD",
["tool", "release"],
)
STEP_ERROR_TOTAL = Counter(
"agent_step_error_total",
"Total non-tool step errors by type and class",
["step_type", "error_class", "release"],
)
def stable_hash(value):
# default=str gibt Basis-Kompatibilitaet;
# in kritischen Systemen ist explizite Serialisierung besser (z. B. ISO 8601)
payload = json.dumps(value, sort_keys=True, ensure_ascii=False, default=str).encode("utf-8")
return hashlib.sha256(payload).hexdigest()
def run_agent(agent, task, release="2026-03-21"):
tool_calls = 0
unique_tools = set()
seen_signatures = set()
try:
for step in agent.iter(task):
step_type = step.type
result = None
if step_type != "tool_call":
try:
result = step.execute()
except Exception as error:
STEP_ERROR_TOTAL.labels(
step_type=step_type,
error_class=type(error).__name__,
release=release,
).inc()
raise
if result and result.is_final:
break
continue
tool_name = getattr(step, "tool_name", "unknown")
args = getattr(step, "args", {})
tool_calls += 1
unique_tools.add(tool_name)
signature = (tool_name, stable_hash(args))
if signature in seen_signatures:
REPEATED_TOOL_CALL_TOTAL.labels(tool=tool_name, release=release).inc()
else:
seen_signatures.add(signature)
started_at = time.time()
try:
result = step.execute()
TOOL_CALL_TOTAL.labels(tool=tool_name, status="ok", release=release).inc()
cost_usd = getattr(result, "cost_usd", None)
if cost_usd:
TOOL_COST_USD_TOTAL.labels(tool=tool_name, release=release).inc(cost_usd)
except Exception as error:
TOOL_CALL_TOTAL.labels(tool=tool_name, status="error", release=release).inc()
TOOL_ERROR_TOTAL.labels(
tool=tool_name,
error_class=type(error).__name__,
release=release,
).inc()
# Dieses Beispiel macht raise.
# In realen Agenten wird der Fehler oft als Observation an das LLM fuer retry gegeben.
raise
finally:
TOOL_LATENCY_MS.labels(tool=tool_name, release=release).observe(
(time.time() - started_at) * 1000
)
if result and result.is_final:
break
finally:
TOOL_CALLS_PER_RUN.labels(release=release).observe(tool_calls)
UNIQUE_TOOLS_PER_RUN.labels(release=release).observe(len(unique_tools))
# tool_cost_per_run wird meist auf Dashboard-Ebene berechnet:
# sum(agent_tool_cost_usd_total) / run_count
So koennen diese Metriken zusammen in einem realen Dashboard aussehen:
| Tool | calls/min | error_rate | p95 latency | Status |
|---|---|---|---|---|
| search_docs | 320 | 6.8% | 1.9s | critical: alert |
| fetch_url | 180 | 1.4% | 680ms | warning: p95 steigt |
| db_lookup | 95 | 0.3% | 120ms | ok |
Fuer error_class ist ein normalisiertes Wertewoerterbuch besser, um unnoetige Kardinalitaet zu vermeiden.
Investigation
Wenn ein Alert ausloest:
- das anomale Tool ueber Metriken finden;
- konkrete Runs im Tracing ansehen;
- Argumente und Antworten in Logs pruefen;
- root cause finden (Tool, Agent-Logik oder externes API).
Typische Fehler
Auch wenn Tool-Metriken vorhanden sind, funktionieren sie oft nicht richtig wegen typischer Fehler unten.
Es gibt nur Gesamtaufrufe, aber keine Aufteilung nach Tool
tool_calls_total ohne Aufteilung pro Tool hilft im Incident kaum.
In dieser Situation ist es schwer, die Quelle von Tool-Ausfall schnell zu finden.
Wiederholte Aufrufe werden nicht erfasst
Ohne repeated_tool_calls sieht man schwer, dass der Agent dasselbe Tool mit denselben args wiederholt aufruft.
Das maskiert oft die fruehe Phase von Tool-Spam.
Keine p95 latency je Tool
Das System kann stabil wirken, waehrend ein Teil der Nutzer bereits 5+ Sekunden wartet.
Fuer den Tool-Layer ist das Minimum p50 und p95.
Hoch-kardinale Labels
run_id, request_id oder args_hash in Labels ueberlasten Metrik-Backends schnell.
Diese Daten besser in Logs halten, nicht in Labels.
Keine Alerts fuer den Tool-Layer
Ohne Alerts bleiben Metriken passive Telemetrie. Dadurch verpasst man leicht fruehe Signale einer Budget-Explosion durch uebermaessige externe API-Aufrufe.
Selbstcheck
Unten ist eine kurze Checkliste fuer Basis-Tool-Usage-Metriken vor Release.
Fortschritt: 0/9
â Grundlegende Observability fehlt
Das System wird in production schwer zu debuggen sein. Starten Sie mit run_id, structured logs und tracing von tool calls.
FAQ
Q: Worin unterscheiden sich tool usage metrics von allgemeinen Agent-Metriken?
A: Allgemeine Metriken zeigen den Gesamtzustand des Systems. Tool usage metrics zeigen, was spezifisch im Tool-Layer passiert.
Q: Welches Minimum an Tool-Metriken braucht man zum Start?
A: Starte mit tool_calls_total, tool_error_rate, tool_latency_p95 und tool_calls_per_run.
Q: Soll args_hash in Labels aufgenommen werden?
A: Nein. Das erzeugt fast immer hohe Kardinalitaet. Fuer solche Daten besser strukturierte Logs nutzen.
Q: Wie trennt man einen einmaligen Fehler von einem systemischen Tool-Layer-Problem?
A: Pruefe, ob sich das Problem fuer ein bestimmtes Tool ueber mehrere Runs und Releases wiederholt. Wenn dieselben Signale (error_class, latency, repeated_tool_calls) wiederkehren, ist es systemisch.
Verwandte Seiten
Weiter zum Thema:
- Agent-Metriken â Gesamtmodell fuer Metriken in Agentensystemen.
- Agent-Logging â Events fuer die Incident-Analyse.
- Agent Tracing â Pfad eines Runs Schritt fuer Schritt.
- Semantisches Logging fuer Agenten â stabiles Event-Vokabular fuer Analytics.
- Cost Monitoring fuer KI-Agenten â Kostenkontrolle in Production.