Das Modell vergisst nicht mehr, was es gedacht hat: Reasoning wird über Turns, Reloads und Compaction hinweg erneut übertragen


Das kennen Sie: Eine Debugging-Session läuft eine halbe Stunde, Sie holen sie mit --resume zurück, und das Modell verhält sich, als hätte es Sie gerade erst kennengelernt. Die Schlussfolgerungen, zu denen es bereits gekommen war, die zwei Richtungen, die es gerade ausgeschlossen hatte — weg. Sie erklären den Kontext noch einmal von vorn. Schlimmer noch: Sie stellen dieselbe Frage zum selben Fehler ein drittes Mal und bekommen jedes Mal eine andere Diagnose.

Die meisten schieben das auf ein schlechtes Gedächtnis des Modells. Eine sehr konkrete Ursache ist, dass Hermes nicht alles zurücktransportierte, was das Modell gedacht hatte. Reasoning-Inhalte müssen bei vielen Providern im nächsten Turn zurückgespiegelt werden, und Hermes tat das nur für eine Handvoll Routen. Überall sonst wurde es fallen gelassen, sobald es das Modell verließ, und Compaction und Session-Branching löschten Reasoning aus früheren Turns sogar aktiv.

In diesem Zeitfenster gemergte Änderungen machen das Replay von einer Ausnahme zum Standard.

Warum Reasoning mit der Konversation mitreisen muss

Zuerst die einfache Fassung, damit der Rest nicht nur Fachjargon ist. Ein Reasoning-Modell erzeugt einen Thinking-Block, bevor es antwortet. Das kann lesbarer Text sein oder ein verschlüsselter bzw. signierter Blob — Provider benennen das unterschiedlich: reasoning_content, reasoning_details, ein signierter Thinking-Block, Geminis thoughtSignature und so weiter.

Drei Eigenschaften sind wichtig:

  • Manche Provider behandeln ihn als Teil der Konversationshistorie: Lassen Sie das Thinking des vorherigen Turns aus der nächsten Anfrage weg, sinkt die Qualität spürbar, oder die Anfrage scheitert mit einem 400, sobald Tool-Aufrufe im Spiel sind.
  • Manche Provider verschlüsseln oder signieren ihn, sodass er nur funktioniert, wenn er wortgetreu zurückgespiegelt wird — ihn selbst zu rekonstruieren bringt nichts Brauchbares.
  • Manche Provider fahren ein strenges Anfrage-Schema, in dem ein zusätzliches Feld ein harter Fehler ist; bei diesen Routen dürfen Sie also kein Byte mehr senden.

Darum war Replay nie einfach nur „alles mitschicken“. Es ist Dispatch pro Route: jede Route weiß, was sie liest und was sie niemals bekommen darf.

Wie es sich früher verhielt

Vorher ließ sich das Verhalten am besten so beschreiben: um ein paar Routen hatte man sich gekümmert.

  • reasoning_content ging nur an DeepSeek, Kimi, MiMo und Ollama Cloud zurück.
  • reasoning_details ging nur an openrouter.ai zurück.
  • Signiertes Claude-Thinking wurde bei Bedrock, Vertex und Foundry verworfen.
  • Der thoughtSignature von Gemini-Text-Turns wurde überhaupt nie erfasst.
  • Compaction entfernte Reasoning früherer Turns aus der kanonischen Historie.

Das Ergebnis sind die beiden Erfahrungen von oben: ein Modell, das nach --resume erneut rät, und eine Session, deren Qualität direkt nach der Compaction abstürzt.

Wie es heute funktioniert: eine Routen-Tabelle plus ein privater Carrier-Record

Eine Routen-Tabelle. Jede Route ist jetzt auf die Carrier-Felder abgebildet, die sie lesen kann (_REASONING_CARRIER_ROUTES in message_sanitization):

Route Erhaltene Felder
OpenRouter, Kilo, Vercel AI Gateway, Nous Portal alle drei: reasoning_content, reasoning, reasoning_details (signierte und verschlüsselte Blöcke wortgetreu)
Novita, MiniMax, Tencent reasoning_content + reasoning_details
Lokale Server reasoning_content + reasoning
Alles andere reasoning_content
Strenge Schemata (Mistral, Groq, Cerebras, SambaNova, Hunyuan) keine
Must-Echo-Stufe (DeepSeek, Kimi, MiMo und Verwandte) nur ein " "-Platzhalter, um den 400 bei Tool-Turns ohne gespeichertes Reasoning zu verhindern — es ist selbst kein Reasoning

Die Keep-Thinking-Flags der Anbieter kommen ebenfalls mit: GLMs clear_thinking: false, DashScopes preserve_thinking, Kimis keep: "all".

Ein privater Carrier-Record. Felder wie Geminis thoughtSignature und Copilots reasoning_text / reasoning_opaque / thought_signature gehören nicht zur Standardform — es gab keinen Ort dafür. Sie werden jetzt als privater hermes.native_assistant-Record gespeichert (implementiert in agent/reasoning_carriers.py, wobei StreamCarriers sie aus gestreamten Deltas sammelt), und nur ihre eigene Route erhält diesen Record jemals. Andere Routen sehen das Feld nie und bekommen dafür also auch nie einen 400. Weil der Record in der Session lebt, übersteht er Gateway-Turns und --resume.

Bedrock Converse bekommt eine eigene Behandlung. Es fordert Thinking für Claude an, führt eine Replay-Richtlinie pro Modell (signed / strip / unsigned) und speichert Reasoning als reasoning_details in Anthropic-Form, sodass ein aus state.db neu geladener Turn ebenfalls erneut übertragen wird. Das ist genau die Zeile im Release-Text von v0.21.7: signiertes Thinking wird nach hermes chat --resume und im nächsten Gateway-Turn erneut übertragen.

Wenn eine Route ablehnt: aufzeichnen, einmal wiederholen

Selbst eine sorgfältige Routen-Tabelle veraltet — was ein Provider heute akzeptiert, kann er morgen ablehnen. Deshalb gibt es einen Fallback:

Wenn eine Anfrage ein 4xx zurückgibt, dessen Meldung ein erneut übertragenes Feld nennt (nur die Formulierung für unbekannte Felder zählt), zeichnet Hermes dieses Urteil pro (provider, host, model) auf, persistiert es in der Modellkonfiguration der Session, sodass --resume die Erinnerung behält, und wiederholt die Anfrage dann einmal ohne dieses Feld.

Drei Einschränkungen verhindern, dass daraus eine Belastung wird:

  • Die Must-Echo-Stufe kann reasoning_content niemals aufzeichnen, weil dieses Feld vorgeschrieben ist.
  • Die Historie wird nie verändert — das Feld wird nur aus der Anfrage weggelassen.
  • Es wird genau einmal wiederholt, es kann also nicht in einer Schleife über einen Fehler laufen.

Compaction und Branching fressen kein Reasoning mehr

Die andere Hälfte ist die Beibehaltung:

  • Compaction (compress() und der Salvage-Pass) entfernt keinen Reasoning-Text und keine verschlüsselten Elemente mehr aus behaltenen Turns. Nur Checkpoints, die von einem neueren überdeckt werden, werden verworfen.
  • CLI-/branch kopiert reasoning_content, ein neuer Branch trägt also seine eigene Historie mit.
  • Die Preflight-Schätzung und der Compaction-Tail-Walk bepreisen undurchsichtige Carrier identisch, sodass Compaction nicht in einer Schleife versucht, sie zu berücksichtigen.

Was das für Sie ändert

  • Eine fortgesetzte Session verhält sich wie die Session, die Sie verlassen haben. Lange Debugging-Sessions, die mit --resume fortgesetzt oder über Gateway-Turns weitergeführt werden, behalten die bereits erreichten Schlussfolgerungen.
  • Compaction hält die Qualität gleichmäßiger. Nach der Kompression des Kontexts sieht das Modell weiterhin sein eigenes früheres Denken statt nur die Schlussfolgerungen.
  • Claude-Nutzer auf Bedrock, Vertex und Foundry. Signiertes Thinking wird nicht mehr verworfen, und fortgesetzte Sessions werden nicht mehr pauschal abgelehnt.
  • Gemini- und Copilot-Nutzer. Signaturen und undurchsichtige Felder, die vorher nie erfasst wurden, reisen jetzt mit der Konversation mit.

Wo Sie nachsehen, wenn es sich falsch verhält

# trägt die Session einen Carrier-Record
hermes sessions show <session-id> | head -40

# beobachten, welche Reasoning-Felder eine Anfrage trägt (Debug-Logging)
hermes logs --follow | grep -i reasoning

# wo der Session-Zustand liegt, um zu bestätigen, dass Reasoning pro Turn behalten wird
ls -l "$HERMES_HOME/state.db"

Wenn Sie sehen, dass eine Route eine Ablehnung wegen eines unbekannten Feldes protokolliert und dann wiederholt, arbeitet der Fallback korrekt — meist erholt er sich im nächsten Turn. Wenn eine Route weiterhin auf dieselbe Weise scheitert, ist das ein Issue wert: die Routen-Tabelle muss aktualisiert werden.

Für die anderen Reasoning-Schrauben siehe unseren Leitfaden zum Reasoning-Aufwand. Die operative Seite des Fortsetzens steht im Leitfaden zum Session-Resume, und der Hintergrund zur Compaction im Leitfaden zum Lean-Tail bei der Kompression.