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 im Langzeitgedächtnis zu konsolidieren. Der LLM-Aufruf schlägt fehl. Rate Limit. Timeout. Oder das Modell gibt Text zurück, anstatt das erforderliche Tool aufzurufen.
Das Gedächtnis ist weg. Dreißig Minuten Kontext, einfach verflogen.
Das passiert öfter, als man denkt. Wir haben dies über unsere LemonClaw-Instanzen hinweg verfolgt: Die Gedächtniskonsolidierung hatte eine Fehlerrate von ca. 15 % bei jedem einzelnen Modell. Für eine Funktion, die eigentlich als unsichtbare Infrastruktur fungieren sollte, ist das inakzeptabel.
Wenn Sie die umgebende Produktoberfläche aufbauen und nicht nur das Gedächtnis-Subsystem, kombinieren Sie diese Seite mit dem One-Key-Chatbot-Guide und dem Self-Hosted LemonClaw-Guide. Die Beständigkeit des Gedächtnisses ist nur dann wichtig, wenn der Agent tatsächlich in einer nutzbaren Anwendung lebt.
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 das Gedächtnis verloren.
Das Vorgänger-Framework von LemonClaw verwendet dasselbe Modell für die Konsolidierung 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, Modellfehler), protokolliert das Framework eine Warnung und macht weiter. Der Kontext des Benutzers ist weg.
nanobot, ein weiteres beliebtes Framework, hat dieselbe Architektur. Ein Modell, 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 Hauptmodell. Keines verfügt über eine Fallback-Logik für Gedächtnisoperationen. Keines unterscheidet zwischen „der API-Aufruf ist fehlgeschlagen“ und „der API-Aufruf war erfolgreich, aber das Modell hat nicht getan, was wir verlangt haben“.
Dies sind keine Randfälle. Bei einer Fehlerrate von 15 % bei jedem einzelnen Modell verliert ein Framework, das 100 Konsolidierungen pro Tag durchführt, bei 15 davon das Gedächtnis. Über eine Woche hinweg 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. 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])
Dies fängt 429er-Fehler und Netzwerk-Glitches ab. Aber zwei Fehlermodi schlüpfen durch:
Fehlermodus 1: Das Modell beherrscht kein Tool Calling. Einige Modelle, insbesondere kleinere, die auf schnellen Inference-Engines laufen, scheitern gelegentlich daran, gültige Funktionsaufrufe bei komplexen Prompts zu generieren. Die API gibt einen 200er-Status mit einem ServiceUnavailableError zurück, der in einem MidStreamFallbackError verpackt ist. Ihre Retry-Logik sieht eine Exception, versucht es mit demselben Modell erneut und erhält denselben Fehler.
Fehlermodus 2: Das Modell ist „erfolgreich“, ruft aber das Tool nicht auf. Das LLM liefert eine perfekt gültige 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 war. Keine noch so große Anzahl von Retries auf HTTP-Ebene wird ein Modell 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 Modell save_memory aufgerufen?"
│ Nein → nächstes Modell in der Kette versuchen
│
▼
_chat_with_retry() ─── Transport-Layer Fallback
│ HTTP-Fehler → exponential backoff
│ Alle Versuche 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) (letzter Ausweg)
Layer 1 kümmert sich um Transportfehler. Layer 2 kümmert sich um Fehler in der Business-Logik. Die Fallback-Kette wird von beiden Layern gemeinsam genutzt und einmal in einem zentralen Katalog definiert.
Dies ist ein grundlegend anderer Ansatz als das bloße Wiederholen desselben Modells. Wenn ein Modell ein Tool nicht aufrufen kann, hilft ein erneuter Versuch mit demselben Prompt selten. Der Wechsel zu einem anderen Modell mit anderen Gewichten und anderem Tool-Calling-Verhalten hingegen schon.
Der Modell-Katalog: Eine Single Source of Truth
Jedes Modell in unserem Katalog hat ein optionales fallback-Feld, das auf das nächste zu testende Modell verweist:
@dataclass(frozen=True)
class ModelEntry:
id: str
label: str
tier: str
description: str
fallback: str | None = None
hidden: bool = False # Vor der benutzerseitigen /model-Liste verborgen
MODEL_CATALOG = [
# Für Benutzer sichtbare Modelle (16 Modelle, zwischen denen Benutzer wählen können)
ModelEntry("claude-sonnet-4-6", "Claude Sonnet 4.6", "standard",
"Empfohlen", fallback="claude-sonnet-4-5"),
ModelEntry("gpt-4.1-mini", "GPT-4.1 Mini", "economy",
"Stabiles Tool Calling", fallback="claude-haiku-4-5"),
# Versteckte Konsolidierungsmodelle (nur für interne Zwecke)
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 Modelle aus dem benutzerseitigen /model-Befehl fern, während sie dennoch an Fallback-Ketten teilnehmen. Benutzer sehen 16 Modelle, zwischen denen sie wechseln können. Das System verwendet 19. Die drei versteckten Modelle existieren ausschließlich für Hintergrundaufgaben wie die Gedächtniskonsolidierung, bei denen Geschwindigkeit und Kosten wichtiger sind als die Konversationsqualität.
Dieser Katalog ist die einzige verlässliche Quelle für das gesamte Modell-Routing. Ein neues Modell zur Fallback-Kette hinzuzufügen bedeutet, eine einzige Zeile hinzuzufügen. Keine Konfigurationsdateien zum Synchronisieren, keine Umgebungsvariablen zum Aktualisieren, keine Deployment-Skripte zum Ändern.
Transport-Layer: Geketteter Fallback mit Zyklenerkennung
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 beim primären Modell
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 ungültig.", finish_reason="error")
# Phase 2: Die 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)
# Korrektes Gateway für dieses Modell 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 Zielmodells 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="Dienst nicht verfügbar.", finish_reason="error")
Das visited-Set ist entscheidend. Ohne es würde eine Kette wie A→B→A ewig loopen. Damit probiert die Engine jedes Modell genau einmal aus.
Auch die Gateway-Auflösung ist wichtig. Verschiedene Modelle benötigen unterschiedliche API-Formate. Claude-Modelle werden über ein Gateway im Anthropic-Format geroutet (ohne /v1-Suffix). GPT-Modelle nutzen ein OpenAI-kompatibles Gateway (mit /v1). Groq-Modelle verwenden wiederum einen anderen Endpunkt. Die Fallback-Engine löst das korrekte Gateway für jedes Modell in der Kette auf und verhindert so Protokollfehler, wie das Senden von Anthropic-Anfragen an einen OpenAI-Endpunkt.
Dies ist ein Detail, das die meisten Frameworks völlig ignorieren. Sie gehen davon aus, dass alle Modelle dasselbe Protokoll sprechen. In der Produktion, mit 19 Modellen über 4 verschiedene API-Formate hinweg, bricht diese Annahme sofort zusammen.
Business-Layer: Verifizierung des Tool-Aufrufs
Die Konsolidierungsfunktion fügt darüber hinaus eine eigene Fallback-Schleife 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: Gedächtnis extrahieren und speichern
args = response.tool_calls[0].arguments
self.write_long_term(args["memory_update"])
self.append_history(args["history_entry"])
return True
# Modell 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 mehr
return False
Dies fängt den Fall ab, in dem _chat_with_retry eine erfolgreiche Antwort zurückgibt (HTTP 200, gültiger Inhalt), aber das Modell das Tool nicht verwendet hat. Die Konsolidierungsfunktion prüft auf has_tool_calls, und falls diese fehlen, wechselt sie zum nächsten Modell in der Kette.
Der Timeout-Wrapper (asyncio.wait_for) löst ebenfalls einen Fallback aus. Wenn ein Modell länger als 30 Sekunden benötigt (häufig bei Cloudflare 524-Fehlern bei langsamen Upstreams), fängt die Funktion den TimeoutError ab und versucht es mit dem nächsten Modell, anstatt die Session des Benutzers auf unbestimmte Zeit zu blockieren.
Warum Groq für die Konsolidierung
Die Gedächtniskonsolidierung ist eine Hintergrundaufgabe. Der Benutzer sieht die Ausgabe nicht. Er braucht nur, dass es funktioniert. Das macht sie zu einem perfekten Kandidaten für schnelle, günstige Modelle.
Die meisten Frameworks verwenden dasselbe teure Modell für alles. Wenn Sie Claude Sonnet für die Konversation verwenden, nutzen Sie Claude Sonnet auch für die Gedächtniskonsolidierung. Das sind 3 $/M Input-Token und mehr als 8 Sekunden pro Konsolidierung für eine Aufgabe, deren Ergebnis kein Mensch jemals liest.
Wir haben die Konsolidierung vollständig vom Konversationsmodell entkoppelt. Die Konversation nutzt das vom Benutzer gewählte Modell. Die Konsolidierung nutzt eine dedizierte Kette von auf Groq gehosteten Modellen:
| Modell | Geschwindigkeit | 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 (vorher) | ~150 TPS | 0,40 $/M | 1,60 $/M |
Das primäre Modell (llama-3.3-70b) konsolidiert eine Session mit 60 Nachrichten in ca. 5 Sekunden. Der vorherige Standard (gpt-4.1-mini) dauerte mehr als 8 Sekunden. Die Kosten pro Konsolidierung sanken von ca. 0,003 $ auf ca. 0,001 $.
Der Kompromiss: Groq-Modelle haben ein weniger zuverlässiges Tool Calling bei komplexen Prompts. Genau deshalb existiert der Dual-Layer Fallback. Wenn llama-3.3-70b das Tool nicht aufrufen kann, übernimmt qwen3-32b. Wenn auch das fehlschlägt, versucht es llama-4-scout. Wenn alle drei Groq-Modelle scheitern, übernimmt gpt-4.1-mini mit nahezu 100 % Zuverlässigkeit beim Tool Calling.
In der Produktion sehen wir, dass das primäre Modell in ca. 85 % der Fälle erfolgreich ist. Die Kette erreicht gpt-4.1-mini in weniger als 2 % der Konsolidierungen. Die Gesamtausfallrate: praktisch null.
Produktionsergebnisse
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 gab Text zurück, ohne das Tool aufzurufen. Der Single-Layer Fallback konnte dies nicht handhaben. Dies ist genau das Szenario, bei 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 Modell, dasselbe Nachrichtenvolumen. Diesmal funktionierte es beim ersten Versuch. Die Unvorhersehbarkeit von Tool-Calling-Fehlern ist genau der Grund, warum man eine Fallback-Kette anstelle eines einzelnen Backup-Modells benötigt.
Wenn das primäre Modell tatsächlich versagt, fängt die Kette es ab:
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 Modelle versucht, Gedächtnis gespeichert. Der Benutzer sieht „Neue Session gestartet.“ und hat keine Ahnung, dass all dies passiert ist.
Die Architektur-Lücke
Das Gedächtnissystem von LemonClaw im Vergleich zu den Alternativen, Feature für Feature:
| Fähigkeit | Typisches AI-Agent-Framework | LemonClaw |
|---|---|---|
| Konsolidierungsmodell | Gleich wie Konversation (teuer, langsam) | Unabhängige Modellkette, Groq-beschleunigt |
| Fehlerbehandlung | Warnung loggen, Gedächtnis verlieren | Dual-Layer Fallback, 5 Modelle tief |
| Transport-Fallback | Dasselbe Modell 3x wiederholen | Geketteter Fallback über verschiedene Modelle |
| Business-Logik-Fallback | Keiner | Tool-Call-Verifizierung + Modellwechsel |
| 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 |
| Verlaufssuche | Keine | HISTORY.md Rolling Window, per Grep durchsuchbar |
| Interne Modelle | Nicht unterstützt | hidden=True für rein systeminterne Modelle |
| Zyklenerkennung | Nicht benötigt (keine Ketten) | visited-Set verhindert A→B→A-Schleifen |
| Gateway-Auflösung | Ein API-Format vorausgesetzt | Pro-Modell-Gateway mit Protokollerkennung |
Jede Zeile in dieser Tabelle repräsentiert einen Produktionsfehler, den wir entweder selbst erlebt oder in den Issue-Trackern anderer Frameworks beobachtet haben. Der Dual-Layer Fallback, der versteckte Modellkatalog, die Pro-Modell-Gateway-Auflösung, der durch Timeout ausgelöste Fallback: Nichts davon existiert in nanobot oder einem anderen Open-Source-Agent-Framework, das wir untersucht haben.
Was wir gelernt haben
„Anfrage erfolgreich“ bedeutet nicht „Aufgabe erfolgreich“. Generische Retry-Engines arbeiten auf HTTP-Ebene. Sie können nicht wissen, dass eine 200er-Antwort mit gültigem JSON eigentlich ein Fehler ist, weil das Modell das von Ihnen angeforderte Tool nicht verwendet hat. Geschäftskritische Operationen benötigen ihre eigenen Erfolgskriterien und ihre eigene Fallback-Logik.
Kleine Modelle scheitern anders als große Modelle. Große Modelle (GPT-4.1, Claude Sonnet) rufen fast immer Tools auf, wenn sie dazu aufgefordert werden. Kleine Modelle auf schnellen Inference-Engines generieren manchmal gültig aussehende Antworten, die das Tool-Schema völlig ignorieren. Dies ist kein Bug, den man mit Prompt Engineering beheben kann. Es ist eine Fähigkeitslücke, die eine architektonische Entschärfung erfordert.
Testen Sie mit Produktionsdaten, nicht mit synthetischen Daten. Unser initialer Test mit 6 synthetischen Nachrichten verlief bei jedem Modell erfolgreich. Die echte Session mit 60 Nachrichten inklusive Tool-Call-Historie, Zeitstempeln und gemischten Sprachen schlug bei zwei von drei Groq-Modellen fehl. Die Komplexität realer Daten deckt Fehlermodi auf, die saubere Testdaten niemals zeigen würden.
Dies ist auch der Grund, warum der AI API Rate Limiting Guide hier wichtig ist. Das Gedächtnissystem braucht nicht nur ein „besseres Modell“. Es benötigt eine Transport-Policy, eine Erfolgsprüfung der Business-Logik und eine Fallback-Leiter, die bei gewöhnlichen Provider-Ausfällen nicht zusammenbricht.
LemonClaw ist ein Open-Source-AI-Agent-Framework mit integriertem Multi-Modell-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 über 300 AI-Modelle über einen einzigen API-Key? lemondata.cc bietet vereinheitlichten Zugriff auf OpenAI, Anthropic, Google, DeepSeek, Groq und mehr.
