Потоковая передача + разбиение

OpenClaw имеет два отдельных "потоковых" слоя:

  • Блочная потоковая передача (каналы): отправлять завершённые блоки по мере написания ассистентом. Это обычные сообщения каналов (не дельты токенов).
  • Токеноподобная потоковая передача (только Telegram): обновлять черновик пузыря с частичным текстом во время генерации; финальное сообщение отправляется в конце.

Сегодня нет реальной токеновой потоковой передачи в сообщения внешних каналов. Потоковая передача черновика Telegram — единственная поверхность частичного потока.

Блочная потоковая передача (сообщения каналов)

Блочная потоковая передача отправляет вывод ассистента крупными частями по мере их доступности.

Вывод модели
  └─ text_delta/events
       ├─ (blockStreamingBreak=text_end)
       │    └─ chunker отправляет блоки по мере роста буфера
       └─ (blockStreamingBreak=message_end)
            └─ chunker сбрасывает при message_end
                   └─ отправка канала (блочные ответы)

Легенда:

  • text_delta/events: события потока модели (могут быть разреженными для непотоковых моделей).
  • chunker: EmbeddedBlockChunker применяющий мин/макс границы + предпочтение разрыва.
  • отправка канала: фактические исходящие сообщения (блочные ответы).

Управление:

  • agents.defaults.blockStreamingDefault: "on"/"off" (по умолчанию выкл).
  • Переопределения каналов: *.blockStreaming (и варианты для конкретного аккаунта) для принудительного "on"/"off" для каждого канала.
  • agents.defaults.blockStreamingBreak: "text_end" или "message_end".
  • agents.defaults.blockStreamingChunk: { minChars, maxChars, breakPreference? }.
  • agents.defaults.blockStreamingCoalesce: { minChars?, maxChars?, idleMs? } (объединить потоковые блоки перед отправкой).
  • Жёсткое ограничение канала: *.textChunkLimit (например, channels.whatsapp.textChunkLimit).
  • Режим разбиения канала: *.chunkMode (по умолчанию length, newline разделяет по пустым строкам (границы абзацев) перед разбиением по длине).
  • Мягкое ограничение Discord: channels.discord.maxLinesPerMessage (по умолчанию 17) разделяет высокие ответы, чтобы избежать обрезания UI.

Семантика границ:

  • text_end: потоковые блоки как только chunker отправляет; сброс при каждом text_end.
  • message_end: ждать до завершения сообщения ассистента, затем сбросить буферизованный вывод.

message_end всё ещё использует chunker, если буферизованный текст превышает maxChars, поэтому он может отправить несколько частей в конце.

Алгоритм разбиения (нижняя/верхняя границы)

Блочное разбиение реализовано EmbeddedBlockChunker:

  • Нижняя граница: не отправлять до буфера >= minChars (если не принуждено).
  • Верхняя граница: предпочитать разделения до maxChars; если принуждено, разделить при maxChars.
  • Предпочтение разрыва: paragraphnewlinesentencewhitespace → жёсткий разрыв.
  • Кодовые ограждения: никогда не разделять внутри ограждений; когда принуждено при maxChars, закрыть + заново открыть ограждение, чтобы сохранить Markdown валидным.

maxChars ограничен textChunkLimit канала, поэтому вы не можете превысить ограничения для каждого канала.

Объединение (слияние потоковых блоков)

Когда блочная потоковая передача включена, OpenClaw может объединять последовательные блочные части перед их отправкой. Это уменьшает "однострочный спам" при всё ещё предоставлении прогрессивного вывода.

  • Объединение ждёт пробелов простоя (idleMs) перед сбросом.
  • Буферы ограничены maxChars и сбросятся, если превысят его.
  • minChars предотвращает отправку крошечных фрагментов до накопления достаточного текста (финальный сброс всегда отправляет оставшийся текст).
  • Соединитель выводится из blockStreamingChunk.breakPreference (paragraph\n\n, newline\n, sentence → пробел).
  • Переопределения каналов доступны через *.blockStreamingCoalesce (включая конфигурации для конкретного аккаунта).
  • По умолчанию minChars объединения увеличивается до 1500 для Signal/Slack/Discord если не переопределено.

Человекоподобная пауза между блоками

Когда блочная потоковая передача включена, вы можете добавить рандомизированную паузу между блочными ответами (после первого блока). Это делает многопузырные ответы более естественными.

  • Конфигурация: agents.defaults.humanDelay (переопределить для каждого агента через agents.list[].humanDelay).
  • Режимы: off (по умолчанию), natural (800–2500мс), custom (minMs/maxMs).
  • Применяется только к блочным ответам, не финальным ответам или сводкам инструментов.

"Потоковые части или всё"

Это соответствует:

  • Потоковые части: blockStreamingDefault: "on" + blockStreamingBreak: "text_end" (отправлять по мере продвижения). Каналы не-Telegram также требуют *.blockStreaming: true.
  • Потоковое всё в конце: blockStreamingBreak: "message_end" (сброс один раз, возможно несколько частей если очень длинный).
  • Нет блочной потоковой передачи: blockStreamingDefault: "off" (только финальный ответ).

Примечание канала: Для каналов не-Telegram блочная потоковая передача выключена если *.blockStreaming явно не установлен в true. Telegram может потоково передавать черновики (channels.telegram.streamMode) без блочных ответов.

Напоминание о расположении конфигурации: умолчания blockStreaming* находятся под agents.defaults, а не корневой конфигурацией.

Потоковая передача черновика Telegram (токеноподобная)

Telegram — единственный канал с потоковой передачей черновика:

  • Использует Bot API sendMessageDraft в приватных чатах с топиками.
  • channels.telegram.streamMode: "partial" | "block" | "off".
    • partial: обновления черновика с последним потоковым текстом.
    • block: обновления черновика разбитыми блоками (те же правила chunker).
    • off: нет потоковой передачи черновика.
  • Конфигурация частей черновика (только для streamMode: "block"): channels.telegram.draftChunk (по умолчанию: minChars: 200, maxChars: 800).
  • Потоковая передача черновика отдельна от блочной потоковой передачи; блочные ответы выключены по умолчанию и включаются только *.blockStreaming: true на каналах не-Telegram.
  • Финальный ответ всё ещё обычное сообщение.
  • /reasoning stream записывает рассуждение в черновик пузыря (только Telegram).

Когда потоковая передача черновика активна, OpenClaw отключает блочную потоковую передачу для этого ответа, чтобы избежать двойной потоковой передачи.

Telegram (приватный + топики)
  └─ sendMessageDraft (черновик пузыря)
       ├─ streamMode=partial → обновить последний текст
       └─ streamMode=block   → chunker обновляет черновик
  └─ финальный ответ → обычное сообщение

Легенда:

  • sendMessageDraft: черновик пузыря Telegram (не реальное сообщение).
  • финальный ответ: обычная отправка сообщения Telegram.