Streaming + Chunking

OpenClaw hat zwei separate Streaming-Ebenen:

  • Block-Streaming (Channels): Sendet fertige Blöcke, sobald der Assistent sie schreibt. Das sind normale Channel-Nachrichten (keine Token-Deltas).
  • Token-artiges Streaming (nur Telegram): Aktualisiert eine Draft-Blase mit Teiltext während der Generierung; die finale Nachricht wird am Ende gesendet.

Es gibt kein echtes Token-Streaming zu externen Channel-Nachrichten. Telegram Draft-Streaming ist die einzige Oberfläche mit Teilausgabe.

Block-Streaming (Channel-Nachrichten)

Block-Streaming sendet die Ausgabe des Assistenten in groben Blöcken, sobald sie verfügbar ist.

Model output
  └─ text_delta/events
       ├─ (blockStreamingBreak=text_end)
       │    └─ chunker emits blocks as buffer grows
       └─ (blockStreamingBreak=message_end)
            └─ chunker flushes at message_end
                   └─ channel send (block replies)

Legende:

  • text_delta/events: Model-Stream-Events (können bei nicht-streamenden Modellen spärlich sein).
  • chunker: EmbeddedBlockChunker wendet min/max-Grenzen + Break-Präferenz an.
  • channel send: tatsächlich ausgehende Nachrichten (Block-Antworten).

Steuerung:

  • agents.defaults.blockStreamingDefault: "on"/"off" (Standard: off).
  • Channel-Overrides: *.blockStreaming (und Account-spezifische Varianten) um "on"/"off" pro Channel zu erzwingen.
  • agents.defaults.blockStreamingBreak: "text_end" oder "message_end".
  • agents.defaults.blockStreamingChunk: { minChars, maxChars, breakPreference? }.
  • agents.defaults.blockStreamingCoalesce: { minChars?, maxChars?, idleMs? } (gestreamte Blöcke vor dem Senden zusammenführen).
  • Channel-Hardcap: *.textChunkLimit (z.B. channels.whatsapp.textChunkLimit).
  • Channel-Chunk-Modus: *.chunkMode (length Standard, newline trennt an Leerzeilen (Absatzgrenzen) vor dem Längen-Chunking).
  • Discord-Softcap: channels.discord.maxLinesPerMessage (Standard: 17) teilt hohe Antworten auf, um UI-Clipping zu vermeiden.

Grenz-Semantik:

  • text_end: Blöcke streamen, sobald der Chunker sie ausgibt; bei jedem text_end flushen.
  • message_end: Warten bis die Assistenten-Nachricht fertig ist, dann gepufferte Ausgabe flushen.

message_end nutzt trotzdem den Chunker, wenn der gepufferte Text maxChars überschreitet, sodass am Ende mehrere Chunks ausgegeben werden können.

Chunking-Algorithmus (Low/High Bounds)

Block-Chunking wird von EmbeddedBlockChunker implementiert:

  • Low Bound: Nicht ausgeben, bis Buffer >= minChars (außer erzwungen).
  • High Bound: Bevorzugt Splits vor maxChars; wenn erzwungen, Split bei maxChars.
  • Break-Präferenz: paragraphnewlinesentencewhitespace → Hard Break.
  • Code-Fences: Niemals innerhalb von Fences splitten; wenn bei maxChars erzwungen, Fence schließen + wieder öffnen, um Markdown valide zu halten.

maxChars wird auf das Channel-textChunkLimit begrenzt, sodass du die Channel-Caps nicht überschreiten kannst.

Coalescing (gestreamte Blöcke zusammenführen)

Wenn Block-Streaming aktiviert ist, kann OpenClaw aufeinanderfolgende Block-Chunks zusammenführen, bevor sie gesendet werden. Das reduziert “Einzeiler-Spam” und liefert trotzdem progressive Ausgabe.

  • Coalescing wartet auf Idle-Gaps (idleMs) vor dem Flushen.
  • Buffer sind durch maxChars begrenzt und flushen, wenn sie es überschreiten.
  • minChars verhindert, dass winzige Fragmente gesendet werden, bis genug Text angesammelt ist (finaler Flush sendet immer den restlichen Text).
  • Der Joiner wird von blockStreamingChunk.breakPreference abgeleitet (paragraph\n\n, newline\n, sentence → Leerzeichen).
  • Channel-Overrides sind über *.blockStreamingCoalesce verfügbar (inklusive Account-spezifischer Configs).
  • Standard-Coalesce minChars wird auf 1500 für Signal/Slack/Discord erhöht, sofern nicht überschrieben.

Menschenähnliches Pacing zwischen Blöcken

Wenn Block-Streaming aktiviert ist, kannst du eine zufällige Pause zwischen Block-Antworten hinzufügen (nach dem ersten Block). Das lässt Multi-Bubble-Antworten natürlicher wirken.

  • Config: agents.defaults.humanDelay (Override pro Agent via agents.list[].humanDelay).
  • Modi: off (Standard), natural (800–2500ms), custom (minMs/maxMs).
  • Gilt nur für Block-Antworten, nicht für finale Antworten oder Tool-Zusammenfassungen.

”Chunks streamen oder alles”

Das entspricht:

  • Chunks streamen: blockStreamingDefault: "on" + blockStreamingBreak: "text_end" (ausgeben während du schreibst). Nicht-Telegram-Channels brauchen auch *.blockStreaming: true.
  • Alles am Ende streamen: blockStreamingBreak: "message_end" (einmal flushen, möglicherweise mehrere Chunks wenn sehr lang).
  • Kein Block-Streaming: blockStreamingDefault: "off" (nur finale Antwort).

Channel-Hinweis: Für Nicht-Telegram-Channels ist Block-Streaming aus, außer *.blockStreaming ist explizit auf true gesetzt. Telegram kann Drafts streamen (channels.telegram.streamMode) ohne Block-Antworten.

Config-Erinnerung: Die blockStreaming*-Defaults liegen unter agents.defaults, nicht in der Root-Config.

Telegram Draft-Streaming (Token-artig)

Telegram ist der einzige Channel mit Draft-Streaming:

  • Nutzt Bot API sendMessageDraft in privaten Chats mit Topics.
  • channels.telegram.streamMode: "partial" | "block" | "off".
    • partial: Draft-Updates mit dem neuesten Stream-Text.
    • block: Draft-Updates in Chunk-Blöcken (gleiche Chunker-Regeln).
    • off: Kein Draft-Streaming.
  • Draft-Chunk-Config (nur für streamMode: "block"): channels.telegram.draftChunk (Defaults: minChars: 200, maxChars: 800).
  • Draft-Streaming ist getrennt von Block-Streaming; Block-Antworten sind standardmäßig aus und werden nur durch *.blockStreaming: true auf Nicht-Telegram-Channels aktiviert.
  • Die finale Antwort ist weiterhin eine normale Nachricht.
  • /reasoning stream schreibt Reasoning in die Draft-Blase (nur Telegram).

Wenn Draft-Streaming aktiv ist, deaktiviert OpenClaw Block-Streaming für diese Antwort, um Doppel-Streaming zu vermeiden.

Telegram (private + topics)
  └─ sendMessageDraft (draft bubble)
       ├─ streamMode=partial → update latest text
       └─ streamMode=block   → chunker updates draft
  └─ final reply → normal message

Legende:

  • sendMessageDraft: Telegram Draft-Blase (keine echte Nachricht).
  • final reply: normaler Telegram-Nachrichtenversand.