Problem
Die Anfrage wirkt normal: RĂŒckgabestatus prĂŒfen und kurz antworten.
In den Traces sieht es anders aus: In 6 Minuten hat ein Run 52 Tool-Aufrufe gemacht
(search.read - 31, crm.lookup - 14, http.get - 7) und endete trotzdem mit timeout.
FĂŒr diese Aufgabenklasse kann das etwa ~$3 statt der ĂŒblichen ~$0.10 kosten.
Die API ist formal "alive": die meisten Antworten sind 200, es gibt keinen klaren Crash.
Aber der User bekommt keine Antwort, und die Run-Kosten steigen mit jeder Wiederholung.
Das System crasht nicht.
Es vervielfacht identische Aufrufe und verbrennt leise Budget.
Analogie: Stell dir einen Support-Mitarbeiter vor, der auf derselben Nummer immer wieder redial drĂŒckt, statt zu eskalieren oder den Plan zu Ă€ndern. Er ist beschĂ€ftigt, aber das Problem bewegt sich nicht. Tool Spam in Agenten sieht genauso aus: viele Aktionen, wenig nĂŒtzlicher Fortschritt.
Warum das passiert
Tool Spam entsteht nicht, weil der Agent "zu hart arbeitet", sondern weil die Runtime eine neue sinnvolle Aktion nicht von einem Duplikat ohne Fortschritt trennt.
In Production sieht das meistens so aus:
- LLM wÀhlt einen
tool_call; - das Tool liefert ein instabiles oder unzureichendes Signal;
- der Agent wiederholt denselben Aufruf (oder fast denselben);
- ohne Dedupe, Budget-Gates und eine zentrale Retry-Policy wÀchst der Zyklus.
Das Problem ist nicht ein einzelnes Tool. Das Problem ist, dass das System Wiederholungen nicht begrenzt, bevor sie zum Incident werden.
Welche AusfÀlle am hÀufigsten auftreten
Um es praktisch zu halten: In Production sieht man meist vier Tool-Spam-Pattern.
Repeated signature spam
Der Agent ruft dasselbe tool mit denselben Argumenten mehrmals hintereinander auf.
Typische Ursache: kein Dedupe ĂŒber tool+args_hash innerhalb des Runs.
Argument jitter spam
Nur Kleinigkeiten in den Argumenten Ă€ndern sich: GroĂ-/Kleinschreibung, Leerzeichen, Wortreihenfolge. Semantisch ist es dieselbe Anfrage, aber das System behandelt sie als neu.
Typische Ursache: keine Argument-Normalisierung vor Dedupe.
Retry amplification
Retries passieren im Agenten, im Gateway und im Tool-SDK. Ein Fehler wird zur Serie duplizierter Aufrufe.
Typische Ursache: Retry-Policy ist ĂŒber mehrere Stellen verteilt.
Fan-out spam
Ein Agent-Schritt startet viele parallele Aufrufe ohne hartes Limit. Selbst ohne Zyklus ĂŒberlastet das externe APIs schnell.
Typische Ursache: kein bounded fan-out und keine per-tool caps.
Wie man diese Probleme erkennt
Tool Spam ist gut ĂŒber die Kombination von Runtime- und Gateway-Metriken sichtbar.
| Metrik | Tool-Spam-Signal | Was tun |
|---|---|---|
tool_calls_per_task | starker Anstieg der Aufrufe pro Run | max_tool_calls und per-tool caps setzen |
repeated_tool_signature_rate | hÀufige Wiederholungen von tool+args innerhalb eines Runs | Dedupe-Window und Short-Lived-Cache ergÀnzen |
unique_signature_ratio | Anteil einzigartiger Aufrufe sinkt | No-Progress-Regel fĂŒr N Schritte ergĂ€nzen |
retry_amplification_rate | Retries werden zwischen Schichten dupliziert | Retry-Policy in einem Gateway zentralisieren |
cost_per_run | Run-Kosten steigen ohne QualitĂ€tsgewinn | Budget-Gate und Kill Switch fĂŒr problematisches Tool aktivieren |
Wie man Tool Spam von wirklich breiter Suche unterscheidet
Nicht jede hohe Zahl von Aufrufen ist ein Fehler. Die SchlĂŒsselfrage: liefert jeder Aufruf ein neues nĂŒtzliches Signal?
Normal, wenn:
- neue
tool_call-Aktionen wirklich neue Quellen oder Fakten öffnen; unique_signature_ratiostabil bleibt;- Kosten zusammen mit der AntwortqualitÀt wachsen.
GefÀhrlich, wenn:
- dieselbe Signature (oder fast dieselbe) wiederholt wird;
- 3-5 Schritte hintereinander keine neue Information liefern;
- Kosten und Latenz wachsen, aber die AntwortqualitÀt nicht besser wird.
Wie man solche AusfÀlle stoppt
Praktisch sieht das so aus:
max_tool_callspro Run und per-tool limits setzen;- Dedupe ĂŒber
tool+args_hashmit kurzem Window hinzufĂŒgen; - Retry-Policy nur im Gateway halten (mit klarer Liste non-retryable Fehler);
- bei Duplikaten oder Limit-VerstoĂ cached/partial Ergebnis und Stop Reason zurĂŒckgeben.
Minimaler Guard zur Kontrolle wiederholter Aufrufe:
from dataclasses import dataclass
import json
def call_signature(tool: str, args: dict) -> str:
normalized_args = normalize_args(args)
normalized = json.dumps(normalized_args, sort_keys=True, ensure_ascii=False)
return f"{tool}:{normalized}"
def normalize_text(value: str) -> str:
return " ".join(value.strip().lower().split())
def normalize_args(args: dict) -> dict:
normalized: dict = {}
for key, value in args.items():
if isinstance(value, str):
normalized[key] = normalize_text(value)
else:
normalized[key] = value
return normalized
@dataclass(frozen=True)
class ToolSpamLimits:
max_tool_calls: int = 12
max_repeat_per_signature: int = 2
class ToolSpamGuard:
def __init__(self, limits: ToolSpamLimits = ToolSpamLimits()):
self.limits = limits
self.total_calls = 0
self.by_signature: dict[str, int] = {}
def on_tool_call(self, tool: str, args: dict) -> str | None:
self.total_calls += 1
if self.total_calls > self.limits.max_tool_calls:
return "budget:tool_calls"
sig = call_signature(tool, args)
self.by_signature[sig] = self.by_signature.get(sig, 0) + 1
if self.by_signature[sig] > self.limits.max_repeat_per_signature:
return "tool_spam:repeated_signature"
return None
Das ist ein Baseline-Guard: In Production ergÀnzt man vor args_hash oft Domain-Normalisierung
(trim/lowercase/collapse spaces fĂŒr Text, und canonical ordering fĂŒr ausgewĂ€hlte Felder),
und on_tool_call(...) wird vor der eigentlichen tool-AusfĂŒhrung aufgerufen, um Duplikate vor dem unnötigen externen Aufruf zu stoppen.
Wo das in der Architektur umgesetzt wird
Tool-Spam-Kontrolle liegt in Production meist in drei Schichten.
Agent Runtime ist verantwortlich fĂŒr Run-Limits,
Stop Reasons, No-Progress-Regeln und kontrolliertes Beenden.
Hier werden budget:tool_calls und tool_spam:* typischerweise erfasst.
Tool Execution Layer ist verantwortlich fĂŒr Dedupe, Retry-Policy, Short-Lived-Cache und Normalisierung von Tool-Fehlern. Wenn diese Schicht schwach ist, breitet sich Spam schnell im ganzen Workflow aus.
Policy Boundaries definiert, welche Tools wie oft und unter welchen Bedingungen aufgerufen werden dĂŒrfen. So lassen sich riskante Tools schon vor der AusfĂŒhrung begrenzen.
Selbstcheck
Schneller Check vor dem Release. Hake die Punkte ab und sieh dir den Status unten an.
Das ist ein kurzer Sanity-Check, kein formales Audit.
Fortschritt: 0/8
â Es gibt Risikosignale
Grundlegende Kontrollen fehlen. SchlieĂen Sie die wichtigsten Checklist-Punkte vor dem Release.
FAQ
Q: Reicht nur max_steps?
A: Nein. Ein Agent-Schritt kann mehrere tool_call enthalten, deshalb braucht es ein separates Tool-Limit.
Q: Zerstört Dedupe die Freshness?
A: Nein, wenn Dedupe kurz ist und pro Run scoped bleibt. Ziel ist, Rausch-Duplikate zu entfernen, nicht "alte Wahrheit" lange zu cachen.
Q: Wo sollen Retries leben?
A: In einem einzigen choke point, meist im Tool-Gateway. Dort sollten non-retryable Fehler explizit abgeschnitten werden: 401, 403, 404, schema validation errors und policy denials sollten den Run in der Regel sofort beenden.
Q: Was soll der User sehen, wenn ein Run wegen Spam gestoppt wird?
A: Den Grund fĂŒr den Stopp, was bereits geprĂŒft wurde, und den sicheren nĂ€chsten Schritt (fallback oder manuelle Eskalation).
Tool Spam sieht fast nie wie ein lauter Ausfall aus.
Es ist ein langsames AufblÀhen von Aufrufen, Latenz und Kosten, das man vor allem in Traces sieht.
Deshalb brauchen Production-Agenten nicht nur bessere Modelle, sondern strenge tool_call-Kontrolle auf Runtime- und Gateway-Ebene.
Verwandte Seiten
Um das Problem tiefer zu schlieĂen, siehe:
- Warum AI-Agenten scheitern - allgemeine Karte von AusfÀllen in Production.
- Infinite loop - wie Schleifen schnell in Wiederholungsaufrufe kippen.
- Budget explosion - wie Tool Spam Kosten aufblÀht.
- Tool failure - wie instabile Tools Retry-Wellen auslösen.
- Agent Runtime - wo Stop Reasons und Execution-Limits gesetzt werden.
- Tool Execution Layer - wo Dedupe, Retries und Aufrufkontrolle liegen.