Streaming + fragmentación
OpenClaw tiene dos capas de streaming separadas:
- Streaming de bloques (canales): emite bloques completados a medida que el asistente escribe. Son mensajes normales del canal (no deltas de tokens).
- Streaming de vista previa (Telegram/Discord/Slack): actualiza un mensaje de vista previa temporal durante la generación.
No hay streaming de delta de tokens real hacia mensajes de canal hoy. El streaming de vista previa es basado en mensajes (envío + ediciones/adiciones).
Streaming de bloques (mensajes de canal)
El streaming de bloques envía la salida del asistente en fragmentos gruesos a medida que están disponibles.
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)
Leyenda:
text_delta/events: eventos del stream del modelo (pueden ser escasos para modelos sin streaming).chunker:EmbeddedBlockChunkeraplicando límites min/max + preferencia de corte.channel send: mensajes salientes reales (respuestas por bloques).
Controles:
agents.defaults.blockStreamingDefault:"on"/"off"(por defecto off).- Sobreescrituras por canal:
*.blockStreaming(y variantes por cuenta) para forzar"on"/"off"por canal. agents.defaults.blockStreamingBreak:"text_end"o"message_end".agents.defaults.blockStreamingChunk:{ minChars, maxChars, breakPreference? }.agents.defaults.blockStreamingCoalesce:{ minChars?, maxChars?, idleMs? }(fusionar bloques del stream antes de enviar).- Tope estricto por canal:
*.textChunkLimit(por ejemplo,channels.whatsapp.textChunkLimit). - Modo de fragmentación por canal:
*.chunkMode(lengthpor defecto,newlinedivide en líneas en blanco (límites de párrafo) antes de la fragmentación por longitud). - Tope suave de Discord:
channels.discord.maxLinesPerMessage(por defecto 17) divide respuestas altas para evitar recorte en la UI.
Semántica de límites:
text_end: enviar bloques del stream tan pronto como el chunker los emite; flush en cadatext_end.message_end: esperar hasta que el mensaje del asistente termine, luego flush de la salida almacenada.
message_end aún usa el chunker si el texto almacenado excede maxChars, por lo que puede emitir múltiples fragmentos al final.
Algoritmo de fragmentación (límites bajo/alto)
La fragmentación por bloques se implementa mediante EmbeddedBlockChunker:
- Límite bajo: no emitir hasta que el buffer >=
minChars(a menos que se fuerce). - Límite alto: preferir cortes antes de
maxChars; si se fuerza, cortar enmaxChars. - Preferencia de corte:
paragraph→newline→sentence→whitespace→ corte duro. - Bloques de código: nunca cortar dentro de fences; cuando se fuerza en
maxChars, cierra + reabre el fence para mantener el Markdown válido.
maxChars se limita al textChunkLimit del canal, así que no puedes exceder los topes por canal.
Coalescencia (fusionar bloques del stream)
Cuando el streaming de bloques está habilitado, OpenClaw puede fusionar fragmentos de bloques consecutivos antes de enviarlos. Esto reduce el “spam de una sola línea” mientras sigue proporcionando salida progresiva.
- La coalescencia espera gaps de inactividad (
idleMs) antes de hacer flush. - Los buffers tienen un tope de
maxCharsy hacen flush si lo exceden. minCharspreviene que fragmentos pequeños se envíen hasta que se acumule suficiente texto (el flush final siempre envía el texto restante).- El separador se deriva de
blockStreamingChunk.breakPreference(paragraph→\n\n,newline→\n,sentence→ espacio). - Las sobreescrituras por canal están disponibles mediante
*.blockStreamingCoalesce(incluyendo configuraciones por cuenta). - El
minCharsde coalescencia por defecto se eleva a 1500 para Signal/Slack/Discord a menos que se sobreescriba.
Pausas con ritmo humano entre bloques
Cuando el streaming de bloques está habilitado, puedes agregar una pausa aleatorizada entre respuestas por bloques (después del primer bloque). Esto hace que las respuestas multi-burbuja se sientan más naturales.
- Config:
agents.defaults.humanDelay(sobreescribir por agente medianteagents.list[].humanDelay). - Modos:
off(por defecto),natural(800–2500ms),custom(minMs/maxMs). - Aplica solo a respuestas por bloques, no a respuestas finales ni resúmenes de herramientas.
”Fragmentos del stream o todo”
Esto se mapea a:
- Fragmentos del stream:
blockStreamingDefault: "on"+blockStreamingBreak: "text_end"(emitir sobre la marcha). Los canales no-Telegram también necesitan*.blockStreaming: true. - Todo al final del stream:
blockStreamingBreak: "message_end"(flush una vez, posiblemente múltiples fragmentos si es muy largo). - Sin streaming de bloques:
blockStreamingDefault: "off"(solo respuesta final).
Nota sobre canales: El streaming de bloques está desactivado a menos que *.blockStreaming esté explícitamente configurado a true. Los canales pueden hacer streaming de una vista previa en vivo (channels.<channel>.streaming) sin respuestas por bloques.
Recordatorio de ubicación de configuración: los valores por defecto de blockStreaming* están bajo agents.defaults, no en la raíz de la configuración.
Modos de streaming de vista previa
Clave canónica: channels.<channel>.streaming
Modos:
off: deshabilitar streaming de vista previa.partial: una sola vista previa que se reemplaza con el último texto.block: vista previa se actualiza en pasos fragmentados/adicionados.progress: vista previa de progreso/estado durante la generación, respuesta final al completarse.
Mapeo por canal
| Canal | off | partial | block | progress |
|---|---|---|---|---|
| Telegram | ✅ | ✅ | ✅ | mapea a partial |
| Discord | ✅ | ✅ | ✅ | mapea a partial |
| Slack | ✅ | ✅ | ✅ | ✅ |
Solo Slack:
channels.slack.nativeStreamingactiva/desactiva las llamadas a la API nativa de streaming de Slack cuandostreaming=partial(por defecto:true).
Migración de clave legacy:
- Telegram:
streamMode+ booleanostreamingmigran automáticamente al enumstreaming. - Discord:
streamMode+ booleanostreamingmigran automáticamente al enumstreaming. - Slack:
streamModemigra automáticamente al enumstreaming; booleanostreamingmigra automáticamente anativeStreaming.
Comportamiento en runtime
Telegram:
- Usa
sendMessage+editMessageTextpara actualizaciones de vista previa en DMs y grupos/temas. - El streaming de vista previa se omite cuando el streaming de bloques de Telegram está explícitamente habilitado (para evitar streaming doble).
/reasoning streampuede escribir razonamiento en la vista previa.
Discord:
- Usa send + edit para mensajes de vista previa.
- El modo
blockusa fragmentación de borrador (draftChunk). - El streaming de vista previa se omite cuando el streaming de bloques de Discord está explícitamente habilitado.
Slack:
partialpuede usar streaming nativo de Slack (chat.startStream/append/stop) cuando está disponible.blockusa vistas previas de borrador estilo append.progressusa texto de vista previa de estado, luego respuesta final.