Warum Ihr AI Agent ständig sein Gedächtnis verliert (und wie wir es behoben haben)
Ihr AI Agent hat gerade ein 30-minütiges Gespräch mit einem Benutzer geführt. Sie haben Projektanforderungen besprochen, Präferenzen ausgetauscht und Entscheidungen getroffen. Dann gibt der Benutzer /new ein, um eine neue Session zu starten.
Der Agent versucht, diese Konversation in das Langzeitgedächtnis zu konsolidieren. Der LLM-Aufruf schlägt fehl. Rate limit. Timeout. Oder das Model gibt Text zurück, anstatt das erforderliche Tool aufzurufen.
Das Gedächtnis ist weg. Dreißig Minuten Kontext, einfach verflogen.
Das passiert häufiger, als man denkt. Wir haben es in unseren LemonClaw-Instanzen nachverfolgt: Die Gedächtniskonsolidierung hatte eine Fehlerrate von ~15 % bei jedem einzelnen Model. Für ein Feature, das eigentlich eine unsichtbare Infrastruktur sein sollte, ist das inakzeptabel.
Wie andere Frameworks damit umgehen (Gar nicht)
Die meisten AI Agent Frameworks behandeln die Gedächtniskonsolidierung als einfachen LLM-Aufruf. Wenn es funktioniert, super. Wenn nicht, ist die Erinnerung verloren.
OpenClaw, das beliebteste Open-Source-Agent-Framework, verwendet für die Konsolidierung dasselbe Model wie für die Konversation. Ein Claude Sonnet-Aufruf, der 0,003 $ kostet und mehr als 8 Sekunden dauert, nur um einen Chat zusammenzufassen, den der Benutzer nie sehen wird. Wenn dieser Aufruf fehlschlägt (Rate limit, Timeout, Model-Fehler), loggt das Framework eine Warnung und macht weiter. Der Kontext des Benutzers ist weg.
nanobot, ein weiteres bekanntes Framework, hat dieselbe Architektur. Ein Model, ein Versuch, kein Fallback. Die Konsolidierungsfunktion hat nicht einmal einen Timeout. Ein langsamer Upstream (Cloudflare 524-Fehler sind häufig) blockiert die gesamte Session, bis die Verbindung abbricht.
Keines der Frameworks trennt die Konsolidierung vom Haupt-Model. Keines verfügt über eine Fallback-Logik für Memory-Operationen. Keines unterscheidet zwischen „der API-Aufruf ist fehlgeschlagen“ und „der API-Aufruf war erfolgreich, aber das Model hat nicht getan, was wir verlangt haben“.
Dies sind keine Randfälle. Bei einer Fehlerrate von 15 % pro Model verliert ein Framework, das 100 Konsolidierungen pro Tag durchführt, bei 15 davon das Gedächtnis. Über eine Woche gerechnet sind das 105 Konversationen, bei denen der Agent alles vergisst.
Das Problem liegt tiefer als die Retry-Logik
Die offensichtliche Lösung ist ein Retry mit exponential backoff. Das hatten wir bereits. Es fängt vorübergehende HTTP-Fehler gut ab:
# Retry-Schleife: 1s → 2s → 4s Backoff
for attempt in range(3):
try:
response = await acompletion(**kwargs)
return await self._collect_stream(response)
except (RateLimitError, APIConnectionError) as e:
await asyncio.sleep(RETRY_DELAYS[attempt])
Das fängt 429er-Fehler und Netzwerk-Blips ab. Aber zwei Fehlermodi schlüpfen durch:
Fehlermodus 1: Das Model beherrscht kein Tool Calling. Einige Models, insbesondere kleinere, die auf schnellen Inference-Engines laufen, scheitern gelegentlich daran, bei komplexen Prompts valide Function Calls zu generieren. Die API gibt einen 200er-Status mit einem ServiceUnavailableError zurück, der in einen MidStreamFallbackError eingepackt ist. Ihre Retry-Logik sieht eine Exception, versucht es mit demselben Model erneut und erhält denselben Fehler.
Fehlermodus 2: Das Model ist „erfolgreich“, ruft aber das Tool nicht auf. Das LLM liefert eine perfekt valide Antwort. HTTP 200. Keine Fehler. Aber anstatt save_memory mit strukturierten Daten aufzurufen, schreibt es eine einfache Textzusammenfassung. Ihre Retry-Engine betrachtet dies als Erfolg. Die Konsolidierungsfunktion prüft auf Tool-Aufrufe, findet keine und gibt auf.
Der zweite Fehlermodus ist der tückische. Der Transport Layer denkt, alles hätte funktioniert. Der Business Layer weiß, dass es nicht so ist. Keine noch so große Anzahl von Retries auf HTTP-Ebene wird ein Model reparieren, das Ihr Tool-Schema nicht versteht.
Dual-Layer Fallback Architektur
Wir haben dies mit zwei unabhängigen Fallback-Schleifen gelöst, die auf verschiedenen Ebenen operieren:
Benutzer sendet /new
│
▼
consolidate() ─── Business Layer Fallback
│ "Hat das Model save_memory aufgerufen?"
│ Nein → nächstes Model in der Kette versuchen
│
▼
_chat_with_retry() ─── Transport Layer Fallback
│ HTTP-Fehler → exponential backoff
│ Alle Retries erschöpft → Fallback-Kette durchlaufen
│
▼
MODEL_MAP Fallback-Kette:
llama-3.3-70b ─$0.59/M─→ qwen3-32b ─$0.29/M─→ llama-4-scout ─$0.11/M─→ gpt-4.1-mini ─→ claude-haiku
(394 TPS) (662 TPS) (594 TPS) (zuverlässig) (letzte Instanz)
Ebene 1 kümmert sich um Transportfehler. Ebene 2 kümmert sich um Fehler in der Business-Logik. Die Fallback-Kette wird von beiden Ebenen gemeinsam genutzt und einmal in einem zentralen Katalog definiert.
Dies ist ein grundlegend anderer Ansatz als „Retry-dasselbe-Model“. Wenn ein Model ein Tool nicht aufrufen kann, hilft ein erneuter Versuch mit demselben Prompt selten. Der Wechsel zu einem anderen Model mit anderen Gewichten und anderem Tool Calling-Verhalten hingegen schon.
Der Model-Katalog: Eine Single Source of Truth
Jedes Model in unserem Katalog hat ein optionales fallback-Feld, das auf das nächste zu testende Model verweist:
@dataclass(frozen=True)
class ModelEntry:
id: str
label: str
tier: str
description: str
fallback: str | None = None
hidden: bool = False # Vor Benutzern in /model Liste versteckt
MODEL_CATALOG = [
# Für Benutzer sichtbare Models (16 Models zur Auswahl)
ModelEntry("claude-sonnet-4-6", "Claude Sonnet 4.6", "standard",
"Recommended", fallback="claude-sonnet-4-5"),
ModelEntry("gpt-4.1-mini", "GPT-4.1 Mini", "economy",
"Stable tool calling", fallback="claude-haiku-4-5"),
# Versteckte Konsolidierungs-Models (nur intern)
ModelEntry("llama-3.3-70b-versatile", "Llama 3.3 70B (Groq)", "economy",
"394 TPS", fallback="qwen3-32b", hidden=True),
ModelEntry("qwen3-32b", "Qwen3 32B (Groq)", "economy",
"662 TPS", fallback="llama-4-scout-17b-16e-instruct", hidden=True),
# ...
]
Das Flag hidden=True hält interne Models aus dem benutzerseitigen /model-Befehl fern, während sie dennoch Teil der Fallback-Ketten bleiben. Benutzer sehen 16 Models, zwischen denen sie wechseln können. Das System nutzt 19. Die drei versteckten Models existieren ausschließlich für Hintergrundaufgaben wie die Gedächtniskonsolidierung, bei denen Geschwindigkeit und Kosten wichtiger sind als die Gesprächsqualität.
Dieser Katalog ist die einzige Quelle der Wahrheit für das gesamte Model-Routing. Ein neues Model zur Fallback-Kette hinzuzufügen, bedeutet eine einzige Zeile Code. Keine Konfigurationsdateien, die synchronisiert werden müssen, keine Umgebungsvariablen zum Aktualisieren, keine Deployment-Skripte zum Ändern.
Transport Layer: Geketteter Fallback mit Zykluserkennung
Die Retry-Engine durchläuft die Fallback-Kette unter Verwendung eines „Visited-Sets“, um Endlosschleifen zu verhindern:
async def _chat_with_retry(self, kwargs, original_model):
# Phase 1: Exponential Backoff auf dem primären Model
for attempt in range(3):
try:
response = await acompletion(**kwargs)
return await self._collect_stream(response)
except (RateLimitError, APIConnectionError, APIError) as e:
await asyncio.sleep(RETRY_DELAYS[attempt])
except AuthenticationError:
return LLMResponse(content="API key invalid.", finish_reason="error")
# Phase 2: Fallback-Kette durchlaufen
visited = {original_model}
current = original_model
while True:
entry = MODEL_MAP.get(current)
if not entry or not entry.fallback or entry.fallback in visited:
break
current = entry.fallback
visited.add(current)
# Korrekten Gateway für dieses Model auflösen
gw = self._resolve_gateway_for_model(current)
resolved = self._resolve_model(current, gateway=gw)
fb_kwargs = {**kwargs, "model": resolved}
# api_base für das Protokoll des Ziel-Models korrigieren
if gw and gw.default_api_base:
fb_kwargs["api_base"] = gw.default_api_base
try:
response = await acompletion(**fb_kwargs)
return await self._collect_stream(response)
except Exception:
continue # Nächstes in der Kette versuchen
return LLMResponse(content="Service unavailable.", finish_reason="error")
Das visited-Set ist entscheidend. Ohne dieses würde eine Kette wie A→B→A ewig kreisen. Mit ihm testet die Engine jedes Model genau einmal.
Auch die Gateway-Auflösung ist wichtig. Verschiedene Models benötigen unterschiedliche API-Formate. Claude-Models werden über ein Anthropic-Format-Gateway geroutet (ohne /v1 Suffix). GPT-Models laufen über ein OpenAI-kompatibles Gateway (mit /v1). Groq-Models nutzen einen weiteren Endpoint. Die Fallback-Engine löst für jedes Model in der Kette das korrekte Gateway auf und verhindert so Protokoll-Fehler, wie das Senden von Anthropic-Requests an einen OpenAI-Endpoint.
Dies ist ein Detail, das die meisten Frameworks völlig ignorieren. Sie gehen davon aus, dass alle Models dasselbe Protokoll sprechen. In der Produktion, mit 19 Models über 4 verschiedene API-Formate hinweg, bricht diese Annahme sofort zusammen.
Business Layer: Tool Call Verifizierung
Die Konsolidierungsfunktion fügt ihre eigene Fallback-Schleife obenauf hinzu:
async def consolidate(self, session, provider, model, **kwargs):
visited = set()
current_model = model
while current_model and current_model not in visited and len(visited) <= 3:
visited.add(current_model)
response = await asyncio.wait_for(
provider.chat(messages=messages, tools=SAVE_MEMORY_TOOL, model=current_model),
timeout=30,
)
if response.has_tool_calls:
# Erfolg: Memory extrahieren und speichern
args = response.tool_calls[0].arguments
self.write_long_term(args["memory_update"])
self.append_history(args["history_entry"])
return True
# Model hat das Tool nicht aufgerufen — nächstes in der Kette versuchen
entry = MODEL_MAP.get(current_model)
next_model = entry.fallback if entry else None
if next_model and next_model not in visited:
current_model = next_model
continue
return False # Keine weiteren Fallbacks vorhanden
return False
Dies fängt den Fall ab, in dem _chat_with_retry eine erfolgreiche Antwort zurückgibt (HTTP 200, valider Inhalt), das Model aber das Tool nicht benutzt hat. Die Konsolidierungsfunktion prüft has_tool_calls, und falls diese fehlen, wechselt sie zum nächsten Model in der Kette.
Der Timeout-Wrapper (asyncio.wait_for) löst ebenfalls einen Fallback aus. Wenn ein Model länger als 30 Sekunden braucht (häufig bei Cloudflare 524-Fehlern bei langsamen Upstreams), fängt die Funktion den TimeoutError ab und versucht das nächste Model, anstatt die Session des Benutzers unbegrenzt zu blockieren.
Warum Groq für die Konsolidierung
Die Gedächtniskonsolidierung ist eine Hintergrundaufgabe. Der Benutzer sieht den Output nicht. Er muss einfach nur funktionieren. Das macht sie zum perfekten Kandidaten für schnelle, günstige Models.
Die meisten Frameworks nutzen dasselbe teure Model für alles. Wenn Sie Claude Sonnet für die Konversation nutzen, nutzen Sie es auch für die Gedächtniskonsolidierung. Das sind 3 $/M Input-Token und über 8 Sekunden pro Konsolidierung für eine Aufgabe, die einen Output erzeugt, den kein Mensch jemals liest.
Wir haben die Konsolidierung komplett vom Konversations-Model entkoppelt. Das Gespräch nutzt das Model, das der Benutzer gewählt hat. Die Konsolidierung nutzt eine dedizierte Kette von Groq-gehosteten Models:
| Model | Speed | Input Kosten | Output Kosten |
|---|---|---|---|
| llama-3.3-70b-versatile | 394 TPS | $0.59/M | $0.79/M |
| qwen3-32b | 662 TPS | $0.29/M | $0.59/M |
| llama-4-scout-17b-16e | 594 TPS | $0.11/M | $0.34/M |
| gpt-4.1-mini (vorherig) | ~150 TPS | $0.40/M | $1.60/M |
Das primäre Model (llama-3.3-70b) konsolidiert eine Session mit 60 Nachrichten in ~5 Sekunden. Der vorherige Standard (gpt-4.1-mini) brauchte über 8 Sekunden. Die Kosten pro Konsolidierung sanken von ~$0,003 auf ~$0,001.
Der Kompromiss: Groq-Models haben bei komplexen Prompts ein weniger zuverlässiges Tool Calling. Genau deshalb existiert der Dual-Layer Fallback. Wenn llama-3.3-70b das Tool nicht aufrufen kann, übernimmt qwen3-32b. Wenn auch das scheitert, versucht es llama-4-scout. Sollten alle drei Groq-Models versagen, übernimmt gpt-4.1-mini mit einer Tool Calling-Zuverlässigkeit von fast 100 %.
In der Produktion sehen wir, dass das primäre Model in ca. 85 % der Fälle erfolgreich ist. Die Kette erreicht gpt-4.1-mini in weniger als 2 % der Konsolidierungen. Die totale Fehlerrate: praktisch null.
Ergebnisse aus der Produktion
Wir haben dies auf zwei LemonClaw-Instanzen ausgerollt und mit echten Telegram-Konversationen getestet.
Erstes Deployment (nur Single-Layer Fallback):
Memory consolidation (archive_all): 56 messages
llama-3.3-70b-versatile → "Failed to call a function"
Falling back → qwen3-32b
qwen3-32b: LLM did not call save_memory, skipping
→ "Memory archival failed, session not cleared."
Der Transport Layer fing den ersten Fehler ab und wechselte zum Fallback. Aber qwen3-32b lieferte Text zurück, ohne das Tool aufzurufen. Der Single-Layer Fallback konnte das nicht handhaben. Dies ist genau das Szenario, in dem jedes andere Framework stillschweigend das Gedächtnis verlieren würde.
Zweites Deployment (Dual-Layer Fallback):
Memory consolidation (archive_all): 60 messages
model=llama-3.3-70b-versatile → success
Memory consolidation done: 60 messages remaining
Dasselbe Model, dasselbe Nachrichtenvolumen. Diesmal funktionierte es beim ersten Versuch. Die unbeständige Natur von Tool Calling-Fehlern ist genau der Grund, warum man eine Fallback-Kette anstelle eines einzelnen Backup-Models braucht.
Wenn das primäre Model tatsächlich versagt, fängt die Kette es auf:
llama-3.3-70b → tool call failed
→ consolidate() fallback → qwen3-32b
→ qwen3-32b didn't call tool
→ consolidate() fallback → llama-4-scout
→ llama-4-scout didn't call tool
→ consolidate() fallback → gpt-4.1-mini
→ gpt-4.1-mini called save_memory ✓
Memory consolidation done
Vier Models probiert, Gedächtnis gespeichert. Der Benutzer sieht „Neue Session gestartet.“ und hat keine Ahnung, dass das alles passiert ist.
Die Architektur-Lücke
Das LemonClaw-Memory-System im Vergleich zu Alternativen, Feature für Feature:
| Fähigkeit | Typisches AI Agent Framework | LemonClaw |
|---|---|---|
| Konsolidierungs-Model | Dasselbe wie Konversation (teuer, langsam) | Unabhängige Model-Kette, Groq-beschleunigt |
| Fehlerbehandlung | Warnung loggen, Gedächtnis verlieren | Dual-Layer Fallback, 5 Models tief |
| Transport-Fallback | Dasselbe Model 3x erneut versuchen | Geketteter Fallback über versch. Models |
| Business-Logik-Fallback | Keiner | Tool-Call-Verifizierung + Model-Wechsel |
| Timeout-Schutz | Keiner (Cloudflare 524 blockiert Session) | asyncio.wait_for(timeout=30) + Fallback |
| Session-Kürzung | Keine (Kontext wächst ewig) | Alte Nachrichten nach Konsolidierung kürzen |
| Historie-Suche | Keine | HISTORY.md Rolling Window, per Grep durchsuchbar |
| Interne Models | Nicht unterstützt | hidden=True für rein systeminterne Models |
| Zyklus-Vermeidung | Nicht nötig (keine Ketten) | visited-Set verhindert A→B→A Schleifen |
| Gateway-Auflösung | Ein API-Format wird vorausgesetzt | Gateway pro Model mit Protokoll-Erkennung |
Jede Zeile in dieser Tabelle steht für einen Produktionsfehler, den wir entweder selbst erlebt oder in den Issue-Trackern anderer Frameworks beobachtet haben. Der Dual-Layer Fallback, der versteckte Model-Katalog, die Gateway-Auflösung pro Model, der Timeout-gesteuerte Fallback: Nichts davon existiert in OpenClaw, nanobot oder anderen Open-Source-Agent-Frameworks, die wir untersucht haben.
Was wir gelernt haben
„Request erfolgreich“ bedeutet nicht „Aufgabe erfolgreich“. Generische Retry-Engines arbeiten auf HTTP-Ebene. Sie können nicht wissen, dass eine 200er-Antwort mit validem JSON eigentlich ein Fehler ist, weil das Model das angeforderte Tool nicht benutzt hat. Geschäftskritische Operationen brauchen ihre eigenen Erfolgskriterien und ihre eigene Fallback-Logik.
Kleine Models scheitern anders als große Models. Große Models (GPT-4.1, Claude Sonnet) rufen Tools fast immer auf, wenn sie darum gebeten werden. Kleine Models auf schnellen Inference-Engines generieren manchmal valide aussehende Antworten, die das Tool-Schema komplett ignorieren. Das ist kein Bug, den man mit Prompt Engineering beheben kann. Es ist eine Fähigkeitslücke, die architektonische Absicherung erfordert.
Testen Sie mit Produktionsdaten, nicht mit synthetischen Daten. Unser initialer Test mit 6 synthetischen Nachrichten wurde von jedem Model bestanden. Die echte 60-Nachrichten-Session mit Tool-Call-Historie, Zeitstempeln und gemischten Sprachen schlug bei zwei von drei Groq-Models fehl. Die Komplexität echter Daten deckt Fehlermodi auf, die saubere Testdaten niemals zeigen würden.
LemonClaw ist ein Open-Source AI Agent Framework mit integriertem Multi-Model-Routing, persistentem Gedächtnis und über 10 Chat-Plattform-Integrationen. Das gesamte hier beschriebene Dual-Layer Fallback-System ist im Open-Source-Release enthalten. Lassen Sie es auf Ihrem eigenen Server laufen: github.com/hedging8563/lemonclaw
Benötigen Sie Zugriff auf über 300 AI-Models über einen einzigen API-Key? lemondata.cc bietet vereinheitlichten Zugang zu OpenAI, Anthropic, Google, DeepSeek, Groq und mehr.
